6

我有这两个 gcd 函数的实现:

def gcd1(a,b)
  if a==b
    a
  elsif a>b
    if (a%b)==0
      b
    else
      gcd1(a%b,b)
    end
  else
    if (b%a)==0
      a
    else
      gcd1(a,b%a)
    end
  end
end
def gcd2(a,b)
  if(a==b)
    return a
  elsif b>a
    min,max=a,b
  else
    min,max=b,a
  end
  while (max%min)!=0
    min,max=max%min,min
  end
  min
end

函数 gcd1 是尾递归的,而 gcd2 使用 while 循环。

我已经验证了 rubinius 通过对阶乘函数进行基准测试来实现 TCO,只有阶乘函数的基准显示递归版本和迭代版本是“相同的”(我使用了 benchmark-ips)。

但是对于上述情况,基准测试表明 gcd1 至少比 gcd2 快两倍(递归比迭代快两倍,甚至更快)。

我用来基准测试的代码是这样的:

Benchmark.ips do |x|
  x.report "gcd1 tail recursive" do
    gcd1(12016,18016)
  end
  x.report "gcd2 while loop" do
    gcd2(12016,18016)
  end
  x.compare!
end

结果 :

Warming up --------------------------------------
 gcd1 tail recursive    47.720k i/100ms
     gcd2 while loop    23.118k i/100ms
Calculating -------------------------------------
 gcd1 tail recursive    874.210k (± 7.1%) i/s -      4.343M
     gcd2 while loop    299.676k (± 6.6%) i/s -      1.503M

Comparison:
 gcd1 tail recursive:   874209.8 i/s
     gcd2 while loop:   299676.2 i/s - 2.92x slower

我正在运行 Arch linux x64,处理器 i5-5200 2.2 GHZ 四核。

红宝石实现是 Rubinius 3.40 。

那么递归怎么能比循环快呢?

更新

只是说斐波那契有相同的情况:尾递归版本至少是循环版本的两倍,我用于斐波那契的程序:http: //pastebin.com/C8ZFB0FR

4

1 回答 1

2

在您使用的示例中,它只需要 3 次调用/循环即可获得答案,所以我认为您实际上并没有测试正确的东西。尝试使用两个连续的斐波那契数(例如第 2000 和第 2001),结果应该不会有太大差异。

(对不起,我还没有发表评论的声誉)。

编辑:我终于设法安装 [a part of] rubinius 并设法重新创建您所指的现象。这不是关于递归,而是关于多重赋值。如果你改变

      while n>0
        a,b=b,a+b
        n-=1
      end

      while n>0
        t=a
        a=b
        b=t+b
        n-=1
      end

while 循环版本应该(有点)更快地执行。同样适用于原来的 GCD 程序,即替换

        min,max=max%min,min

        t=min
        min=max%t
        max=t

是事情。

这不是 ruby​​-2.1 的情况,即 while 循环似乎更快,只是在您提供的形式。

希望有帮助!

于 2016-06-24T03:00:11.717 回答