2KB项目,专业的源码交易网站 帮助 收藏 每日签到

Ruby 太慢了

  • 时间:2019-01-23 18:49 编辑:2KB 来源:2KB.COM 阅读:423
  • 扫一扫,手机访问
  • 分享
摘要:
Ruby 英文原文:Ruby Is Too Slow for Programming Competitions

上个周末,我参加了Google Code Jam 2013的资格赛。今年是我第三次参加了,也是我使用Ruby作为主要语言的第三个年头。

因为之前没有参加编程竞赛的经验,再加上我工作中使用Ruby,所以我从来没想过选择别的语言。当然,我早就知道Ruby不是那么的快,但我始终认为只要选择了正确的解决方案并且写出高效的代码(内存空间,值存取,限制搜索空间等等),比起处理速度而言,更让人担心的是我是否能快速地写出简洁的代码。

我错了.

2013资格赛的问题C 叫‘Fair and Square’, 简单讲就是:

给定数字X和Y,返回其中包括多少个数字,数字本身是回文数,同时也是另一个回文数的平方。
Given two numbers X and Y, return how many numbers in that range
(inclusive) are palindromes, and also the square of a palindrome.

这个问题看起来很简单明了.很明显,我需要一个函数判断一个给定的数字是否是回文数。所以,我比较了两个函数的性能,一个先转换为字符串再判断是否是回文,一个使用整数计算来判断是否是回文数,因为我听说计算机做整数计算相当快.

Rehearsal -------------------------------------------------------------
Integer palindrome method   7.700000   0.000000   7.700000 (  7.705190)
String palindrome method    4.890000   0.010000   4.900000 (  4.907374)
--------------------------------------------------- total: 12.600000sec

                                user     system      total        real
Integer palindrome method   7.770000   0.000000   7.770000 (  7.773088)
String palindrome method    4.720000   0.010000   4.730000 (  4.726223)

该测试的源码

在过去的编码大赛中,当可以使用整数计算时,如果我用了字符串来处理就会被搞得很受伤。所以这个结果使我很吃惊。但是,毕竟数字不会说谎,而且现在我需要写出高效率的Ruby代码,所以这个测试结果还是很有用的。

很快, 我写了一个Ruby程序测试通过了示例输入,以及小输入集.

def string_palindrome?(num)
  s = num.to_s
  s == s.reverse
end

ARGF.readline
count = 0
ARGF.each do |input|
  count += 1
  found = 0
  start, finish = input.split(" ").map(&:to_i)
  sqrt_start = Math.sqrt(start).to_i
  sqrt_finish = Math.sqrt(finish).to_i
  (sqrt_start..sqrt_finish).each do |x|
    if string_palindrome?(x)
      square = x ** 2
      if string_palindrome?(square) && (start..finish).cover?(square)
        found += 1
      end
    end
  end
  puts "Case ##{count}: #{found}"
end

首先,在检查回文的两个方法中我选择了最快的那个,而且我通过只遍历边界的平方根,大大减少了搜索空间。所以,我指望这个算法很有可能表现良好。

这个问题的第一个大输入集包含了10000个测试用例,每个测试用例使用的范围从 1 到 1014 (文件在这). 编码大赛需要你在8分钟内运行输入并上传正确的结果 - 但我的实现却用了53分钟运行。

这个结果让我很吃惊,让我们仔细看看为什么。测试部分输入的性能测试结果如下:

%self      total      self      wait     child     calls  name
 49.89   1549.912   984.824     0.000   565.087 326068740   Object#string_palindrome?
 21.47   1974.150   423.798     0.000  1550.352      499   Range#each
 16.34    322.536   322.536     0.000     0.000 326069738   Fixnum#to_s
 12.29    242.552   242.552     0.000     0.000 326068740   String#reverse
  0.02      0.409     0.409     0.000     0.000   557447   Fixnum#**

大约50%的执行时间花在string_palindrome?。这个结果不好,但是也还合理。起码我们知道如果使用integer_palindrome??结果会更糟糕。更大的麻烦是Range#each占用了21.47%的执行时间。这意味着全部运行的50分钟里面,10.5分钟花在递增数字。这太不科学了,对吧?经过明智的思考,我决定不使用Range来优化它:

file = File.new(ARGV[0], "r")
file.gets
count = 0
while (input = file.gets)
  start, finish = input.split(" ").map(&:to_i)
  sqrt_start = Math.sqrt(start).to_i
  sqrt_finish = Math.sqrt(finish).to_i
  x = sqrt_start
  while(x <= sqrt_finish) do
    x += 1
  end
end

最终结果是…

real  5m42.952s
user  5m42.787s
sys   0m0.153s

哇. 只遍历我们需要的数字就花了5分半种。简单推断一下,几乎所有的时间花在递增和比较数字,这里应该没有GC,但是Fixnum提升为Bignum可能还是花了些时间.

很明显,我必须做大改动了,所以我用Go语言重写了这个代码(more on that experience at a later date).

package main

import (
  "fmt"
  "math"
)

func palindrome(num int) bool {
  var n, reverse, dig int
  n = num
  reverse = 0
  for (num > 0){
    dig = num % 10
    reverse = reverse * 10 + dig
    num = num / 10
  }
  return n == reverse
}

func main() {
  var cases int
  fmt.Scan(&cases)
  for i := 0; i < cases; i++ {
    var found, start, finish, sqrt_start, sqrt_finish, square int
    fmt.Scan(&start, &finish)
    sqrt_start = int(math.Sqrt(float64(start)))
    sqrt_finish = int(math.Sqrt(float64(finish)))
    for j := sqrt_start; j <= sqrt_finish; j++ {
      if palindrome(j){
        square = j*j
        if palindrome(square) && square >= start && square <= finish {
          found += 1
        }
      }
    }
    fmt.Print("Case #", (i + 1), ": ", found, "
")
  }
}

我没有为了让这个程序通过测试做更多算法上的修改,这个代码和Ruby的算法一样,只是跑起来更快。

real  0m0.740s
user  0m0.482s
sys   0m0.189s

我知道作为解释性语言,Ruby的性能不高,但是只是简单的遍历整数就花了好几分钟还是让我大吃一惊。将来,我肯定不会在时间敏感的编码竞赛中使用Ruby了。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部