【问题标题】:Fast combinatoric generator in PythonPython中的快速组合生成器
【发布时间】:2015-03-20 16:37:09
【问题描述】:

作为 python 中的一个大型项目的一部分,我需要一个快速生成器函数,它可以生成小于n 的所有可能的非负整数集合,这样每个集合最多有s 元素和两者之间的差异集合中最大和最小的数字小于w

迄今为止我实现的最快实现是使用itertools

import itertools

def subsample(n, s, w):
    nn = range(w)
    for p in range(s):
        o = list(itertools.combinations(nn, p+1))
        for t in o:
            yield t
        for _ in range(0, n-w):
            pt = o
            o = [tuple([op + 1 for op in list(u)]) for u in pt]
            for t in list((set(o) ^ set(pt)) & set(o)):
                yield t

例如:

In [1]: list(subsample(6,3,3))
Out [1]: [(0,), (1,), (2,), (3,), (4,), (5,), (0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (3, 4), (2, 4), (4, 5), (3, 5), (0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5)]

但我坚信必须有更有效的方法来做到这一点。有什么可以让这更快吗?

【问题讨论】:

  • 最快的方法是什么? 是一种不好的提问方式。更容易得到答案的是有什么可以让这更快吗?,我会回答除非你想搞砸你的程序的意图.
  • 你的 python 版本是多少?
  • 您使用的是哪个 Python 版本?这可能与范围有关;如果它是 for p in range(s): 和 for _ in range(0, n-w): 中的 xrange@
  • @A.J.如果问题不好,请道歉。我已根据您的建议对其进行了编辑。 @Kasra 和 @Eithos,我使用的是 python 2.7。 range vs xrange 问题是一个很好的观察,谢谢。
  • @STU 如果您将问题发布到here,您可能会得到更好的答案。只是一个建议。

标签: python performance generator combinatorics


【解决方案1】:

这里有一些基于 Knuth 算法的生成器。

  • subsets4 生成具有 k 或更少元素的 1..n 的所有子集
  • subsets5subsets4 生成的子集限制为最大差异为 w 的子集
  • subsets 生成 1..n 的所有子集,长度正好为 k
  • subsets2subsets 生成的子集限制为最大差异为 w 的子集

运行megatest() 函数以测试subsets5 生成器的多个nkw 值。

# all subsets of k or less elements of 1..n
def subsets4(n,k):
  a = [ 0 ] * k
  i = 0
  while i >= 0:
    a[i] += 1
    yield a[0:i+1]
    r = a[i]+1
    i += 1
    while i < k and r <= n:
      a[i] = r
      yield a[0:i+1]
      i += 1
      r += 1
    i -= 1
    if a[i] >= n:
      i -= 1

# all subsets of k or less elements of 1..n with max difference <= w
def subsets5(n,k,w):
  a = [ 0 ] * k
  i = 0
  while i >= 0:
    a[i] += 1
    yield a[0:i+1]
    r = a[i]+1
    i += 1
    while i < k and r <= n and r-a[0] <= w:
      a[i] = r
      yield a[0:i+1]
      i += 1
      r += 1
    i -= 1
    if a[i] >= n or a[i]+1-a[0] > w:
      i -= 1

# all subsets of 1..n having exactly k elements
def subsets(n,k):
  a = range(1,k+1)
  while a[0] <= n+1-k:
    yield a
    # find i
    i = k-1
    while i >= 0 and a[i]+k-i >= n+1: i -= 1
    r = a[i]
    a[i] += 1
    j = 2
    i += 1
    while i < k:
      a[i] = r + j
      i += 1
      j += 1

# all subsets of 1..n having exactly k elements and whose max
# difference is w
def subsets2(n,k,w):
  if k > w: return
  a = range(1,k+1)
  while a[0] <= n+1-k:
    yield a
    i = k-1
    while i >= 0 and (a[i]+k-i >= n+1 or a[i]+k-i-a[0] > w) : i -= 1
    r = a[i]
    a[i] += 1
    j = 2
    i += 1
    while i < k:
      a[i] = r + j
      i += 1
      j += 1

def test(n,k,w):
  s1 = [ s for s in subsets4(n,k) if s[-1] - s[0] <= w ]
  s2 = [ s for s in subsets5(n,k,w) ]
  if s1 == s2:
    print "OK", n,k,w
    return 0
  else:
    print "NOT OK", n, k, w
    return 1 

# for s in subsets2(10,3,4): print s
# for s in subsets(10,3): print s
def megatest():
  failed = 0
  for n in xrange(10,20):
    for k in xrange(1,n+1):
      for w in xrange(k,n+1):
        failed += test(n,k,w)
  print "failed:", failed

【讨论】:

  • 嘿,这非常好,比我的实现快 3 倍以上:%timeit [f for f in subsets5(500,6,11)] 提供 474 ms per loop,而 %timeit [f for f in subsample(500,6,12)] 提供 1.71 s per loop。请注意参数w 的约定略有不同。
猜你喜欢
  • 2015-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-26
  • 2016-09-04
  • 2014-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多