2011-06-18 92 views
11

在ruby中,計算兩個無符號整數(例如漢明距離)之間的位差的最有效方法是什麼?計算紅寶石漢明距離最有效的方法是什麼?

例如,我有一個整數= 2323409845和b = 178264714​​4.

它們的二進制表示是:

a = 10001010011111000110101110110101 
b = 01101010010000010000100101101000 

之間的一個& b爲17比特差..

我可以對它們進行邏輯異或,但是這會給我一個不同的整數!= 17,然後我必須迭代結果的二進制表示,並計算1的#號。

什麼是計算位差的最有效方法?

現在,爲了計算多個整數序列的位差,答案是否改變?例如。給出2個無符號整數序列:

x = {2323409845,641760420,509499086....} 
y = {uint,uint,uint...} 

什麼是計算兩個序列之間的位差的最有效的方法?

你會遍歷序列,還是有更快的方法來計算整個序列的差異一次?

+0

謝謝!我只是這樣做了,它似乎比下面的方法快了3倍(使用Ruby的優化字符串函數) – ch3rryc0ke

+0

我對這個派對很遲,但你可能想要[這個popcount基準](http:// dalkescientific。 com/writings/diary/popcnt.cpp)。 '__builtin_popcount'是最慢的方法之一,如果你不[使用編譯標誌](http://www.dalkescientific.com/writings/diary/archive/2011/11/02/faster_popcount_update.html) – x1a4

回答

19

您可以使用Ruby中優化字符串函數做位的計數,而不是純粹的算術。事實證明,通過一些快速基準測試,速度提高了大約6倍。

def h2(a, b) 
    (a^b).to_s(2).count("1") 
end 

H1是計算正常方式,而H2轉換XOR成一個字符串,並計算的數字 「1」

基準:

ruby-1.9.2-p180:001:0>> def h1(a, b) 
ruby-1.9.2-p180:002:1*> ret = 0 
ruby-1.9.2-p180:003:1*> xor = a^b 
ruby-1.9.2-p180:004:1*> until xor == 0 
ruby-1.9.2-p180:005:2*> ret += 1 
ruby-1.9.2-p180:006:2*> xor &= xor - 1 
ruby-1.9.2-p180:007:2*> end 
ruby-1.9.2-p180:008:1*> ret 
ruby-1.9.2-p180:009:1*> end 
# => nil 
ruby-1.9.2-p180:010:0>> def h2(a, b) 
ruby-1.9.2-p180:011:1*> (a^b).to_s(2).count("1") 
ruby-1.9.2-p180:012:1*> end 
# => nil 
ruby-1.9.2-p180:013:0>> h1(2323409845, 1782647144) 
# => 17 
ruby-1.9.2-p180:014:0>> h2(2323409845, 1782647144) 
# => 17 
ruby-1.9.2-p180:015:0>> quickbench(10**5) { h1(2323409845, 1782647144) } 
Rehearsal ------------------------------------ 
    2.060000 0.000000 2.060000 ( 1.944690) 
--------------------------- total: 2.060000sec 

     user  system  total  real 
    1.990000 0.000000 1.990000 ( 1.958056) 
# => nil 
ruby-1.9.2-p180:016:0>> quickbench(10**5) { h2(2323409845, 1782647144) } 
Rehearsal ------------------------------------ 
    0.340000 0.000000 0.340000 ( 0.333673) 
--------------------------- total: 0.340000sec 

     user  system  total  real 
    0.320000 0.000000 0.320000 ( 0.326854) 
# => nil 
ruby-1.9.2-p180:017:0>> 
+0

謝謝噸,我發現這也快得多。按照您的建議,使用內置的字符串函數進行大約21K的比較需要大約3秒鐘,而傳統方式需要花費兩倍的時間 – ch3rryc0ke

3

韋格納的算法:

def hamm_dist(a, b) 
    dist = 0 
    val = a^b 

    while not val.zero? 
    dist += 1 
    val &= val - 1 
    end 
    dist 
end 

p hamm_dist(2323409845, 1782647144) # => 17 
5

%的建議mu太短,我寫了一個簡單的C擴展名使用__builtin_popcount,並使用基準驗證它至少比ruby的優化字符串函數快3倍..

我看着以下兩個教程:

在我的程序:

require './FastPopcount/fastpopcount.so' 
include FastPopcount 

def hamming(a,b) 
    popcount(a^b) 
end 

然後在目錄包含我的計劃,我創建一個文件夾 「PopCount」 具有以下文件。

extconf.rb:

# Loads mkmf which is used to make makefiles for Ruby extensions 
require 'mkmf' 

# Give it a name 
extension_name = 'fastpopcount' 

# The destination 
dir_config(extension_name) 

# Do the work 
create_makefile(extension_name) 

popcount。C:

// Include the Ruby headers and goodies 
#include "ruby.h" 

// Defining a space for information and references about the module to be stored internally 
VALUE FastPopcount = Qnil; 

// Prototype for the initialization method - Ruby calls this, not you 
void Init_fastpopcount(); 

// Prototype for our method 'popcount' - methods are prefixed by 'method_' here 
VALUE method_popcount(int argc, VALUE *argv, VALUE self); 

// The initialization method for this module 
void Init_fastpopcount() { 
    FastPopcount = rb_define_module("FastPopcount"); 
    rb_define_method(FastPopcount, "popcount", method_popcount, 1); 
} 

// Our 'popcount' method.. it uses the builtin popcount 
VALUE method_popcount(int argc, VALUE *argv, VALUE self) { 
    return INT2NUM(__builtin_popcount(NUM2UINT(argv))); 
} 

然後在popcount目錄下運行:

紅寶石extconf.rb 使

然後運行該程序,有你有它做漢明距離....最快的方法在紅寶石。

0

如果您打算遵循基於c的路徑,最好將編譯器標記-msse4.2添加到您的makefile中。這允許編譯器生成基於硬件的popcnt指令,而不是使用表生成popcount。在我的系統上,這大約快了2.5倍。