【发布时间】:2021-02-24 17:56:55
【问题描述】:
到目前为止,我想出了三个解决方案:
效率极低的标准库pow 和log2 函数:
int_fast16_t powlog(uint_fast16_t n)
{
return static_cast<uint_fast16_t>(pow(2, floor(log2(n))));
}
更有效地计算 2 的后续幂,直到我达到比我必须达到的更大的数字:
uint_fast16_t multiply(uint_fast16_t n)
{
uint_fast16_t maxpow = 1;
while(2*maxpow <= n)
maxpow *= 2;
return maxpow;
}
迄今为止最有效的对预先计算的 2 幂表进行 bin 搜索:
uint_fast16_t binsearch(uint_fast16_t n)
{
static array<uint_fast16_t, 20> pows {1,2,4,8,16,32,64,128,256,512,
1024,2048,4096,8192,16384,32768,65536,131072,262144,524288};
return *(upper_bound(pows.begin(), pows.end(), n)-1);
}
这可以进一步优化吗?有什么可以在这里使用的技巧吗?
我使用的完整基准:
#include <iostream>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <array>
#include <algorithm>
using namespace std;
using namespace chrono;
uint_fast16_t powlog(uint_fast16_t n)
{
return static_cast<uint_fast16_t>(pow(2, floor(log2(n))));
}
uint_fast16_t multiply(uint_fast16_t n)
{
uint_fast16_t maxpow = 1;
while(2*maxpow <= n)
maxpow *= 2;
return maxpow;
}
uint_fast16_t binsearch(uint_fast16_t n)
{
static array<uint_fast16_t, 20> pows {1,2,4,8,16,32,64,128,256,512,
1024,2048,4096,8192,16384,32768,65536,131072,262144,524288};
return *(upper_bound(pows.begin(), pows.end(), n)-1);
}
high_resolution_clock::duration test(uint_fast16_t(powfunct)(uint_fast16_t))
{
auto tbegin = high_resolution_clock::now();
volatile uint_fast16_t sink;
for(uint_fast8_t i = 0; i < UINT8_MAX; ++i)
for(uint_fast16_t n = 1; n <= 999999; ++n)
sink = powfunct(n);
auto tend = high_resolution_clock::now();
return tend - tbegin;
}
int main()
{
cout << "Pow and log took " << duration_cast<milliseconds>(test(powlog)).count() << " milliseconds." << endl;
cout << "Multiplying by 2 took " << duration_cast<milliseconds>(test(multiply)).count() << " milliseconds." << endl;
cout << "Binsearching precomputed table of powers took " << duration_cast<milliseconds>(test(binsearch)).count() << " milliseconds." << endl;
}
使用-O2 编译,在我的笔记本电脑上得到以下结果:
Pow and log took 19294 milliseconds.
Multiplying by 2 took 2756 milliseconds.
Binsearching precomputed table of powers took 2278 milliseconds.
【问题讨论】:
-
您需要什么类型的二的幂?像
2**3这样的整数或像2**(1/2)或2**0.1这样的任何(非)有理数?生成 2 的 整体 次方的最有效方法是位移,因此,您实际上不需要pow(2, integer),您可以改为使用2 << integer。 -
我认为您的基准由于分支预测而被打破。您应该为
n使用随机值。 -
您的意思是问“如何有效地找到 ...”(而不是“计数”)?
-
你知道表的最后一个元素超出了 16 位整数的范围吧?
标签: c++ optimization