[英]The running time of my code in big O time
所以我正在做一些練習題,以更好地理解代碼,以及為一些即將到來的面試做練習,但我在理解 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);
}
如果有人能給我一個答案,或者對 Big O 如何處理遞歸的一個很好的解釋,我將不勝感激。 此外,如果您對更有效的解決方案有任何想法,我將不勝感激,因為我也在努力改進它。
讓我回答兩個部分。
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);
}
對於給定的n
值,讓T(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
這應該看起來像斐波那契數列。 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)。
記憶可以產生巨大的不同:
#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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.