使用2种欧几里德方法寻找GCD的复杂性

时间:2016-01-05 18:46:18

标签: c algorithm greatest-common-divisor

我试图用两种方法找到两个数字的gcd,一种是减法

    int gcd2(int a, int b)
{
    if (a == 0)
        return b;
    else
        printf("Iteration\n");

    if(a>=b)
    a=a-b;
    else
    b=b-a;

    return gcd2(min(a,b),max(a,b));
}

,另一个是模运算

    int gcd1(int a, int b)
{
    if (a == 0)
        return b;
    else
        printf("Iteration\n");
    return gcd1(b%a, a);
}

我知道gcd2中的迭代次数比gcd1多,但是在gcd1中我使用的是模运算,这也很昂贵所以我想知道这两种方法在运行时方面是否相同。

2 个答案:

答案 0 :(得分:2)

Knuth在“计算机程序设计的艺术”第4.5卷第2卷中详细介绍了gcd。

他的二进制gcd版本更复杂并且使用了这些事实:

a)如果u和v是偶数,gcd(u,v)= 2gcd(u / 2,v / 2)

b)如果你是偶数而v是奇数,gcd(u,v)= gcd(u / 2,v)

c)与Euclid算法一样,gcd(u,v)= gcd(u-v,v)

d)如果u和v都是奇数,那么u-v是偶数和| u-v | < max(u,v)。

对于他的模型计算机MIX,二进制gcd比Euclidean gcd快约20%。您的里程可能会有所不同。

答案 1 :(得分:0)

对于大多数实际用途,使用模运算符的版本应该更快,原因有两个:

(1)基于减法的版本必须更频繁地迭代,从而导致更多的分支误预测

(2)基于模的方法(和二进制版本)不太容易受到Henry提到的性能故障的影响,当一个操作数比另一个操作数大得多时会发生这种情况。

此外,基于模数和基于移位的版本将更多工作转移到处理器内部的专用电路中,这使它们具有更大的优势。二进制版本的代码更复杂;它可以很容易地在接近金属的语言中更快地制作,比如C / C ++或汇编程序,但在远离煤炭面的语言中却不那么容易。一个原因是C / C ++编译器可以通过采用条件移动指令(例如CMOV)或至少允许等效位技巧来避免某些分支;其他语言的编译器往往落后于C / C ++,除非它们共享相同的后端(与gnu编译器一样)。

像python这样的东西完全是一个不同的故事,因为解释器的开销使加法运算,移位运算和mul / div运算之间的指令级时序差异或分支(和分支误预测)的成本相形见绌。

这是对事物的一般看法;总是可以构建可以证明任何版本低于或高于的病态输入。

无论如何,Dijkstra's dictum regarding premature optimisation应该被认真对待。除非有相反的证据,最好的代码是代码,它非常简单明了,很难出错。坚持模数版本,直到你有证据证明它太慢。如果您确实有这样的证据,请回来,我们会加快速度(从那时起将会有具体的细节,可以利用的具体细节)。