C++ 如何快速计算任意基的整数对数?

C++ 如何快速计算任意基的整数对数?,c++,math,c++17,base,logarithm,C++,Math,C++17,Base,Logarithm,如何快速计算任意基数的整数对数,而不仅仅是基数10?对于基数10有一个非常有效的解决方案,但我想了解如何将其推广到其他基数。基本原理 可以使用log(n)/log(b)计算任意数nlog_b(n)的对数基数b,其中log是任何基数的对数(通常是自然对数)log(b)是一个常数,因此如果我们能有效地计算某个基数的对数,我们就能有效地计算任意基数的对数 不幸的是,这种转换只有在我们不删除数字的情况下才可能实现。对于整数,我们只能快速地计算落地对数。例如,log_2(10)=3。这将是8到15之间任何

如何快速计算任意基数的整数对数,而不仅仅是基数10?对于基数10有一个非常有效的解决方案,但我想了解如何将其推广到其他基数。

基本原理 可以使用
log(n)/log(b)
计算任意数
n
log_b(n)
的对数基数
b
,其中
log
是任何基数的对数(通常是自然对数)
log(b)
是一个常数,因此如果我们能有效地计算某个基数的对数,我们就能有效地计算任意基数的对数

不幸的是,这种转换只有在我们不删除数字的情况下才可能实现。对于整数,我们只能快速地计算落地对数。例如,
log_2(10)=3
。这将是8到15之间任何数字的结果,尽管这些数字的小数位数不同。所以这个二元对数可以帮助我们做出一个很好的猜测,但是我们需要改进这个猜测

基数10 上述问题有以下解决办法:

constexpr无符号log2floor(uint64\u t x){
//使用clang或gcc实现C++17
返回x?63-uuu内置clzll(x):0;
//使用新的C++20头实现
返回x?63-std::countl_zero(x):0;
}
constexpr无符号log10floor(无符号x){
constexpr无符号字符猜测[32]={
0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 6, 7, 7, 7, 8, 8, 8,
9, 9
};
constexpr uint64_t powers[11]={
1, 10, 100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000, 10000000000
};
无符号猜测=猜测[log2floor(x)];
返回guess+(x>=幂[guess+1]);
}
注意,我不得不做一些修改,因为解决方案实际上不是100%正确的

正如问题中所解释的,我们根据二进制对数进行猜测,这可以非常有效地计算,然后在必要时增加猜测

可使用以下公式计算猜测表:

index->log_10(exp(2,index))=log_10(1=10
,因此我们的猜测太低,我们返回
guess+1=0+1=1
任意基的推广 要将此方法推广到任何基,我们必须在
constexpr
上下文中计算查找表。要计算猜测表,我们首先需要对任何基使用简单对数实现:

模板
constexpr Uint logFloor_naive(Uint val,无符号基){
Uint结果=0;
while(val/=base){
++结果;
}
返回结果;
}
现在,我们可以计算查找表:

#包括
#包括
模板
constexpr std::数组makeGuessTable()
{
decltype(makeGuessTable())结果{};
对于(size_t i=0;i=幂[guess]);
}
}
}
powers
查找表上的注释 我们总是使用
uint64\u t
作为
powers
表的原因是,我们访问
guess+1
exp(10,guess+1)
并不总是可表示的。例如,如果我们使用8位整数,并且猜测
2
,那么
exp(10,guess+1)
将是
1000
,不能用8位整数表示

通常,这会导致64位整数出现问题,因为没有更大的整数类型可用。但也有例外。例如,可表示的最大幂2,
exp(2,63)
低于
exp(10,19)
,它是10的最大可表示幂。这意味着最高猜测将是
18
,并且
exp(10,guess+1)=exp(10,19)
是可表示的。因此,我们总是可以安全地访问
幂[guess+1]

这些异常非常有用,因为在这种情况下,我们可以避免整数除法。如上所述,可以通过以下方法检测此类异常:

guesss.back()+2
您也可以使用(C++20)中的函数,而不是
\uuuu builtin\u clzll
,或者。在MSVC中,您可以使用类似的内置函数(假设您的cpu支持LZCNT指令)