仅针对自然数具有以下算法: rounds(n)= {1,如果n = 1; 1 +轮(ceil(n / 2)),否则} 所以用编程语言写这将是
int rounds(int n){
if(n==1)
return 1;
return 1+rounds(ceil(n/2));
}
我认为这有时间复杂度O(log n)
是否有更好的复杂性?
答案 0 :(得分:1)
如果你认为算法是迭代的并且数字是二进制的,那么这个函数会移出最低位,如果它是一个被移出的1,则将数字增加1。因此,除了增量之外,它计算数字中的位数(即,最高位置1)。增量最终会将结果增加1,除非数字的形式为1000 ....因此,您获得的位数加1,或者如果数字是2的幂,则获得位数。根据您的机器型号,计算速度可能比O(log n)更快。
答案 1 :(得分:1)
首先列出1向上的结果,
rounds(1) = 1
rounds(2) = 1 + rounds(2/2) = 1 + 1 = 2
接下来,当ceil(n/2)
为2时,rounds(n)
将为3.这是n = 3
和n = 4
。
rounds(3) = rounds(4) = 3
然后,当ceil(n/2)
为3或4时,结果为4. 3 <= ceil(n/2) <= 4
当且仅当2*3-1 <= n <= 2*4
发生时,
round(5) = ... = rounds(8) = 4
继续,你可以看到
rounds(n) = k+2 if 2^k < n <= 2^(k+1)
通过归纳。
您可以将其重写为
rounds(n) = 2 + floor(log_2(n-1)) if n > 1 [and rounds(1) = 1]
并且在数学上,您还可以通过将其重写为
来统一处理n = 1
rounds(n) = 1 + floor(log_2(2*n-1))
如果使用固定宽度类型,最后一个公式可能会溢出。
所以问题是
对于固定宽度类型,因此是一个有界范围,所有这些当然是O(1)操作,但是你可能仍然有兴趣使其尽可能高效,即使计算复杂性没有进入游戏。
对于本机类型 - int
和long
通常是 - 比较和减去整数是非常快的机器指令,因此唯一可能存在问题的是基数2对数。
许多处理器都有一个机器指令来计算机器类型值中的前导0位,如果编译器可以访问它,您将获得基本2对数的非常快速的实现。如果没有,您可以使用one of the classic bit-hacks获得比递归更快的版本。
例如,gcc和clang的最新版本具有__builtin_clz
(对于64位类型,分别为__builtin_clzl
),如果该指令存在,则映射到bsr*
指令如果处理器没有提供处理器,那么可能是一个很好的实现,使用一些bit-twiddling。
版本
unsigned rounds(unsigned long n) {
if (n <= 1) return n;
return sizeof n * CHAR_BIT + 1 - __builtin_clzl(n-1);
}
使用bsrq
指令(在我的方框中)0.165秒来计算rounds
1到100,000,000,比特黑客
unsigned rounds(unsigned n) {
if (n <= 1) return n;
--n;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n -= (n >> 1) & 0x55555555;
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
n = (n & 0x0F0F0F0F) + ((n >> 4) & 0x0F0F0F0F);
return ((n * 0x01010101) >> 24)+1;
}
需要0.626秒,而天真循环
unsigned rounds(unsigned n) {
unsigned r = 1;
while(n > 1) {
++r;
n = (n+1)/2;
}
return r;
}
需要1.865秒。
如果你不使用固定宽度类型,但是使用任意精度整数,事情会有所改变。天真循环(或递归)仍然使用Θ(log n)
步骤,但这些步骤平均花费Θ(log n)
时间(或更差),因此整体上您有Θ(log² n)
算法(或更糟)。然后使用上面的公式不仅可以提供具有较低常数因子的实现,而且可以提供具有较低算法复杂度的实现。
O(log n)
是合理表示的最坏情况。O(log n)
才能得到合理的表示。O(log n)
中进行计算[如果它们使用2次幂的基数,则全部任意我熟悉的精密图书馆;如果他们使用10次幂的基数,则会有所不同]。