C++子集和2^n/递归错误/澄清
C++ subset sum 2^n/recursion bug/clarification
这不是家庭作业,我没有钱上学,所以我在高速公路上的收费站轮班时自学(晚上很长,顾客很少)。
我正在尝试实现一个简单的子集求和算法,该算法在给定一个整数数组的情况下,返回一个子集,其总和等于所需的总和,并报告需要调用多少次才能找到它。
我使用Collections在Java中实现了一个,但这是一个非常臃肿的代码,即使我能够返回所有集合加起来达到所需的数字,并告诉函数是否在第一次匹配时停止。
我对这段代码的问题如下:它不是在2^n时间内运行(对于这样一个没有结果的实现来说,这是正确的,不是吗?),而是在[2^(n+1)]-1时间内运行;O(2^n)如评论所指出的。我明白为什么我会在比我能做的更深的层次上检查(runningTotal==targetTotal),本质上是自己增加了额外的深度,不是吗?我试图尽可能干净地对基本情况进行建模,如果您检测到任何"代码气味",请告诉我。我应该一看到就崩溃吗(runningTotal+consider)==targetTotal?
注意:我不认为这属于"代码审查",因为我问的是特定的代码行,而不是整体方法(如果我需要改变方法,那就顺其自然,我这样做是为了学习)。
这里是我的尝试(除了上面提到的缺乏优化之外,这个"还可以"的C/C++吗?):
#include <iostream>
using namespace std;
bool setTotalling(int chooseFrom[], int nChoices, int targetTotal,
int chooseIndex, int runningTotal, int solutionSet[], int &solutionDigits,
int &nIterations) {
nIterations++;
if (runningTotal == targetTotal) {
return true;
}
if (chooseIndex >= nChoices) {
return false;
}
int consider = chooseFrom[chooseIndex];
if (setTotalling(chooseFrom, nChoices, targetTotal, chooseIndex + 1,
runningTotal + consider, solutionSet, solutionDigits, nIterations)) {
solutionSet[solutionDigits++] = consider;
return true;
}
if (setTotalling(chooseFrom, nChoices, targetTotal, chooseIndex + 1,
runningTotal, solutionSet, solutionDigits, nIterations)) {
return true;
}
return false;
}
void testSetTotalling() {
int chooseFrom[] = { 1, 2, 5, 9, 10 };
int nChoices = 5;
int targetTotal = 23;
int chooseIndex = 0;
int runningTotal = 0;
int solutionSet[] = { 0, 0, 0, 0, 0 };
int solutionDigits = 0;
int nIterations = 0;
cout << "Looking for a set of numbers totalling" << endl << "--> "
<< targetTotal << endl << "choosing from these:" << endl;
for (int i = 0; i < nChoices; i++) {
int n = chooseFrom[i];
cout << n << ", ";
}
cout << endl << endl;
bool setExists = setTotalling(chooseFrom, nChoices, targetTotal, chooseIndex,
runningTotal, solutionSet, solutionDigits, nIterations);
if (setExists) {
cout << "Found:" << endl;
for (int i = 0; i < solutionDigits; i++) {
int n = solutionSet[i];
cout << n << ", ";
}
cout << endl;
} else {
cout << "Not found." << endl;
}
cout << "Iterations: " << nIterations << endl;
}
int main() {
testSetTotalling();
return 0;
}
重点是如何计算"迭代"。假设您有一个简单的例子,n=1
的目标是一个不为零的和,而不是您所拥有的元素。
你调用函数,这会立即增加计数器,然后你到达分支,函数调用自己两次(一次考虑元素,一次不考虑元素)。这些呼叫中的每一个都将计数为1,因此您最终将得到一个总数为3的计数器。
我看不出有什么问题。。。
如果剩余选项的麻木为零,您可以添加一个特殊的检查来重复测试并避免调用,但这需要重复检查。只在递归调用处进行结束检查不会考虑到可以直接使用零选择来调用函数。基本上,您正在"内联"级别0……但为什么要停止在级别0,而不内联级别1呢?
如果你正在寻找加速,请注意(假设所有元素都是非负的)如果你知道添加所有剩余的可用数字仍然不足以达到目标,那么你可以避免检查所有可能的子集。通过计算一次从给定索引到可用元素列表末尾的所有剩余数字的总和(这是O(n)
计算),您可以保存(剩余2^)迭代。此外,如果当前的总和已经太大,那么考虑添加其他元素也没有意义。
if (targetTotal > runningTotal)
return false; // We already passed the limit
if (targetTotal - runningTotal > sumOfAllFrom[choseIndex])
return false; // We're not going to make it
如果你也按递减顺序对元素进行排序,上述优化可以节省很多。
- C++:正在检查LinkedList中的回文-递归方法-错误
- 从类型列表中递归删除重复项会导致编译器堆空间错误 (VS2017)
- 递归求和任务的错误答案
- 为什么我的递归代码给出 sigsegv 错误?
- 任何人都可以查明我的递归函数中的错误吗?
- 如何修复递归函数导致的堆栈溢出错误?C++
- 运行合并排序递归算法时EXC_BAD_ACCESS错误
- 在尝试使用递归查找集合子集的总数时,我遇到了分割错误
- 对可变参数函数的递归调用的链接器错误
- 我试图用c++编写递归fibonacci序列,但当我编译时,我遇到了一个错误
- 使用递归函数 (c++) 将长字符串转换为整数时输出错误
- 递归树遍历/分支删除的隔离错误
- Fibbonaci 递归代码返回错误值,始终返回下一个数字
- 为什么使用递归lambda时会遇到编译错误
- 使用递归方法遇到分段错误
- 分段错误(核心转储)错误C++递归调用
- 尝试"复制"shared_ptr向上转换行为会导致复制构造函数上的无限递归(导致段错误)
- C++子集和2^n/递归错误/澄清
- 为什么通过引用传递可以修复我的递归错误
- 解析c++源代码时相互左递归错误