列表中所有可能的总和,每个列表只汇总一个条目

时间:2017-03-17 01:04:54

标签: python list math

我用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)

2 个答案:

答案 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))

所以算法是:

  1. 构建所有简单权重的单个列表(加入tsfs,...)
  2. 使用powersequence生成powerset
  3. 汇总每个子集中的所有项目
  4. 将结果设置为过滤唯一性
  5. 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