簡體   English   中英

我的代碼在大 O 時間的運行時間

[英]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)=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

這應該看起來像斐波那契數列。 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM