• 周日. 5月 26th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

Fibonacci各种玩法

admin

11月 28, 2021

1-基础入门玩法

auto fib(auto n){
    if (n == 0)  return 0;
    if (n == 1) return 1;
    return fib(n-1) + fib(n-2);
}

2-metaprogramming,编译期间展开

template<auto N>
struct FibT;

template<>
struct FibT<0>{
    static constexpr int result = 0;
};

template<>
struct FibT<1>{
    static constexpr int result = 1;
};

template<auto N>
struct FibT{
    static constexpr int result = FibT<N-1>::result + FibT<N-2>::result;
};
template<auto N>
inline constexpr auto fib_v = FibT<N>::result;
std::cout << fib_v<3> << std::endl;

3,metaprogramming,同样编译期间计算展开

template<auto N>
struct FibT{
    static auto constexpr compute(){
        auto result = 0;
        if constexpr (N==0) result = 0;
        else if constexpr (N==1) result = 1;
        else
            result = FibT<N-1>::compute() + FibT<N-2>::compute();
        return result;
    }
};
template<auto N>
inline constexpr auto fib_v = FibT<N>::compute();
std::cout << fib_v<3> << std::endl;

4,运行期间展开的有个问题,由于是递归,会再递归的 node上重复计算

比如fib(5),第一次递归,fib(4),fib(3),而计算fib(4)也计算了fib(3)

#include <iostream>
#include <vector>

static std::vector<int > cache{0,1};

auto fib(auto n){
    if(cache.size() > n){
        return cache[n];
    }
    else{
        auto var = fib(n-1) + fib(n-2);
        cache.emplace_back(var);
        return var;

    }
}


int main() {
    std::cout << fib(5) << std::endl;
    std::cout << fib(4) << std::endl;// cached

    std::cout << "container:" << std::endl;
    for(auto &&v : cache){
        std::cout << v << "   " ;
    }
    std::cout << std::endl;
    return 0;
}

 输出:

5
3
container:
0   1   1   2   3   5   

5,对任意函数的结果cache

template<typename ResultT, typename ... Args>
auto make_memoized(ResultT (*func)(Args...))
{
    using TupleArgsT = std::tuple< Args...>;
    std::map< TupleArgsT, ResultT> cache;// unordered map can not use in this place

    return [func, cache](Args... args) mutable -> ResultT
    {
        auto args_tuple= std::make_tuple( args...);
        auto iter = cache.find(args_tuple);
        if(iter == cache.end()){
            auto result = func( args...);
            cache[args_tuple] = result;
            return result;
        }
        else{
            return cache[args_tuple];
        }
        return ResultT();
    };
}

调用方法:

auto fib_mem = make_memoized(fib);
fib_mem(fib_n);
fib_mem(fib_n) ;
fib_mem(fib_n) ;

第一次会慢点,但是第二次调用会非常快,但是这个也有缺陷,

— 函数结果被cache,函数本身的过程没cache

— 适合调用相同的函数多次

 对 N=40 性能的测试:

#include <iostream>
#include <unordered_map>
#include <tuple>
#include <functional>
#include <map>
#include <chrono>

class Timer{
private:
    std::chrono::steady_clock::time_point last;
public:
    Timer(): last{std::chrono::steady_clock::now()}{

    }
    void reset(){
        last = std::chrono::steady_clock::now();
    }
    void printDiff(const std::string &msg = "time diff: "){
        auto now{std::chrono::steady_clock::now()};
        std::chrono::duration<double, std::milli> diff{now-last};
        std::cout << msg << diff.count() << std::endl;
        last = std::chrono::steady_clock::now();
    }
};



int fib(int n){
    if(n==0 || n==1) return n;
    else
        return fib(n-1)+fib(n-2);
}


template<typename ResultT, typename ... Args>
auto make_memoized(ResultT (*func)(Args...))
{
    using TupleArgsT = std::tuple< Args...>;
    std::map< TupleArgsT, ResultT> cache;// unordered map can not use in this place

    return [func, cache](Args... args) mutable -> ResultT
    {
        auto args_tuple= std::make_tuple( args...);

        auto iter = cache.find(args_tuple);

        if(iter == cache.end()){
            auto result = func( args...);
            cache[args_tuple] = result;
            return result;
        }
        else{
            return cache[args_tuple];
        }
        return ResultT();
    };
}



template<auto N>
struct FibT;

template<>
struct FibT<0>{
    static constexpr int result = 0;
};

template<>
struct FibT<1>{
    static constexpr int result = 1;
};

template<auto N>
struct FibT{
    static constexpr int result = FibT<N-1>::result + FibT<N-2>::result;
};
template<auto N>
inline constexpr auto fib_v = FibT<N>::result;



int main() {
    constexpr auto fib_n = 40;
    Timer t;
    fib(fib_n) ;
    fib(fib_n) ;
    fib(fib_n) ;
    t.printDiff("fib() time:   ");
    t.reset();

    auto fib_mem = make_memoized(fib);
    fib_mem(fib_n);
    fib_mem(fib_n) ;
    fib_mem(fib_n) ;
    t.printDiff("fib_mem() time:   ");
    t.reset();

    fib_v<fib_n>;
    fib_v<fib_n>;
    fib_v<fib_n>;
    t.printDiff("metaprogramming time:    ");
    t.reset();

    
    return 0;
}

View Code

上面代码是对每种函数调用3次结果:

fib() time: 3202.85
fib_mem() time: 1066.87
metaprogramming time: 0.000604

如果调用一次:

fib() time: 1056.4
fib_mem() time: 1056.15
metaprogramming time: 0.000681

编译器的递归也有限制,比如将fib_n = 100时候:

Scanning dependencies of target fib_mem
[ 50%] Building CXX object CMakeFiles/fib_mem.dir/main.cpp.o
/home/father/dev/cpp/fib_mem/main.cpp: In instantiation of ‘constexpr const int FibT<47>::result’:
/home/father/dev/cpp/fib_mem/main.cpp:76:46:   recursively required from ‘constexpr const int FibT<99>::result’
/home/father/dev/cpp/fib_mem/main.cpp:76:46:   required from ‘constexpr const int FibT<100>::result’
/home/father/dev/cpp/fib_mem/main.cpp:79:40:   required from ‘constexpr const auto fib_v<100>/home/father/dev/cpp/fib_mem/main.cpp:101:5:   required from here
/home/father/dev/cpp/fib_mem/main.cpp:76:53: warning: integer overflow in expression of type ‘int’ results in ‘-1323752223’ [-Woverflow]
   76 |     static constexpr int result = FibT<N-1>::result + FibT<N-2>::result;
      |                                              ~~~~~~~^~~~~~~~~~~~~~~~~~~
/home/father/dev/cpp/fib_mem/main.cpp:76:26: error: overflow in constant expression [-fpermissive]
   76 |     static constexpr int result = FibT<N-1>::result + FibT<N-2>::result;
      |                          ^~~~~~
/home/father/dev/cpp/fib_mem/main.cpp:76:26: error: overflow in constant expression [-fpermissive]
make[2]: *** [CMakeFiles/fib_mem.dir/build.make:72: CMakeFiles/fib_mem.dir/main.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/fib_mem.dir/all] Error 2
make: *** [Makefile:91: all] Error 2

REF:

Functional Programming In C++ — Ivan

《Fibonacci各种玩法》有一个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注