简体   繁体   English

Python 循环或任何迭代以找到满足条件的所有组合

[英]Python loops or any iterations to find all the combinations such that a condition is satisfied

I want to find all possible combinations of n numbers such that the sum is = 100 in Python我想在 Python 中找到 n 个数字的所有可能组合,使得总和 = 100

A sample of 2 numbers: 2个数字的样本:

x=[]
for i, j in itertools.product(range(0,101), range(0,101)):
    if i+j==100:
       x.append([i,j])

Any alternative and clever way to do this with a variable number of iteration variables and get the outcome in the form of this: n=5:使用可变数量的迭代变量执行此操作并以以下形式获得结果的任何替代和聪明的方法:n = 5:

[[10,10,10,30,40], [100,0,0,0,0], [1,1,2,3,97] .......]

itertools.product takes a repeat argument, which you can use to repeat the range(1, 101) iterator repeat number of times. itertools.product接受一个repeat参数,您可以使用它来repeat range(1, 101)迭代器repeat次数。 This way you don't need to specify the iterator multiple times or generate the desired number of arguments.这样您就不需要多次指定迭代器或生成所需数量的参数。 For example, for 5 times:例如5次:

[i for i in itertools.product(range(1, 101), repeat=5) if sum(i) == 100]

A Pure Python Solution (ie without itertools.product )纯 Python 解决方案(即没有itertools.product

The main difficulty here is executing a variable number of for-loops inside a function.这里的主要困难是在函数内执行可变数量的for-loops The way we can get around this easily is using recursion which involves a function calling itself.我们可以轻松解决这个问题的方法是使用涉及调用自身的函数的递归

If we use recursion, then inside any instance of the function, only one for-loop is actually iterated through.如果我们使用递归,那么在函数的任何实例中,实际上只迭代一个for-loop So to apply this to the problem at hand, we want our function to take two parameters: the target for what number we are trying to sum to, and n - the number of positive integers we have available to use.因此,要将其应用于手头的问题,我们希望我们的函数采用两个参数:我们试图求和的数字的目标,以及n - 我们可以使用的正整数的数量。

Each function will then return (given a target and n numbers), all the combinations that will make that target - in the form of a two-dimensional list .然后每个函数将返回(给定一个targetn数字),所有组合将构成该目标- 以二维list的形式。

The only special case that we must consider is the "leaf nodes" of our recursive tree (the cases where we have a certain target, but n == 1 , so we only have one number to make the target with).我们必须考虑的唯一特殊情况是递归树的“叶节点”(我们有特定目标的情况,但n == 1 ,因此我们只有一个数字来构成目标)。 This is easy to handle, we just need to remember that we should always return all combinations that make the target so in this case, there is only one "combination" which is the target .这很容易处理,我们只需要记住我们应该始终返回构成目标的所有组合,因此在这种情况下,只有一个“组合”就是target

Then (if n > 1 ) the rest is self explanatory, we are simply looping through every number less than target and adding to a list of combinations ( cs ) with the results of calling the function again.然后(如果n > 1 )剩下的就不言自明了,我们只是循环遍历每个小于target然后将再次调用该函数的结果添加到组合list ( cs ) 中。

However, before we concatenate these combos onto our list , we need to use a comprehension to add i (the next number) to the start of every combination.然而,在我们将这些组合连接到我们的list ,我们需要使用comprehensioni (下一个数字)添加到每个组合的开头。

And that's it!就是这样! Hopefully you can see how the above translates into the following code:希望您能看到以上内容如何转换为以下代码:

def combos(target, n):
    if n == 1:
        return [[target]]
    cs = []
    for i in range(0, target+1):
        cs += [[i]+c for c in combos(target-i, n-1)]
    return cs

and a test (with target as 10 and n as 3 to make it clearer) shows it works:并且测试( target10n3以使其更清晰)表明它有效:

>>> combos(10, 3)
[[0, 0, 10], [0, 1, 9], [0, 2, 8], [0, 3, 7], [0, 4, 6], [0, 5, 5], [0, 6, 4], [0, 7, 3], [0, 8, 2], [0, 9, 1], [0, 10, 0], [1, 0, 9], [1, 1, 8], [1, 2, 7], [1, 3, 6], [1, 4, 5], [1, 5, 4], [1, 6, 3], [1, 7, 2], [1, 8, 1], [1, 9, 0], [2, 0, 8], [2, 1, 7], [2, 2, 6], [2, 3, 5], [2, 4, 4], [2, 5, 3], [2, 6, 2], [2, 7, 1], [2, 8, 0], [3, 0, 7], [3, 1, 6], [3, 2, 5], [3, 3, 4], [3, 4, 3], [3, 5, 2], [3, 6, 1], [3, 7, 0], [4, 0, 6], [4, 1, 5], [4, 2, 4], [4, 3, 3], [4, 4, 2], [4, 5, 1], [4, 6, 0], [5, 0, 5], [5, 1, 4], [5, 2, 3], [5, 3, 2], [5, 4, 1], [5, 5, 0], [6, 0, 4], [6, 1, 3], [6, 2, 2], [6, 3, 1], [6, 4, 0], [7, 0, 3], [7, 1, 2], [7, 2, 1], [7, 3, 0], [8, 0, 2], [8, 1, 1], [8, 2, 0], [9, 0, 1], [9, 1, 0], [10, 0, 0]]

Improving performance提高性能

If we consider the case where we are trying to make 10 with 4 numbers.如果我们考虑尝试用4数字组成10的情况。 At one point, the function will be called with a target of 6 after say 1 and 3 .在某一时刻,该函数将在13之后以6的目标被调用。 The algorithm will as we have already explained and return the combinations using 2 numbers to make 6 .正如我们已经解释的那样,该算法将使用2数字返回组合以生成6 However, if we now consider another case further down the line when the function is asked to give the combinations that make 6 (same as before) having been called with say 2 and 2 .然而,如果我们现在考虑另一种情况,当函数被要求给出使6 (与以前相同)被调用的组合时,例如22 Notice how even though we will get the right answer (through our recursion and the for-loop ), we will return the same combinations as before - when we were called with 1 and 3 .注意即使我们会得到正确的答案(通过我们的递归for-loop ),我们将返回与之前相同的组合 - 当我们被调用13 Furthermore, this scenario will happen extremely often: the function will be called from different situations but be asked to give the same combinations that have already been previously calculated at a different time.此外,这种情况会非常频繁地发生:该函数将在不同情况下被调用,但被要求提供先前在不同时间计算过的相同组合。

This gives way to a great optimisation technique called memoization which essentially just means storing the results of our function as a key: value pair in a dictionary ( mem ) before returning.这让位于一种称为memoization的出色优化技术,它本质上只是意味着在返回之前将我们函数的结果作为key: value对存储在字典 ( mem ) 中。

We then just check at the start of every function call if we have ever been called before with the same parameters (by seeing if the key is in the dictionary) and if it is, then we can just return the result we got last time.然后我们只需在每个函数调用开始时检查我们之前是否曾使用相同的参数调用过(通过查看键是否在字典中),如果是,那么我们可以返回上次得到的结果。

This speeds up the algorithm dramatically.这大大加快了算法的速度。

mem = {}
def combos(target, n):
    k = (target, n)
    if k in mem:
        return mem[k]
    if n == 1:
        return [[target]]
    cs = []
    for i in range(0, target+1):
        cs += [[i]+c for c in combos(target-i, n-1)]
    mem[k] = cs
    return cs

This is a trivial generalisation / partial optimisation of your algorithm.这是算法的简单概括/部分优化。

Edit : @heemayl's alternative which uses repeat is preferable to this solution.编辑@heemayl使用repeat的替代方案比此解决方案更可取。

import itertools

n = 3
x = []

x = [list(i) for i in itertools.product(*(range(0,101) \
             for _ in range(n))) if sum(i) == 100]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM