所有正整数元组

时间:2018-05-24 10:48:58

标签: python combinatorics

如何创建一个生成器,它将返回所有正整数组合的tulples,例如生成三元组的示例。

(1, 1, 1)
(2, 1, 1)
(1, 2, 1)
(1, 1, 2)
(2, 2, 1)
(2, 1, 2)
(2, 2, 2)
(3, 2, 2)
(2, 3, 2)
# and so on...

3 个答案:

答案 0 :(得分:6)

此代码使用与Paul Hankin类似的方法,但它更通用,因为它会生成任何所需宽度的元组,而不仅仅是3。

from itertools import combinations, count

def compositions(num, width):
    m = num - 1
    first, last = (-1,), (m,)
    for t in combinations(range(m), width - 1):
        yield tuple(v - u for u, v in zip(first + t, t + last))

def ordered_compositions(width):
    for n in count(width):
        yield from compositions(n, width)

# test

width = 3
for i, t in enumerate(ordered_compositions(width), 1):
    print(i, t)
    if i > 30:
        break

<强>输出

1 (1, 1, 1)
2 (1, 1, 2)
3 (1, 2, 1)
4 (2, 1, 1)
5 (1, 1, 3)
6 (1, 2, 2)
7 (1, 3, 1)
8 (2, 1, 2)
9 (2, 2, 1)
10 (3, 1, 1)
11 (1, 1, 4)
12 (1, 2, 3)
13 (1, 3, 2)
14 (1, 4, 1)
15 (2, 1, 3)
16 (2, 2, 2)
17 (2, 3, 1)
18 (3, 1, 2)
19 (3, 2, 1)
20 (4, 1, 1)
21 (1, 1, 5)
22 (1, 2, 4)
23 (1, 3, 3)
24 (1, 4, 2)
25 (1, 5, 1)
26 (2, 1, 4)
27 (2, 2, 3)
28 (2, 3, 2)
29 (2, 4, 1)
30 (3, 1, 3)
31 (3, 2, 2)

compositions的算法源自用于计算compositions上维基百科文章中解释的合成数量的技术。这基本上是众所周知的Stars and Bars技术的变体。

答案 1 :(得分:2)

您可以通过根据总数枚举它们来生成所有正整数元组(总数= 3,总数= 4秒,总数= 5,等等)。请注意,total = 3是可能的最小总数,因为每个元素都是正数,因此我们从t=3开始。

在每个总数中,此代码按字典顺序生成它们。

def posint():
    t = 3
    while True:
        for i in range(1, t-1):
            for j in range(1, t-i):
                    yield i, j, t-i-j
        t += 1

for i, j, k in posint():
    print(i, j, k)

如果您想要一个接受描述元组长度的参数n的更通用的版本,您可以使用"Stars and Bars"枚举总和为t的元组。这为您提供了非负元组,但您可以为每个值添加一个以获得唯一的正元组。

import itertools

def posint(n):
    for t in itertools.count(0):
        for c in itertools.combinations(range(t + n - 1), n - 1):
            yield [c[0]+1] + [c[i]-c[i-1] for i in range(1, len(c))] + [t+n-1-c[-1]]

for c in posint(5):
    print(c)

一种易于理解的通用方法根据它们的最大值以及该值出现的第一列来枚举组合。在此方案中,具有最大值t的结果和其中t出现在第i列中的结果在i的左边的列中有小于t的任意值,在i的右边的列中有小于或等于t的任意值。

这种方法相对容易理解,但结果出来的顺序有点奇怪。

import itertools

def posint(n):
    for t in itertools.count(1):
        for i in range(n):
            for c1 in itertools.product(range(1, t), repeat=i):
                for c2 in itertools.product(range(1, t+1), repeat=n-i-1):
                    yield c1 + (t,) + c2

for c in posint(3):
    print(c)

答案 2 :(得分:1)

这是一种蛮力方法,假设没有订购要求。

警告:使用set通过toolz.unique这里删除重复项是内存密集型且价格昂贵的。有更好的方法。

from itertools import count, product, chain
from toolz import unique

def gen_tups(n):
    yield from unique(chain.from_iterable(product(range(1, c), repeat=n) \
                      for c in count(start=2))):

x = gen_tups(3)

next(x)  # (1, 1, 1)
next(x)  # (1, 1, 2)
next(x)  # (1, 2, 1)

注意toolz.unique实现此commonly used recipe,因此您无需访问第三方库。