Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 动态锁大小的锁组合_Python_Recursion - Fatal编程技术网

Python 动态锁大小的锁组合

Python 动态锁大小的锁组合,python,recursion,Python,Recursion,下面我将给出两个具有不同维度值的示例 锁-1 # numbers are the shown values on the so in this case: 0,1,2 numbers = 5 # fields are those things i can turn to change my combination fields = 4 所以我对我所有的可能性的期望是 0 0 0 5 0 0 1 4 0 0 2 3 0 0 3 2 0 0

下面我将给出两个具有不同维度值的示例

锁-1

# numbers are the shown values on the so in this case: 0,1,2
numbers = 5
# fields are those things i can turn to change my combination
fields = 4
所以我对我所有的可能性的期望是

0   0   0   5
0   0   1   4
0   0   2   3
0   0   3   2
0   0   4   1
0   0   5   0
0   1   0   4
0   1   1   3
0   1   2   2
0   1   3   1
0   1   4   0
0   2   0   3
0   2   1   2
0   2   2   1
0   2   3   0
0   3   0   2
0   3   1   1
0   3   2   0
0   4   0   1
0   4   1   0
0   5   0   0
1   0   0   4
1   0   1   3
1   0   2   2
1   0   3   1
1   0   4   0
1   1   0   3
1   1   1   2
1   1   2   1
1   1   3   0
1   2   0   2
1   2   1   1
1   2   2   0
1   3   0   1
1   3   1   0
1   4   0   0
2   0   0   3
2   0   1   2
2   0   2   1
2   0   3   0
2   1   0   2
2   1   1   1
2   1   2   0
2   2   0   1
2   2   1   0
2   3   0   0
3   0   0   2
3   0   1   1
3   0   2   0
3   1   0   1
3   1   1   0
3   2   0   0
4   0   0   1
4   0   1   0
4   1   0   0
5   0   0   0
我的第二个锁具有以下值:

numbers = 3
values = 3
所以我期望我的能力是这样的

0   0   3
0   1   2
0   2   1
0   3   0
1   0   2
1   1   1
1   2   0
2   0   1
2   1   0
3   0   0
我知道这可以通过
itertools.permutations
等来实现,但我希望通过构建行来生成行,而不是通过重载RAM。我发现最后两排总是以同样的方式排列。 所以我写了一个函数为我构建它:

def posibilities(value):
    all_pos = []

    for y in range(value + 1):
        posibility = []
        posibility.append(y)
        posibility.append(value)

        all_pos.append(posibility)

        value -= 1

    return all_pos
现在我需要某种方式来动态地适应函数周围的其他值,例如Lock-2现在看起来是这样的:

0   posibilities(3)
1   posibilities(2)
2   posibilities(1)
3   posibilities(0)
我知道我应该使用
while
循环等等,但我无法获得动态值的解决方案。

您可以递归地执行此操作,但通常最好避免在Python中使用递归,除非您确实需要它,例如在处理递归数据结构(如树)时。标准Python(又名CPython)中的递归不是很有效,因为它不能进行消除。此外,它还应用递归限制(默认情况下为1000级,但用户可以修改)

要生成的序列称为,Wikipedia文章给出了一个简单的算法,借助标准函数很容易实现

#!/usr/bin/env python3

''' Generate the compositions of num of a given width

    Algorithm from 
    https://en.wikipedia.org/wiki/Composition_%28combinatorics%29#Number_of_compositions

    Written by PM 2Ring 2016.11.11
'''

from itertools import combinations

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

# test

for t in compositions(5, 4):
    print(t)

print('- ' * 20)

for t in compositions(3, 3):
    print(t)
输出

