Rabin Karp在Ruby中实现得太慢了

Nit*_*eti 2 ruby algorithm plagiarism-detection rabin-karp

我一直在研究一个小型的抄袭检测引擎,该引擎使用MOSS的 Idea .我需要一个滚动哈希函数,我受到Rabin-Karp算法的启发.

我写的代码 - >

#!/usr/bin/env ruby
#Designing a rolling hash function.
#Inspired from the Rabin-Karp Algorithm

module Myth
  module Hasher

    #Defining a Hash Structure
    #A hash is a integer value + line number where the word for this hash existed in the source file
    Struct.new('Hash',:value,:line_number)

    #For hashing a piece of text we ned two sets of parameters
    #k-->For buildinf units of k grams hashes  
    #q-->Prime which lets calculations stay within range
    def calc_hash(text_to_process,k,q)

      text_length=text_to_process.length
      radix=26

      highorder=(radix**(text_length-1))%q

      #Individual hashes for k-grams
      text_hash=0

      #The entire k-grams hashes list for the document
      text_hash_string=""

      #Preprocessing
      for c in 0...k do
        text_hash=(radix*text_hash+text_to_process[c].ord)%q
      end

      text_hash_string << text_hash.to_s << " "

      loop=text_length-k

      for c in 0..loop do        
        puts text_hash
        text_hash=(radix*(text_hash-text_to_process[c].ord*highorder)+(text_hash[c+k].ord))%q
        text_hash_string << text_hash_string << " "
      end
    end

  end
end
Run Code Online (Sandbox Code Playgroud)

我正在运行它的值 - > calc_hash(text,5,101)其中text是一个String输入.

代码很慢.我哪里错了?

the*_*Man 6

看看Ruby-Prof,一个Ruby的分析器.使用gem install ruby-prof安装它.

一旦你有一些代码滞后的想法,你可以使用Ruby的Benchmark尝试不同的算法来找到最快的.

在StackOverflow上徘徊,你会看到很多地方,我们将使用Benchmark来测试各种方法,看看哪个是最快的.您还将了解设置测试的不同方法.


例如,查看您的代码,我不确定附加是否<<比使用+或使用字符串插值更好.这是测试它的代码和结果:

require 'benchmark'
include Benchmark

n = 1_000_000
bm(13) do |x|
  x.report("interpolate") { n.times { foo = "foo"; bar = "bar"; "#{foo}#{bar}" } }
  x.report("concatenate") { n.times { foo = "foo"; bar = "bar"; foo + bar      } }
  x.report("append")      { n.times { foo = "foo"; bar = "bar"; foo << bar     } }
end

ruby test.rb; ruby test.rb
                   user     system      total        real
interpolate    1.090000   0.000000   1.090000 (  1.093071)
concatenate    0.860000   0.010000   0.870000 (  0.865982)
append         0.750000   0.000000   0.750000 (  0.753016)
                   user     system      total        real
interpolate    1.080000   0.000000   1.080000 (  1.085537)
concatenate    0.870000   0.000000   0.870000 (  0.864697)
append         0.750000   0.000000   0.750000 (  0.750866)
Run Code Online (Sandbox Code Playgroud)

我想知道根据@ Myth17的评论在下面添加字符串时使用固定与变量的效果:

require 'benchmark'
include Benchmark

n = 1_000_000
bm(13) do |x|
  x.report("interpolate") { n.times { foo = "foo"; bar = "bar"; "#{foo}#{bar}" } }
  x.report("concatenate") { n.times { foo = "foo"; bar = "bar"; foo + bar      } }
  x.report("append")      { n.times { foo = "foo"; bar = "bar"; foo << bar     } }
  x.report("append2")     { n.times { foo = "foo"; bar = "bar"; "foo" << bar   } }
  x.report("append3")     { n.times { foo = "foo"; bar = "bar"; "foo" << "bar" } }
end
Run Code Online (Sandbox Code Playgroud)

导致:

ruby test.rb;ruby test.rb

                   user     system      total        real
interpolate    1.330000   0.000000   1.330000 (  1.326833)
concatenate    1.080000   0.000000   1.080000 (  1.084989)
append         0.940000   0.010000   0.950000 (  0.937635)
append2        1.160000   0.000000   1.160000 (  1.165974)
append3        1.400000   0.000000   1.400000 (  1.397616)

                   user     system      total        real
interpolate    1.320000   0.000000   1.320000 (  1.325286)
concatenate    1.100000   0.000000   1.100000 (  1.090585)
append         0.930000   0.000000   0.930000 (  0.936956)
append2        1.160000   0.000000   1.160000 (  1.157424)
append3        1.390000   0.000000   1.390000 (  1.392742)
Run Code Online (Sandbox Code Playgroud)

这些值与我之前的测试不同,因为代码正在我的笔记本电脑上运行.

附加两个变量比涉及固定字符串时更快,因为存在开销; Ruby必须创建一个中间变量然后附加到它.

这里的一个重要教训是,当我们编写代码时,我们可以做出更明智的决定,因为我们知道什么运行得更快.同时,差异不是很大,因为大多数代码都没有运行1,000,000个循环.您的里程可能有所不同