在红宝石中计算汉明距离的最有效方法?
Posted
技术标签:
【中文标题】在红宝石中计算汉明距离的最有效方法?【英文标题】:Most efficient way to calculate hamming distance in ruby? 【发布时间】:2011-06-18 09:45:46 【问题描述】:在 ruby 中,计算两个无符号整数之间的位差(例如汉明距离)最有效的方法是什么?
例如,我有整数 a = 2323409845 和 b = 1782647144。
它们的二进制表示是:
a = 10001010011111000110101110110101
b = 01101010010000010000100101101000
a 和 b 的位差是 17..
我可以对它们进行逻辑异或,但这会给我一个不同的整数!= 17,然后我必须遍历结果的二进制表示并计算 1 的数量。
计算位差的最有效方法是什么?
现在,计算许多整数序列的位差的答案会改变吗?例如。给定 2 个无符号整数序列:
x = 2323409845,641760420,509499086....
y = uint,uint,uint...
计算两个序列的位差最有效的方法是什么?
您会遍历序列,还是有更快的方法来一次计算整个序列的差异?
【问题讨论】:
谢谢!我只是这样做了,它似乎比下面的方法快 3 倍(使用 Ruby 的优化字符串函数) 我参加这个聚会已经很晚了,但您可能想试试this popcount benchmark。__builtin_popcount
是最慢的方法之一,如果你不这样做 use a compile flag
【参考方案1】:
您可以利用 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>>
【讨论】:
非常感谢,我发现这也快了很多。按照您的建议,使用内置字符串函数进行大约 21K 次比较大约需要 3 秒,而传统方法需要两倍的时间【参考方案2】:根据 mu 的建议太短,我写了一个简单的 C 扩展来使用 __builtin_popcount ,并使用基准验证它至少比 ruby 的优化字符串函数快 3 倍。..
我看了以下两个教程:
Extending Ruby With C Ruby Extension in C in 5 min在我的程序中:
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目录下运行:
ruby extconf.rb 制作
然后运行程序,你就有了....在 ruby 中进行汉明距离的最快方法。
【讨论】:
【参考方案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
【讨论】:
【参考方案4】:如果打算遵循基于 c 的路径,最好将编译器标志 -msse4.2
添加到您的 makefile。这允许编译器生成基于硬件的popcnt
指令,而不是使用表来生成popcount。在我的系统上,这大约快 2.5 倍。
【讨论】:
以上是关于在红宝石中计算汉明距离的最有效方法?的主要内容,如果未能解决你的问题,请参考以下文章