[0, 0, 0, 5]
[0, 0, 1, 4]
[0, 0, 2, 3]
[0, 0, 3, 2]
[0, 0, 4, 1]
[0, 0, 5, 0]
[0, 1, 0, 4]
[0, 1, 1, 3]
[0, 1, 2, 2]
[0, 1, 3, 1]
[0, 1, 4, 0]
[0, 2, 0, 3]
[0, 2, 1, 2]
[0, 2, 2, 1]
[0, 2, 3, 0]
[0, 3, 0, 2]
[0, 3, 1, 1]
[0, 3, 2, 0]
[0, 4, 0, 1]
[0, 4, 1, 0]
[0, 5, 0, 0]
[1, 0, 0, 4]
[1, 0, 1, 3]
[1, 0, 2, 2]
[1, 0, 3, 1]
[1, 0, 4, 0]
[1, 1, 0, 3]
[1, 1, 1, 2]
[1, 1, 2, 1]
[1, 1, 3, 0]
[1, 2, 0, 2]
[1, 2, 1, 1]
[1, 2, 2, 0]
[1, 3, 0, 1]
[1, 3, 1, 0]
[1, 4, 0, 0]
[2, 0, 0, 3]
[2, 0, 1, 2]
[2, 0, 2, 1]
[2, 0, 3, 0]
[2, 1, 0, 2]
[2, 1, 1, 1]
[2, 1, 2, 0]
[2, 2, 0, 1]
[2, 2, 1, 0]
[2, 3, 0, 0]
[3, 0, 0, 2]
[3, 0, 1, 1]
[3, 0, 2, 0]
[3, 1, 0, 1]
[3, 1, 1, 0]
[3, 2, 0, 0]
[4, 0, 0, 1]
[4, 0, 1, 0]
[4, 1, 0, 0]
[5, 0, 0, 0]
- - - - - - - - - - - - - - - - - - - - 
[0, 0, 3]
[0, 1, 2]
[0, 2, 1]
[0, 3, 0]
[1, 0, 2]
[1, 1, 1]
[1, 2, 0]
[2, 0, 1]
[2, 1, 0]
[3, 0, 0]
FWIW,上面的代码可以在我的旧2GHz 32位机器上(运行在Python 3.6或Python 2.6上)在大约1.6秒内生成170544个
合成序列(15,8)
。(通过使用Bash
time
命令获得定时信息)


FWIW,这是一个递归版本,来自用户3736966。我对它进行了修改,使其使用与代码相同的参数名,使用列表而不是元组,并与Python 3兼容

def compositions(num, width, parent=[]):
    if width > 1:
        for i in range(num, -1, -1):
            yield from compositions(i, width - 1, parent + [num - i])
    else:
        yield parent + [num]
有点令人惊讶的是,这个版本比原始版本快了一点,对于
合成(15,8)
,计时时间大约为1.5秒

如果您的Python版本不理解来自的
yield,您可以执行以下操作:

def compositions(num, width, parent=[]):
    if width > 1:
        for i in range(num, -1, -1):
            for t in compositions(i, width - 1, parent + [num - i]):
                yield t 
    else:
        yield parent + [num]
要按降序生成合成,只需反转
范围
调用,即
范围内的i(num+1):

最后,这是一个无法阅读的单行版本。:)


作为一个根深蒂固的修补匠,我无法阻止自己制作另一个版本这只是原始版本,与itertools文档中列出的
组合
的代码结合在一起。当然,真正的
itertools.combines
比文档中显示的大致相同的Python代码运行得更快

def compositions(num, width):
    r = width - 1
    indices = list(range(r))
    revrange = range(r-1, -1, -1)
    first = [-1]
    last = [num + r]

    yield [0] * r + [num]
    while True:
        for i in revrange:
            if indices[i] != i + num:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1
        yield [v - u - 1 for u, v in zip(first + indices, indices + last)]

这个版本在编写
合成(15,8)
时比原来的版本慢了50%左右:在我的机器上大约需要2.3秒。

你认为itertools为什么会填满你的RAM?它是专门为惰性迭代器设计的,它不会一次创建所有排列。@jornsharpe实际上我们已经试过了。这比我们自己的解决方案要花更长的时间。不过,这和填满内存不同……好吧,让我们说它花的时间太长了。可能更好您确实需要明确说明是否要求返回的组合加起来等于
数字(而不仅仅是每个数字允许的最大值)。这不是锁组合的正常要求。
itertools
也不能直接为您做任何事情。感谢您链接到Tail call。学到的东西,哇!谢谢你,伙计。这正是我要找的。@fab da boy,我的荣幸!如果你感兴趣的话,我已经在我的答案中添加了一个我在其他地方找到的递归解决方案。尽管我前面说过,递归解决方案应该是可以的。干得好!我不熟悉发电机。但这正是我要找的!再次非常感谢。@fab da boy谢谢!我有点惊讶递归版本如此之快。FWIW,我添加了另一个迭代解决方案。它基于我的原始版本,但是它在内部生成组合,所以它比原始版本慢。
def compositions(num, width):
    r = width - 1
    indices = list(range(r))
    revrange = range(r-1, -1, -1)
    first = [-1]
    last = [num + r]

    yield [0] * r + [num]
    while True:
        for i in revrange:
            if indices[i] != i + num:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1
        yield [v - u - 1 for u, v in zip(first + indices, indices + last)]