有没有办法在没有嵌套循环的情况下遍历数字列表的所有组合?

Posted

技术标签:

【中文标题】有没有办法在没有嵌套循环的情况下遍历数字列表的所有组合?【英文标题】:Is there a way to go through all combinations of a list of numbers without nested loops? 【发布时间】:2013-11-22 05:46:05 【问题描述】:

我的问题有点难以解释,所以我会尽力而为。我正在编写一个程序,该程序将采用目标数字和其他数字列表。我希望它从列表中添加所有可能的数字组合,直到列表中的数字组合总和与目标数字相同。例如,如果目标数字是 6,并且提供的列表中有数字 ,那么程序将打印 2+4=6 的解。

我目前的程序设置了 4 个嵌套循环,其中最外层的循环检查第一个数字作为常量的组合。第二个循环保持第二个数字不变,其他两个也是如此。如果上述列表的目标数为 20,则程序将按以下方式检查:

2
2+3
2+3+4
2+3+4+5
2+3+5
2+4
2+4+5
2+5
3
3+4
3+4+5
3+5
4
4+5
5

然后它会返回一条消息说没有找到解决方案。这适用于小列表,但如果列表包含许多小数字并且目标数字很大,则效果不佳。由于程序只有四个循环,所以最多只能添加 4 个数字。

我不禁想到必须有更好的方法来做到这一点,因为对于更长的列表,唯一的解决方案是制作更多的嵌套循环,这是不切实际的。

我希望这很容易理解。如果您想查看我的代码,请告诉我。感谢您的帮助!

【问题讨论】:

这个被称为knapsack problem的问题是NP完全的,这意味着即使使用最好的算法,一旦问题的规模扩大,总会有一些情况非常昂贵。 是的,使用递归。递归可以处理你喜欢的任何长度的列表。 CodesInChaos,我不知道这个问题有什么名字。以后读起来应该很有趣。 @john,我什至没有想过使用递归,但这是一个绝妙的主意,而且效果很好。非常感谢您的建议! 【参考方案1】:

您可以使用这样的递归方法:

#include <iostream>
#include <vector>

using namespace std;

vector<bool> active_pos;
vector<int> input;

int recurse (int current_sum, int target_sum, int length) 
    if (length < 0) 
        return -1;
    

    for (int i = 0; i < input.size(); i++) 
        if (active_pos[i]) 
            continue;
        

        active_pos[i] = true;

        int tmp_sum = current_sum + input[i];


        if (tmp_sum == target_sum) 
            return tmp_sum;
        

        int next_sum = recurse(tmp_sum, target_sum, length-1);
        if (next_sum == target_sum) 
            return next_sum;
        

        active_pos[i] = false;
    
    return -1;


int main(int argc, const char **argv) 
    input.push_back(2);
    input.push_back(3);
    input.push_back(4);
    input.push_back(5);
    input.push_back(6);

    int length = input.size();
    active_pos.resize(length);

    int res = recurse(0, 10, length);

    cout << "Result is: " << res << endl;
    cout << "Used numbers are: " <<  endl;
    for (int j = 0; j < length; j++) 
        if (active_pos[j]) 
            cout << input[j] << " ";
        
    
    cout << endl;

函数recurse() 调用自身,每次将length 参数减一。一个向量会跟踪已经使用的值。

如果recurse() 的实例找到匹配的总和,则返回该值。如果不是,则返回 -1。如果最终结果为 -1,则没有找到匹配项。

【讨论】:

谢谢!我最终使用了递归方法,因此能够引用它非常有帮助。【参考方案2】:

我会从另一个方向来解决这个问题....将列表中的所有数字加在一起,如果它不等于目标值,您就完成了....如果添加的列表更大比目标,然后开始从列表的开头删除项目,直到您匹配目标值。如果不匹配,(跳过目标值)并且加法值变得小于目标值,则适当处理该情况。

这应该只需要 1 个循环。

【讨论】:

这实际上是我遇到问题时的最初想法。不过,跟踪数字开始变得乏味,所以我改用循环。不过感谢您的意见!【参考方案3】:

您可以将每个组合表示为N 一位值,设置该元素是否应在组合中。然后遍历所有组合就相当于从 1 数到 2N,用一个内循环将设置的位对应的值相加。

如果设置大小为 64 或更少,那么您可以使用 uint64_t 循环计数器轻松完成此操作。如果它更大,那么你可能活不到足够长的时间来看到结果。

【讨论】:

我不确定我是否完全理解你在说什么,但如果我理解正确,我会尝试这样做,但认为这需要一段时间。但是,我能够使用 John 和 Atle 建议的递归,所以它奏效了。不过感谢您的回复!

以上是关于有没有办法在没有嵌套循环的情况下遍历数字列表的所有组合?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在不使用pairs()的情况下循环遍历数组?

有没有办法在目标 C 中循环遍历 nsdictionary,其中键值未知且字典深度嵌套

有没有办法在不使用游标的情况下循环遍历 SQL 中的表变量?

是否可以在没有嵌套循环的情况下读取整个 XML 文件

如何循环并获取firebase中嵌套节点的所有键?

为啥在 JavaScript 中显示数组元素的上限为 100,有没有办法在没有循环或 splice() 的情况下显示所有内容? [复制]