C++ 改进递归问题解决的合理方法是什么?
我喜欢在TopCoder网站上解决算法问题。我可以实现大多数基本的递归问题,如回溯、dfs。。。然而,每当我遇到一个复杂的递归时,它常常会花费我好几个小时。当我检查其他程序员的解决方案时,我为自己感到羞愧。我已经编程将近5年了。我可以看到其他编程技术的显著改进,如操作字符串、图形、GUI。。。但不是递归?有谁能分享一些如何处理递归问题的经验吗?谢谢 更新 我熟悉单元测试方法。甚至在我了解单元测试之前,我经常编写一些小测试函数来查看结果是否符合我的要求。当面对递归问题时,我自然会失去这种能力。我可以插入几个“cout”语句来查看当前结果,但是当调用嵌套得很深时,我就无法再跟踪它了。所以大多数时候,要么我先用铅笔和纸解决它,要么我完成了(不能用常规方法,比如把它分成小块)。我觉得递归必须作为一个整体工作 致以最诚挚的问候,C++ 改进递归问题解决的合理方法是什么?,c++,algorithm,recursion,C++,Algorithm,Recursion,我喜欢在TopCoder网站上解决算法问题。我可以实现大多数基本的递归问题,如回溯、dfs。。。然而,每当我遇到一个复杂的递归时,它常常会花费我好几个小时。当我检查其他程序员的解决方案时,我为自己感到羞愧。我已经编程将近5年了。我可以看到其他编程技术的显著改进,如操作字符串、图形、GUI。。。但不是递归?有谁能分享一些如何处理递归问题的经验吗?谢谢 更新 我熟悉单元测试方法。甚至在我了解单元测试之前,我经常编写一些小测试函数来查看结果是否符合我的要求。当面对递归问题时,我自然会失去这种能力。我可
陈我发现铅笔和纸真的很方便。将问题分解成更小的块也是一个好主意,例如使用一个非常小的数据集。您应该做的第一件事是确定基本条件,即标记递归调用结束的条件。从那里,您可以处理递归问题的主体,并使用更大的数据集对其进行测试/验证
我还想补充一点,速度并不是成为一名优秀工程师的唯一条件。工程师还可以拥有许多其他技能,包括跳出框框进行观察和思考的能力、说服他人采取特定行动的能力、分解问题并向非专业人士(利益相关者和客户)解释问题的能力等等。问题的哪些部分需要你花费数小时 你自己没有想到的其他程序员的解决方案呢
作为一条一般性建议,请记住考虑基本情况,然后记住您认为在递归的每个级别都必须保持的不变量。错误经常出现,因为在递归调用中未正确保留不变量。动态编程有帮助。记忆也很有帮助。我认为最好避免递归。在大多数情况下,循环更优雅,更容易理解。循环也更有效,长循环不会因堆栈溢出错误而使程序崩溃 我发现递归是最优雅的解决方案的问题很少。通常这类问题都是关于图形或曲面上的导航。幸运的是,这个领域已经被研究到了极点,所以你可以在网上找到很多算法
在包含不同类型节点的简单图(如树)中导航时,访问者模式通常比递归简单 这是一个很好的问题 我得到的最好答案是分解:分而治之。这是一个有点棘手的C++,因为它不支持高阶函数很好,但你可以做到。最常见的例程是贴图和折叠之类的。[C++已经有一个名为std::acculate]的余码 <>你必须仔细考虑的是如何在可能的情况下构造代码以提供尾部递归。人们很快就可以识别尾声,并将其视为循环,这大大减少了大脑因到处重复而产生的超负荷 另一个好方法叫做信任。这意味着,您可以编写一个对一个您甚至还没有定义的函数的调用,并且您相信它会做您想做的事情,而无需进一步的麻烦。例如,您相信它将自底向上正确访问树的节点,即使它必须调用您当前正在编写的函数。写评论,说明前置和后置条件是什么 另一种方式是(使用这个Oracl或Haskell)的实际编程语言,然后尝试把漂亮的干净代码翻译成C++。通过这种方式,您可以更轻松地查看结构,而不会陷入内务管理细节、难看的语法、缺乏本地化等问题。一旦你把它正确,你可以把它翻译成C++。(或者您可以使用Felix为您翻译) 我说对不起的原因是。。如果你这样做,你就不想再写C++了,这会让你很难找到满意的工作。例如,在Ocaml中,只需添加列表元素(不使用折叠): 这不是尾部递归,但这是:
let addup (ls: int list) : int =
let rec helper ls sum = match ls with
| [] -> sum
| h :: t -> helper t (h+ sum)
in
helper ls 0
上述转变是众所周知的。当您了解第二个例程的作用时,它实际上更简单。我太懒了,不能把它翻译成C++,也许你可以把它转换成代码。(光是算法的结构就足以理解语法)我曾经参加过一个为喜欢编程的疯狂青少年举办的夏令营。他们教我们解决问题的“法式方法”(内部参考)(递归和其他) 1) 用你的老板的话来定义你的问题,并做一些有用的例子
2)进行观察、考虑边缘情况、约束(如:“算法必须是最坏O(n log n)))
3) 决定如何解决这个问题:图论,动态规划(递归),组合经济学 从这里开始,递归特定: 4) 确定“子问题”,从约束中猜出可能有多少子问题,并利用这些子问题进行猜测,通常会有所帮助。最终,一个子问题会“点击”你的脑袋 5) 选择自下而上或自上而下的算法 6) 密码 在整个步骤中,在第6步之前,所有内容都应该用漂亮的笔写在纸上。在编程比赛中,那些立即开始攻丝的人往往表现不佳let addup (ls: int list) : int =
let rec helper ls sum = match ls with
| [] -> sum
| h :: t -> helper t (h+ sum)
in
helper ls 0
#include <iostream>
#include <functional>
#include <map>
#include <time.h>
//maahcros
#define TIME(__x) init=clock(); __x; final=clock()-init; std::cout << "time:"<<(double)final / ((double)CLOCKS_PER_SEC)<<std::endl;
#define TIME_INIT clock_t init, final;
void sleep(unsigned int mseconds) { clock_t goal = mseconds + clock(); while (goal > clock()); }
//the original memoize
template <typename ReturnType, typename... Args>
std::function<ReturnType (Args...)> memoize(std::function<ReturnType (Args...)> func)
{
std::map<std::tuple<Args...>, ReturnType> cache;
return ([=](Args... args) mutable {
std::tuple<Args...> t(args...);
if (cache.find(t) == cache.end()) {
cache[t] = func(args...);
}
return cache[t];
});
}
/// wrapped factorial
struct factorial_class {
/// the original factorial renamed into _factorial
int _factorial(int n) {
if (n==0) return 1;
else {
std::cout<<" calculating factorial("<<n<<"-1)*"<<n<<std::endl;
sleep(100);
return factorial(n-1)*n;
}
}
/// the trick function :)
int factorial(int n) {
if (memo) return (*memo)(n);
return _factorial(n);
}
/// we're not a function, but a function object
int operator()(int n) {
return _factorial(n);
}
/// the trick holder
std::function<int(int)>* memo;
factorial_class() { memo=0; }
};
int main()
{
TIME_INIT
auto fact=factorial_class(); //virgin wrapper
auto factorial = memoize( (std::function<int(int)>(fact) ) ); //memoize with the virgin wrapper copy
fact.memo=&factorial; //spoilt wrapper
factorial = memoize( (std::function<int(int)>(fact) ) ); //a new memoize with the spoilt wrapper copy
TIME ( std::cout<<"factorial(3)="<<factorial(3)<<std::endl; ) // 3 calculations
TIME ( std::cout<<"factorial(4)="<<factorial(4)<<std::endl; ) // 1 calculation
TIME ( std::cout<<"factorial(6)="<<factorial(6)<<std::endl; ) // 2 calculations
TIME ( std::cout<<"factorial(5)="<<factorial(5)<<std::endl; ) // 0 calculations
TIME ( std::cout<<"factorial(12)="<<factorial(12)<<std::endl; )
TIME ( std::cout<<"factorial(8)="<<factorial(8)<<std::endl; )
return 0;
}