Python 对于数字列表,查找其累积和在范围内的所有组合
假设我有以下列表:Python 对于数字列表,查找其累积和在范围内的所有组合,python,combinations,permutation,combinatorics,Python,Combinations,Permutation,Combinatorics,假设我有以下列表: [A,A,A,A,B,B] 其中A=1,B=-1 查找此列表的所有组合通常很容易(8nCr3),但我想在累积和达到某些界限值(例如0和4)时忽略排列 因此,上面的例子是不正确的,因为累积总和是 [1,2,3,4,5,4,3,2]。 排列[A,A,B,B,A,A,B,A]也不好,因为这里的累积和是[1,2,1,0,1,2,1,2] 我的问题是:如何计算两个组件的有限列表的排列数 如果存在一个简单插入解析解的函数,我会非常高兴,但迭代/递归函数也可以 编辑:我其实不需要知道排列
[A,A,A,A,B,B]
其中A=1,B=-1
查找此列表的所有组合通常很容易(8nCr3),但我想在累积和达到某些界限值(例如0和4)时忽略排列
因此,上面的例子是不正确的,因为累积总和是
[1,2,3,4,5,4,3,2]。排列
[A,A,B,B,A,A,B,A]
也不好,因为这里的累积和是[1,2,1,0,1,2,1,2]
我的问题是:如何计算两个组件的有限列表的排列数
如果存在一个简单插入解析解的函数,我会非常高兴,但迭代/递归函数也可以
编辑:我其实不需要知道排列是什么样的。只要它们的数量就足够了。一个可能的解决方案是使用
itertools.permutations
生成置换,然后检查每个置换
这种方法存在一些问题:
- 无效排列的数量可能很大,因此我们将免费生成许多排列
- 如果一个排列的第一项超出了界限,我们知道所有具有相同开头的排列都是无效的,但是没有办法使
跳过它们,因此我们无论如何都必须生成它们itertools。排列
- 我们必须重新计算每个新排列的所有部分和
def bounded_permutations(data, lower, upper, start=None, curr_sum=0):
if start is None:
start = []
for idx_first, first in enumerate(data):
new_sum = curr_sum + first
if not lower < new_sum < upper:
continue
new_data = data[:]
del new_data[idx_first]
new_start = start + [first]
if not new_data: # we used all values in the list!
yield new_start
else:
yield from bounded_permutations(new_data, lower, upper, new_start, new_sum)
输出:
[1, 1, 1, -1, 1, -1, -1] 1
[1, 1, 1, -1, 1, -1, -1] 2
[1, 1, 1, -1, -1, 1, -1] 3
[1, 1, 1, -1, -1, 1, -1] 4
[1, 1, 1, -1, 1, -1, -1] 5
.
.
.
[1, 1, -1, 1, -1, 1, -1] 575
[1, 1, -1, 1, -1, 1, -1] 576
{(1, 1, -1, 1, 1, -1, -1),
(1, 1, -1, 1, -1, 1, -1),
(1, 1, 1, -1, 1, -1, -1),
(1, 1, 1, -1, -1, 1, -1)}
number of unique permutations: 4
(1, 1, 1, -1, -1, 1, -1)
(1, 1, -1, 1, -1, 1, -1)
(1, 1, -1, 1, 1, -1, -1)
(1, 1, 1, -1, 1, -1, -1)
在这里,我们得到了576个有效排列,总共7个!=5040我们也可以用len(列表(有界排列(数据,0,4))
来计算它们
由于数据中存在重复的值,您将得到许多相同的排列。如果只想保留唯一的,可以使用集
。请注意,您必须将不可破坏列表转换为可哈希元组,才能在集合中使用它们:
uniques = set(map(tuple, bounded_permutations(data, 0, 4)))
print(uniques)
print('number of unique permutations:', len(uniques))
输出:
[1, 1, 1, -1, 1, -1, -1] 1
[1, 1, 1, -1, 1, -1, -1] 2
[1, 1, 1, -1, -1, 1, -1] 3
[1, 1, 1, -1, -1, 1, -1] 4
[1, 1, 1, -1, 1, -1, -1] 5
.
.
.
[1, 1, -1, 1, -1, 1, -1] 575
[1, 1, -1, 1, -1, 1, -1] 576
{(1, 1, -1, 1, 1, -1, -1),
(1, 1, -1, 1, -1, 1, -1),
(1, 1, 1, -1, 1, -1, -1),
(1, 1, 1, -1, -1, 1, -1)}
number of unique permutations: 4
(1, 1, 1, -1, -1, 1, -1)
(1, 1, -1, 1, -1, 1, -1)
(1, 1, -1, 1, 1, -1, -1)
(1, 1, 1, -1, 1, -1, -1)
您可以使用
itertools
生成排列和累积列表。返回其累积列表不包含某些特定值的唯一置换的紧凑解决方案是:
import itertools
def contain_specific_value(lst, value_lst):
return any([True if (item in lst) else False for item in value_lst])
def accumulated_permutations_without_specific_numbers(main_lst, specific_number_lst):
p = list(itertools.permutations(main_lst, len(main_lst)))
res = [y for y in p if not contain_specific_value(list(itertools.accumulate(y)), specific_number_lst)]
return list(set(res))
您只需要将某些绑定值作为列表传递给函数
对于您的列表:
A=1
B=-1
my_list = [A, A, A, A, B, B, B]
res = accumulated_permutations_without_specific_numbers(my_list, [0,4])
输出:
[1, 1, 1, -1, 1, -1, -1] 1
[1, 1, 1, -1, 1, -1, -1] 2
[1, 1, 1, -1, -1, 1, -1] 3
[1, 1, 1, -1, -1, 1, -1] 4
[1, 1, 1, -1, 1, -1, -1] 5
.
.
.
[1, 1, -1, 1, -1, 1, -1] 575
[1, 1, -1, 1, -1, 1, -1] 576
{(1, 1, -1, 1, 1, -1, -1),
(1, 1, -1, 1, -1, 1, -1),
(1, 1, 1, -1, 1, -1, -1),
(1, 1, 1, -1, -1, 1, -1)}
number of unique permutations: 4
(1, 1, 1, -1, -1, 1, -1)
(1, 1, -1, 1, -1, 1, -1)
(1, 1, -1, 1, 1, -1, -1)
(1, 1, 1, -1, 1, -1, -1)
累计名单:
[1, 2, 3, 2, 1, 2, 1]
[1, 2, 1, 2, 1, 2, 1]
[1, 2, 1, 2, 3, 2, 1]
[1, 2, 3, 2, 3, 2, 1]
下面是一个使用动态规划(记忆)的递归解决方案,以便即时处理非常大的列表:
from functools import lru_cache
def boundCumSum(A,loBound,hiBound):
sRange = range(loBound+1,hiBound)
@lru_cache()
def count(s,nPos,nNeg):
if nPos==0 and nNeg==0: return int(s == 0)
if nPos<0 or nNeg<0 or not s in sRange: return 0
return count(s-1,nPos-1,nNeg)+count(s+1,nPos,nNeg-1)
nPos = sum(a>0 for a in A)
nNeg = sum(a<0 for a in A)
return count(sum(A),nPos,nNeg)
为了验证这一点,可以使用生成器函数来显示实际解及其累积和:
def genCumSum(A,loBound,hiBound,combo=[]):
if not A: yield combo; return
for v,i in {v:i for i,v in enumerate(A)}.items():
if v<=loBound or v >=hiBound: continue
yield from genCumSum(A[:i]+A[i+1:],loBound-v,hiBound-v,combo+[v])
到目前为止你都试了些什么?非常感谢。我相信它是有效的,但是如果我将列表大小增加到20年代的某个地方,这个函数将花费很长时间。是的,当然,20个元素的排列数是20!=2432902008176640000,约为2.4*10^18。如果你能每秒生成10^6个排列,那么需要77147年才能生成所有排列,而且你无法存储它们。这个解决方案可以避免生成我们确信会失败的解决方案,也可以避免生成许多解决方案,但根据值和边界,解决方案的数量可能会很大。作为旁注,如果你觉得其中一个答案回答了你的问题,你可以接受它(左边的复选标记)和/或投赞成票。就像Thierry Lathuille的答案一样,我认为这个函数对于相当小的列表来说已经花费了很长时间。但你的回答很有见地,谢谢。