我用Python制作练习程序来计算我可以放在架子上的所有不同重量,并使用一组给定的板块。
def Weights(t=1,f=1,tn=1,tf=1,thf=1,ff=1,b=45):
Max=b+5*t+10*f+20*tn+50*tf+70*thf+90*ff
Poss=range(b,Max+1,5)
ts=(list((i*5 for i in range(0,t+1))))
fs=(list((i*10 for i in range(0,f+1))))
tns=(list((i*20 for i in range(0,tn+1))))
tfs=(list((i*50 for i in range(0,tf+1))))
thfs=(list((i*70 for i in range(0,thf+1))))
ffs=(list((i*90 for i in range(0,ff+1))))
Weights()
这给我留下了6个清单。为了获得所有组合,我需要将列表中的一个元素与每个其他列表的一个元素相加。在这一点上,它显然是一个线性代数问题,我只是不知道如何在Python中表达这一点,特别是因为我不想使用插件(No NumPy)
答案 0 :(得分:3)
要获得所有组合,我需要将列表中的一个元素与每个列表中的一个元素相加。
听起来你想要itertools.product
。为了简化示例,让我们只取七个术语中的三个(但是将更多或更少的参数传递给product
)是微不足道的:
import itertools
b = [45] # I assume this represents the "bar" and that it's not optional
ts = [0, 5, 10, 15]
fs = [0, 10]
combos = itertools.product(b, ts, fs)
for combo in combos: print(list(combo))
打印以下内容:
[45, 0, 0]
[45, 0, 10]
[45, 5, 0]
[45, 5, 10]
[45, 10, 0]
[45, 10, 10]
[45, 15, 0]
[45, 15, 10]
......当你提到“所有组合”时,这听起来像你想要的那样。
要获得每个的总和,可以在一行中完成:
totals = [sum(combo) for combo in itertools.product(b, ts, fs)]
这是一种可能灵活的参数化功能的方法:
import itertools
def PossibleWeights(*weights):
return list(itertools.product(*[[weightset] if isinstance(weightset, (int, float)) else [sum(weightset[:i]) for i in range(len(weightset)+1)] for weightset in weights]))
当你打电话时,比如PossibleWeights( 45, [10]*2, [5]*3 )
,呼叫本身会明确(并且可读)有一个强制性的45磅重量,两个可能的10磅重量和三个可能的5磅重量。关于你传递了多少这样的参数以及它们的值是什么,你有完全的灵活性。
然后,您可以使用dict
将每个总权重与用于实现它的组合相关联(顺便删除重复的总数,其中多个组合加起来相同):
d = {}
for combo in PossibleWeights( 45, [10]*2, [5]*3 ):
d[sum(combo)] = combo
...然后漂亮打印结果:
for total, combo in sorted(d.items()):
print('sum(%r) = %r' % (combo, total))
输出:
sum((45, 0, 0)) = 45
sum((45, 0, 5)) = 50
sum((45, 10, 0)) = 55
sum((45, 10, 5)) = 60
sum((45, 20, 0)) = 65
sum((45, 20, 5)) = 70
sum((45, 20, 10)) = 75
sum((45, 20, 15)) = 80
顺便说一句:如果你想用最少数量的盘子来达到每一个总数,请确保在打电话给PossibleWeights
时在较轻的盘子前面传递较重的盘子,如上例所示。
平衡栏的两侧留给读者;-)
答案 1 :(得分:1)
我认为你的方法是错误的,而且你的签名也是不好的设计:如果你有7种不同的权重,那该怎么办?我认为输入作为元组列表(weight,max_number)更合适。当你开始用这样的术语思考时,你就会明白“交叉加入”#34; 7列表也是错误的做法。你想要的是拥有一个所有简单权重的列表,而不是获得power set即所有子集。 Rosetta Code在Python中有一些很好的power set实现。我特别喜欢这个,因为它不会强制在内存中产生整个功率集,这可能是一个问题:
def powersequence(val):
''' Generate a 'powerset' for sequence types that are indexable by integers.
Uses a binary count to enumerate the members and returns a list
Examples:
>>> powersequence('STR') # String
['', 'S', 'T', 'ST', 'R', 'SR', 'TR', 'STR']
>>> powersequence([0,1,2]) # List
[[], [0], [1], [0, 1], [2], [0, 2], [1, 2], [0, 1, 2]]
>>> powersequence((3,4,5)) # Tuple
[(), (3,), (4,), (3, 4), (5,), (3, 5), (4, 5), (3, 4, 5)]
>>>
'''
vtype = type(val); vlen = len(val); vrange = range(vlen)
return [ reduce( lambda x,y: x+y, (val[i:i+1] for i in vrange if 2**i & n), vtype())
for n in range(2**vlen) ]
或者您可以使用itertools powerset recipe构建一个实际上更快(感谢umutto)
def powersequence(val):
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
所以算法是:
ts
,fs
,...)powersequence
生成powerset def all_weights(ws):
simpleWeights = [0] # add 0 just one time
for (w, c) in ws:
simpleWeights = simpleWeights + [w] * c
allWeights = (sum(subset) for subset in powersequence(simpleWeights))
return set(allWeights) # filter uniqueness
all = all_weights([(1, 1), (5, 1), (10, 1), (20, 1), (50, 1), (70, 1), (90, 1)])
for w in all:
print(w)
请注意,唯一性条件强制将整个集合实现到内存中,因此可能是此代码可以解决的问题大小的限制因素。
<强>更新强>
实际上,jez是对的:用硬币做列表产品会比通过加入列表的powersequence
迭代要快得多
def all_weights(ws):
simpleWeights = []
for (w, c) in ws:
simpleWeights.append(list((i * w for i in range(0, c + 1))))
allWeights = (sum(subset) for subset in itertools.product(*simpleWeights)) # note "*" before simpleWeights!
return set(allWeights) # filter uniqueness