嵌套循环的替代方法

时间:2020-03-03 05:13:42

标签: python for-loop nested

以该代码为例

#Find all combinations of five positive integers whose reciprocals sum to 1 under a limit
limit = 30
none = True
reciprocals5 = []
for x in range(1,limit):
    for y in range(1,limit):
        for z in range(1,limit):
            for a in range(1,limit):
                for b in range(1,limit):
                    if(x!=y and x!=z and x!=a and x!=b and y!=z and y!=a and y!=a and y!=b and z!=a and z!=b and a!=b):
                        if(1/x + 1/y + 1/z + 1/a + 1/b == 1):
                            reciprocals5.append([x,y,z,a,b])
                            none = False

如果必须找到所有6个正整数组合,则必须添加另一个循环,依此类推。 可以说我想找到所有N个正整数组合。由于我无法创建N个嵌套的for循环,因此有什么替代方法?

4 个答案:

答案 0 :(得分:1)

由于我无法创建N个嵌套的for循环,因此有什么选择?

递归!

有一个函数带有额外的参数,例如要求和的项数和目标数,然后在其实现中以更少的项再次调用自身。

您的停止条件是合计项数为零时。在这种情况下,如果要达到的目标数为零,则意味着您找到了有效的总和。如果非零,则表示您没有。 (类似地,您可以在剩下的第一学期进行最后一次检查,检查是否可以选择一个匹配的最终数字。)

由于您只需要找到合计为一组的 distinct 个数字集,因此可以假设x> y> z> a> b(或相反的顺序),以确保您不会一次又一次地找到同一序列,只是顺序不同。

此外,从极限向下迭代意味着倒数将随着迭代的进行而增长。这也意味着一旦总和超过1(或目标变为负数),您就可以停止寻找,这应该可以帮助您快速修剪不会产生新值的循环。

最后,Python还支持fractions,这意味着您可以精确地进行这些计算,而不必担心浮点数的舍入问题。

将它们放在一起:

from fractions import Fraction

def reciprocal_sums(n=5, limit=30, target=1, partial=()):
    if n == 0:
        if target == 0:
            yield partial
        return
    for i in range(limit, 0, -1):
        new_target = target - Fraction(1, i)
        if new_target < 0:
            return
        yield from reciprocal_sums(
            n - 1, i - 1, new_target, partial + (i,))

测试n = 5(默认):

>>> list(reciprocal_sums())
[(30, 20, 12, 3, 2),
 (30, 20, 6, 4, 2),
 (30, 10, 6, 5, 2),
 (28, 21, 12, 3, 2),
 (28, 21, 6, 4, 2),
 (28, 14, 7, 4, 2),
 (24, 12, 8, 4, 2),
 (20, 12, 6, 5, 2),
 (20, 6, 5, 4, 3),
 (18, 12, 9, 4, 2),
 (15, 12, 10, 4, 2)]

对于n = 4:

>>> list(reciprocal_sums(4))
[(24, 8, 3, 2),
 (20, 5, 4, 2),
 (18, 9, 3, 2),
 (15, 10, 3, 2),
 (12, 6, 4, 2)]

n = 6:

>>> list(reciprocal_sums(6))
[(30, 28, 21, 20, 3, 2),
 (30, 24, 20, 8, 4, 2),
 (30, 24, 10, 8, 5, 2),
 (30, 20, 18, 9, 4, 2),
 (30, 20, 15, 10, 4, 2),
 (30, 18, 10, 9, 5, 2),
 (30, 12, 10, 5, 4, 3),
 (28, 24, 21, 8, 4, 2),
 (28, 21, 20, 6, 5, 2),
 (28, 21, 18, 9, 4, 2),
 (28, 21, 15, 10, 4, 2),
 (28, 20, 14, 7, 5, 2),
 (28, 14, 12, 7, 6, 2),
 (28, 14, 7, 6, 4, 3),
 (24, 20, 12, 8, 5, 2),
 (24, 20, 8, 5, 4, 3),
 (24, 18, 9, 8, 6, 2),
 (24, 15, 10, 8, 6, 2),
 (24, 12, 8, 6, 4, 3),
 (20, 18, 12, 9, 5, 2),
 (20, 18, 9, 5, 4, 3),
 (20, 15, 12, 10, 5, 2),
 (20, 15, 10, 5, 4, 3),
 (18, 15, 10, 9, 6, 2),
 (18, 12, 9, 6, 4, 3),
 (15, 12, 10, 6, 4, 3)]

此解决方案非常快。在Snapdragon 845 ARM CPU上运行:

%timeit list(reciprocal_sums(4)) 
365 ms ± 5.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit list(reciprocal_sums(5))
1.94 s ± 8.93 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit list(reciprocal_sums(6))
8.26 s ± 56.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

排序(降低每个级别的限制)以及修剪超出目标的最后一个级别将使此解决方案比评估 all 可能的排列或组合的解决方案快得多。

答案 1 :(得分:0)

itertools.product(list(range(N)),repeat=N)

也许是您想要的

答案 2 :(得分:0)

您可以使用itertools.permutations。

import itertools

reciprocals = []
for x in itertools.permutations(range(1, limit), N):
    if sum(1/i for i in x) == 1:
        reciprocals.append(x)

答案 3 :(得分:0)

尝试一下:

import itertools
myList = []
for combination in itertools.product(range(10), repeat=6):
    myList.append(''.join(map(str, combination)))

这将创建str格式的6位整数列表。您可以轻松键入cast。