所以我做了一些练习问题,既可以更好地理解代码,又可以练习一些即将进行的采访,但是我无法理解Big O中的运行时间。问题是:
你有一个N级台阶,你可以采取单步和双步的混合物到达顶部。你有多少种不同的方式爬楼梯?
我对问题做了一个简单的回答
//When the function is first called, I set the second argument, x, to 0
int count_steps(int n, int x){
if(x==n){
return 1;
}
else if(x>n){
return 0;
}
return count_steps(n, x+2) + count_steps(n,x+1);
}
如果有人可以给我一个答案,或者对大O如何使用递归的一个很好的解释我会很感激。此外,如果您对更有效的解决方案有任何想法,我会欣赏任何类型的提示,因为我也在努力改进它。
答案 0 :(得分:1)
让我回答两个部分。
<强> 1。 OP原始count_steps
功能的运行时间是什么?
<强> 2。如何改善运行时间?
由于您总是在X = 0时调用,因此将函数重写为:
int count_steps(int n){
if(n==0){
return 1;
}
else if(n < 0){
return 0;
}
return count_steps(n-2) + count_steps(n-1);
}
让T(n)
为给定值n
的count_steps的返回值:
T(n) = T(n-1) + T(n-2)
其中T(0)=1
和T(-1)=0
。
解决重复T(n)-T(n-1)-T(n-2) = 0
。特征多项式x^2-x-1=0
的根是(1 + sqrt(5))/ 2和(1-sqrt(5))/ 2
这应该看起来像Fibonacci序列。
https://en.wikipedia.org/wiki/Fibonacci_number。事实上它有一个封闭的形式解决方案。 T(n)=O(Phi^N)
其中Phi=(1+sqrt(5))/2
是黄金比率大约等于1.618。
请注意,最初编写的函数count_steps
的运行时间与其递归的次数成正比。 (函数中的其他所有内容都在恒定的时间内运行)。 因此,最初编写的的运行时间为O(T(n)) = O(Phi^n)
。
如何改进?另一个答案显示线性时间解决方案 - 这要好得多。但由于复发的封闭形式解决方案(与找到第N个斐波纳契数相关),可以将函数改进为O(1)。
答案 1 :(得分:0)
回忆可以带来巨大的变化:
#include <tuple>
#include <unordered_map>
#include <iostream>
#include <boost/functional/hash.hpp>
#include <chrono>
long long count_steps(long long n){
if(n==0){
return 1;
}
else if(n < 0){
return 0;
}
return count_steps(n-2) + count_steps(n-1);
}
struct count_steps_algo
{
using args_type = std::tuple<long long>;
using result_type = long long;
template<class Memo, class N>
result_type operator()(Memo& memo, N&& n)
{
if(n==0){
return 1;
}
else if(n < 0){
return 0;
}
return memo(n-2) + memo(n-1);
}
};
template<class Algo>
struct memoised
{
using algo_type = Algo;
using args_type = typename algo_type::args_type;
using result_type = typename algo_type::result_type;
using memo_map_type =
std::unordered_map
<
args_type,
result_type,
boost::hash<args_type>
>;
template<class...Args>
decltype(auto) operator()(Args&&...args)
{
auto i = memo_map_.find(std::tie(args...));
if (i != memo_map_.end())
{
return i->second;
}
auto result = algo_(*this, args...);
memo_map_.emplace(std::tie(args...), result);
return result;
}
Algo algo_;
memo_map_type memo_map_;
};
int main()
{
constexpr int N = 45;
using clock = std::chrono::system_clock;
auto cs = memoised<count_steps_algo>();
auto start = clock::now();
std::cout << "start" << std::endl;
auto memo_result = cs(N);
std::cout << "stop" << std::endl; // compiler optimisation re-orders this on clang!!!
auto stop = clock::now();
auto secs = std::chrono::duration<double, std::ratio<1>>(stop - start);
std::cout << "memoised : " << memo_result << " took "
<< std::fixed << secs.count() << "s\n";
auto start2 = clock::now(); // compiler optimisation re-orders this on clang!!!
std::cout << "start" << std::endl;
auto raw_result = count_steps(N);
std::cout << "stop" << std::endl; // compiler optimisation re-orders this on clang!!!
auto stop2 = clock::now();
auto secs2 = std::chrono::duration<double, std::ratio<1>>(stop2 - start2);
std::cout << "bruteforce: " << raw_result << " took "
<< secs2.count() << "s\n";
}
示例输出:
start
stop
memoised : 1836311903 took 0.000082s
start
stop
bruteforce: 1836311903 took 11.026068s