算法复杂度与执行时间

2021-01-23
2 min read

实际测试

通过测试(代码见下文,编译指令:g++ -std=c++11 -O0 t.cpp)当代计算机耗时如下表所示,测试机器:

  • Ubuntu 20
  • CPU,intel i7-8750H
  • Mem,16G & DDR4 & 2400MT/s

测试结果

执行次数 优化选项 耗时(ms)
14000(数万) -O0 0.771(毫秒级)
14000 -O2 0.613
4000000(数百万) -O0 35.249(数十毫秒)
4000000 -O2 29.859
28000000(数千万) -O0 240.723(数百毫秒)
28000000 -O2 197.916
196000000(数亿) -O0 1686.2(秒级)
196000000 -O2 1410.34
8000000000(数十亿) -O0 68834(数十秒级)
8000000000 -O2 56567.1

总结

不同机器执行耗时可能不同,比如嵌入式CPU对比服务器机器可能慢了几十倍,但实验结果所表现出的现象不变

竞赛时可以按照数据规模选择算法,如果数据量不大暴力法是很好的选择,简单快速

算法复杂度不能超过亿次级别,否则很容易超时,数十亿一定超时,千万级别比较保险,最好是百万级别及以下

测试代码

#include <cmath>
#include <chrono>
#include <iostream>

double cnt_g = 0;

void base_f() {
    cnt_g += 3.1415926 * (std::rand() % 7);
}

long long liner_f(long long cnt) {
   for(int i = 0; i < cnt; i++) base_f(); 
   return cnt;
}

long long logn_f(long long cnt) {

    int cnt_x = static_cast<long long>(std::log(cnt*1.0));
    return liner_f(cnt_x);    
}

long long nlogn_f(long long cnt) {
    long long exe_cnt = 0;
    for(int i = 0; i < cnt; i++) {
        exe_cnt += logn_f(cnt);
    }
    return exe_cnt;
}

long long pow2_f(long long cnt){
    long long exe_cnt = 0;
    for(int i = 0; i < cnt; i++){
        exe_cnt += liner_f(cnt);
    }
    return exe_cnt;
}

long long pow2logn_f(long long cnt){
    long long exe_cnt = 0;
    auto cnt_x = static_cast<long long>(std::log(cnt*1.0));
    for(int i = 0; i < cnt_x; i++){
        exe_cnt += pow2_f(cnt);
    }
    return exe_cnt;
}

long long pow2lognlogn_f(long long cnt){
    long long exe_cnt = 0;
    auto cnt_x = static_cast<long long>(std::log(cnt*1.0));
    for(int i = 0; i < cnt_x; i++){
        exe_cnt += pow2logn_f(cnt);
    }
    return exe_cnt;
}

long long cube_f(long long cnt) {
    long long exe_cnt = 0;
    for(int i = 0; i < cnt; i++){
        exe_cnt += pow2_f(cnt);
    }
    return exe_cnt;
}


std::pair<long long, long long > time_stat(long long (*f)(long long ), long long cnt) {

    auto res = std::pair<long long, long long>();
    auto start_tp = std::chrono::steady_clock::now();

    res.first = f(cnt);

    auto end_tp = std::chrono::steady_clock::now();
    auto span = std::chrono::duration_cast<std::chrono::microseconds>(end_tp - start_tp).count();

    res.second = span;

    return res;
}

void helper(const std::string& msg, long long (*f)(long long), long long cnt) {

    auto res = time_stat(f, cnt);
    std::cout << msg << ",  cnt: " << cnt << ", exe_cnt: " << res.first 
              << ", time: " << res.second/1000.0 << "ms, cnt_g: " << cnt_g << std::endl;
}

int main(int argc, char** argv) {

    if(argc != 2) {
        std::cerr << "ERR, please do: ./exe_able cnt" << std::endl;
        return -1;
    }
    else {
        long long cnt = std::stoll(argv[1]);

        helper("logn", logn_f, cnt);
        helper("n", liner_f, cnt);
        helper("nlogn", nlogn_f, cnt);
        helper("pow2", pow2_f, cnt);
        helper("pow2logn", pow2logn_f, cnt);
        helper("pow2lognlogn", pow2lognlogn_f, cnt);
        helper("cube", cube_f, cnt);
    }
}

输出示例

编译命令:g++ -std=c++11 -O2 test.cpp

执行命令:./a.out 2000

控制台输出:

logn,  cnt: 2000, exe_cnt: 7, time: 0.034ms, cnt_g: 59.6903
n,  cnt: 2000, exe_cnt: 2000, time: 0.074ms, cnt_g: 18959.5
nlogn,  cnt: 2000, exe_cnt: 14000, time: 0.588ms, cnt_g: 151833
pow2,  cnt: 2000, exe_cnt: 4000000, time: 29.557ms, cnt_g: 3.78582e+07
pow2logn,  cnt: 2000, exe_cnt: 28000000, time: 201.471ms, cnt_g: 3.01775e+08
pow2lognlogn,  cnt: 2000, exe_cnt: 196000000, time: 1409.63ms, cnt_g: 2.14893e+09
cube,  cnt: 2000, exe_cnt: 8000000000, time: 57545.9ms, cnt_g: 7.7547e+10