问题描述
|
在ruby中,计算两个无符号整数之间的位差(例如汉明距离)的最有效方法是什么?
例如,我有整数a = 2323409845和b = 1782647144。
它们的二进制表示为:
a = 10001010011111000110101110110101
b = 01101010010000010000100101101000
a和b之间的位差为17.。
我可以对它们执行逻辑XOR,但这将给我一个不同的整数!= 17,然后我必须遍历结果的二进制表示并计算1的数目。
计算位差的最有效方法是什么?
现在,计算许多整数序列的位差的答案是否改变?例如。给定2个无符号整数序列:
x = {2323409845,641760420,509499086....}
y = {uint,uint,uint...}
计算两个序列之间的位差的最有效方法是什么?
您是要遍历整个序列,还是有一种更快的方法来一次计算整个序列的差异?
解决方法
您可以使用Ruby中优化的String函数来进行位计数,而不是使用纯算术。有了一些快速的基准测试,结果大约快了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>>
, 根据mu的建议,我编写了一个简单的C扩展以使用__builtin_popcount,并使用基准测试证明它比ruby优化的字符串函数至少快3倍。
我看了以下两个教程:
用C扩展Ruby
5分钟内使用C进行Ruby扩展
在我的程序中:
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 self) {
return INT2NUM(__builtin_popcount(NUM2UINT(argv)));
}
然后在popcount目录中运行:
红宝石extconf.rb
使
然后运行程序,就可以了。...最快的方法来在红宝石中进行汉明距离。
, Wegner的算法:
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
, 如果打算遵循基于C的路径,则最好将编译器标志-msse4.2
添加到您的makefile中。这使编译器可以生成基于硬件的“ 9”指令,而无需使用表来生成弹出计数。在我的系统上,速度大约快2.5倍。