我的代码在大O时间运行的时间

时间:2017-10-13 19:28:12

标签: c++ performance

所以我做了一些练习问题,既可以更好地理解代码,又可以练习一些即将进行的采访,但是我无法理解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如何使用递归的一个很好的解释我会很感激。此外,如果您对更有效的解决方案有任何想法,我会欣赏任何类型的提示,因为我也在努力改进它。

2 个答案:

答案 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)=1T(-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