快速整数sqrt上限逼近

时间:2017-03-11 06:43:33

标签: nasm approximation sqrt square-root upperbound

这是一个关于我的家庭作业的问题,特别是关于NASM的问题。

我正在编写一种算法来查找数字的最小整数因子。 (大于1)

在伪代码中,它可以概括为:

if(n%2==0)
    return 2;
for(i=3; i <= n/2; i+=2)
    if(n%i==0)
        return i;
return n;

该程序比大数字的要求稍慢。 (n> 1 000 000 000)

最明显的(对我而言)改进是将n/2替换为sqrt(n)。但是我不应该知道如何使用浮点数并且通过牛顿方法找到整数sqrt似乎有点过分。 (因为我实际上并不需要知道确切的值,虽然我还没有对其进行测试,但我会想象以递归/迭代方式找到isqrt会非常慢)

所以我想知道,某些功能是否存在快速算法,例如sqrt(n) < f(n) < n/2。通过&#34;快速&#34;我的意思是最好是恒定时间,f(n) < n/2我的意思是大n显着更少。

我正在考虑的一些选项是:

  • 检查i <= min(sqrt(2^32), n/2),其中sqrt(2^32) = 2^16是常量。

  • i <= n/2替换为i <= (2^p),其中p = ⌈log_2(n)/2⌉或其他内容。 (pn

  • 最高位的一半

3 个答案:

答案 0 :(得分:3)

有一个求平方根的迭代过程:

def approximate_sqrt(number, depth, start_val):
    for i in range(depth):
        start_val=(start_val+number/start_val)/2
    return start_val

初始猜测(start_val)越好,收敛到合理解决方案的速度就越快。

If start_val>sqrt(number) 

then every iterative value>sqrt(number) 

,因此它提供了一个上限(与start_val < sqrt(number)类似)。如果您的初始猜测非常接近,则可以将迭代深度减小为1或2。因此,例如,要反复猜测素数候选项的上限,可以致电

sqrt_appr=approximate_sqrt(i, 1, sqrt_appr+1) 
对于下一个素数候选者

,其先前对sqrt_appr的平方根进行了估计,并获得了上限,误差约为10E-6。 (尽管每次我检查近似值有多近(以300万个数字为间隔,我都设置sqrt_appr=sqrt(number)+1来重置该过程。)

答案 1 :(得分:1)

将i替换为i * i:

 if (n % 2 == 0)
    return 2;
for (int i = 3; i * i <= n; i += 2)
    if (n % i == 0)
        return i;
return n

答案 2 :(得分:0)

最后我确定了⌈log_2(n)/2⌉版本。自sqrt(n) = 2^(log_2(n)/2)起。所以对于有需要的人来说这是我的解决方案。 sqrt(n)上限近似值为O(1)。整个算法是O(sqrt(n))(我认为)。

mov esi,ebx     ;ebx = N
shr esi,1       ;esi = N/2
cmovnc esi,2    ;if no remainder RETURN 2
jnc RETURN
mov edi,2       ;edi is max counter
bsr eax,ebx     ;eax is most significant set bit of ebx (log_2(eax))
shr eax,1       ;eax/=2
mov cl,al
shl edi,cl      ;max counter is 2^(log_2(N)/2 + 1) >= sqrt(N)
mov esi,1       ;default answer is 1
mov ecx,3       ;start counter is 3
START:
mov edx,0
mov eax,ebx
div ecx         ;divide N by counter
test edx,edx    ;if no remainder RETURN counter
cmovz esi,ecx
jz RETURN
add ecx,2       ;else counter += 2
cmp ecx,edi
jb START        ;if counter <= max counter try agian with new divisor
RETURN:
                ;the answer is in (esi)

P.S。如果您想知道我为什么不检查i*i <= N。它实际上比这个版本慢得多。在循环中添加mul不应该减慢那么多,所以我怀疑它在每次迭代时都会破坏分支预测算法。 cmp ecx,edi正在将计数器与常数进行比较,因此可能会在大多数时间进行预测,其中cmp 'ecx*ecx',ebx将比较计数器的平方,这可能对处理器来说不是那么明显