python:生成整数分区

python:生成整数分区,python,combinatorics,performance,data-partitioning,Python,Combinatorics,Performance,Data Partitioning,我需要生成给定整数的所有值。 我发现了Jerome Kelleher的这个算法,据说它是最有效的: def accelAsc(n): a = [0 for i in range(n + 1)] k = 1 a[0] = 0 y = n - 1 while k != 0: x = a[k - 1] + 1 k -= 1 while 2*x <= y: a[k] = x

我需要生成给定整数的所有值。
我发现了Jerome Kelleher的这个算法,据说它是最有效的:

def accelAsc(n):
    a = [0 for i in range(n + 1)]
    k = 1
    a[0] = 0
    y = n - 1
    while k != 0:
        x = a[k - 1] + 1
        k -= 1
        while 2*x <= y:
            a[k] = x
            y -= x
            k += 1
        l = k + 1
        while x <= y:
            a[k] = x
            a[l] = y
            yield a[:k + 2]
            x += 1
            y -= 1
        a[k] = x + y
        y = x + y - 1
        yield a[:k + 1]
def加速器(n):
a=[0表示范围(n+1)内的i]
k=1
a[0]=0
y=n-1
而k!=0:
x=a[k-1]+1
k-=1

而2*x我想说的是,您的性能问题在其他地方

我没有将其与其他方法进行比较,但在我看来,它确实很有效:

import time

start = time.time()
partitions = list(accelAsc(40))
print('time: {:.5f} sec'.format(time.time() - start))
print('length:', len(partitions))
给出:

time: 0.03636 sec
length: 37338

如果要对相同的输入重复使用此函数,则仍然值得缓存返回值(如果要在不同的运行中使用此函数,则可以将结果存储在文件中)

如果找不到明显更快的算法,那么应该可以通过将代码移动到C扩展中(这可能是最容易使用的),或者通过使用而不是CPython,将速度提高一两个数量级(PyPy有它的缺点——它还不支持Python3,或者一些常用的库,比如numpy和scipy)


原因是,由于python是动态类型化的,解释器可能会花费大部分时间检查变量的类型——解释器知道,其中一个操作可能会将
x
转换为字符串,在这种情况下,像
x+y
这样的表达式突然会有非常不同的含义n通过允许静态地将变量声明为整数来解决这个问题,而PyPy有一个最大限度地减少冗余类型检查的方法。

使用n=75进行测试我得到:

PyPy 1.8:

w:\>c:\pypy-1.8\pypy.exe pstst.py
1.04800009727 secs.
CPython 2.6:

w:\>python pstst.py
5.86199998856 secs.
Cython+mingw+gcc 4.6.2:

w:\pstst> python -c "import pstst;pstst.run()"
4.06399989128
我看不出Psyco有什么不同(?)

运行函数:

def run():
    import time
    start = time.time()
    for p in accelAsc(75):
        pass
    print time.time() - start, 'secs.'
如果我将Cython的accelAsc定义更改为:

def accelAsc(int n):
    cdef int x, y, k
    # no more changes..

我将Cython时间缩短到2.27秒。

要直接生成合成,您可以使用以下算法:

def ruleGen(n, m, sigma):
    """
    Generates all interpart restricted compositions of n with first part
    >= m using restriction function sigma. See Kelleher 2006, 'Encoding
    partitions as ascending compositions' chapters 3 and 4 for details.
    """
    a = [0 for i in range(n + 1)]
    k = 1
    a[0] = m - 1
    a[1] = n - m + 1
    while k != 0:
        x = a[k - 1] + 1
        y = a[k] - 1
        k -= 1
        while sigma(x) <= y:
            a[k] = x
            x = sigma(x)
            y -= x
            k += 1
        a[k] = x + y
        yield a[:k + 1]
生成所有不受限制的合成。第三个参数称为限制函数,用于描述所需的合成/分区类型。该方法非常有效,因为在对生成的所有合成进行平均时,生成每个合成所需的工作量是恒定的。如果要在python中它的速度稍微快一点,然后用1替换函数sigma就很容易了

这里还值得注意的是,对于任何固定摊销时间算法,您对生成对象的实际操作几乎肯定会决定生成它们的成本。例如,如果您将所有分区存储在一个列表中,那么管理这个大列表的内存所花费的时间将远远大于生成它们所花费的时间对分区进行分级


比如说,出于某种原因,你想得到每个分区的乘积。如果你对此采取一种简单的方法,那么所涉及的处理在部件数量上是线性的,而生成的成本是恒定的。很难想象一个组合生成算法的应用,其中处理不占主导地位因此,在实践中,使用更简单、更通用的ruleGen和sigma(x)之间没有可测量的差异=x和专门的accelAsc。

仅仅因为计算40需要几秒钟,并不意味着这是无效的。该算法不会生成合成,而是生成分区。但这是一个幸运的错误:有549755813888个40的合成,这会使任何人的计算机陷入停滞。请编辑您的问题,因为它令人困惑那些真正在寻找整数合成的人。参考页已移动到:不要像这样计时Python内容,使用模块。@Lattyware:我不会像这样计时Python内容,这不是性能计时。我向OP展示了我无法重现他的“几秒冻结”.如何为非负整数生成基数k的所有组合?我发现了这一点,这与我感兴趣的内容大致相同:
ruleGen(n, 1, lambda x: 1)