我有一个二进制数字(52位),表示为字符串“01100011 ....”
计算1的数量的最快方法是什么?
"01100011....".count("1")
显然有效,但如果这项操作需要进行数千次,则非常耗时。
好的,还有一些信息。我正在尝试为单词创建位向量,如下所示def bit_vec(str)
alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
bv = ""
alphabet.each_char do |a|
if str.include?(a)
bv += "1"
else
bv += "0"
end
end
bv
end
bit_vec方法被调用大约170K次。我将位向量存储在散列中,并使用它们通过对位向量进行异或并计算1的数量(更多1 = =更少的相似性)来查找给定单词的相似单词。如果count方法不使用String#scan,还有什么可以使用它?
我知道Ruby比C或Java慢。我只是想尽力改进算法。我不是在寻找原始速度。
也许包含?方法是瓶颈?
答案 0 :(得分:10)
无论如何,你将获得O(n)
表现。试试这个简单的ruby命令。测量它是否真的是一个问题。
使用time ruby test.rb
测量的这个简单脚本耗时0.058秒。这是一个旧的1.25 Ghz处理器。你确定这个操作太慢了吗?
10000.times do
"0100010000100001111101101000111110000001101001010".count("1")
end
如果不够快,请写一个C扩展名。尽量避免使用条件。写得像这样:
count = 0;
for (i = stringLength; i; i++) {
count += string[i] - '0'; // no conditional used.
}
但老实说,如果你需要那种速度,红宝石对你来说是错误的语言。 ruby中有很多不同的东西,比简单的.count("1")
花费的时间多 。
答案 1 :(得分:9)
请注意,计数1位的问题称为“人口计数”。
至少在Ruby中,坚持通过count
方法将这些作为字符串处理,除非你有令人信服的理由使用整数。
count
:
基准:10,000,000次迭代78.60秒(每秒127,225.63次迭代)
对于整数数学,
如果您不关心2**32
以上的值,
def popcount(x)
m1 = 0x55555555
m2 = 0x33333333
m4 = 0x0f0f0f0f
x -= (x >> 1) & m1
x = (x & m2) + ((x >> 2) & m2)
x = (x + (x >> 4)) & m4
x += x >> 8
return (x + (x >> 16)) & 0x3f
end
基准:10,000,000次迭代105.73秒(每秒94,579.03次迭代)
如果您关心2**32
以上的值,
def popcount(x)
b = 0
while x > 0
x &= x - 1
b += 1
end
return b
end
基准测试:10,000,000次迭代365.59次(每秒27,353.27次迭代)
<强>附录强>
您的代码:
基准:1,000,000次迭代(每秒12,779.56次迭代)78.25秒
此代码:
def bit_vec(str)
# alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
bv = "0" * 52
str.each_char do |c|
ord = c.ord
next unless (ord >= 65 && ord <= 90) || (ord >= 97 && ord <= 122)
index = ord - 65
index -= 6 if index > 25
bv[index] = "1"
break if bv == "1111111111111111111111111111111111111111111111111111"
end
bv
end
注意:你说你正在处理一个52位的数字,所以我假设你关心大写和小写字母(26 + 26 = 52)。我选择先检查大写字母,因为这就是它们在几乎所有字符集中出现的方式,使计算更容易。
基准:2,000,000次迭代的24.86秒(每秒40,231.60次迭代)
3.14x加速。
答案 2 :(得分:3)
来自http://www.bergek.com/2009/03/11/count-number-of-bits-in-a-ruby-integer/
yourString.scan(/1/).size
来自http://snippets.dzone.com/posts/show/4233
count = 0
count += byte & 1 and byte >>= 1 until byte == 0
这是一个带有不同循环(在c中)的帖子,用于根据0和1的密度进行计数
http://gurmeetsingh.wordpress.com/2008/08/05/fast-bit-counting-routines/
答案 3 :(得分:3)
这是另一个基准:https://gist.github.com/knugie/3865903
如果您有疑问,只需在您的机器上运行它。
Ruby不应该用于最大程度的优化,但检查代码中的瓶颈总是合理的。在一个域中运行良好的算法在另一个域中不一定有效。尝试使用应用程序中的实际数据进行优化。
示例输出:
$ ruby bit_count_benchmark.rb CPU : Intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz MEM : 3083288 kB RUBY : ruby-1.9.2-p320 "NORM": TEST... OK BENCHMARK (2000000): PREPARE... OK RUN... user system total real scan_string 227.770000 0.250000 228.020000 (227.912435) scan_regex 214.500000 0.220000 214.720000 (214.635405) progressive_right_shift 43.420000 0.030000 43.450000 ( 43.412643) continuous_right_shift 39.340000 0.010000 39.350000 ( 39.345163) count_string 19.910000 0.030000 19.940000 ( 19.932677) access_bit_fast 18.310000 0.040000 18.350000 ( 18.345740) bit_elimination_for 16.400000 0.010000 16.410000 ( 16.388461) bit_elimination_until 14.650000 0.000000 14.650000 ( 14.650187) bit_elimination_while 14.610000 0.000000 14.610000 ( 14.604845) pre_compute_16 4.370000 0.000000 4.370000 ( 4.371228) "NORM" FINISHED "LOTTO": TEST... OK BENCHMARK (2000000): PREPARE... OK RUN... user system total real scan_string 92.900000 0.100000 93.000000 ( 92.947647) scan_regex 79.500000 0.230000 79.730000 ( 79.671581) progressive_right_shift 43.430000 0.010000 43.440000 ( 43.424880) continuous_right_shift 35.360000 0.020000 35.380000 ( 35.360854) count_string 19.210000 0.020000 19.230000 ( 19.215173) access_bit_fast 17.890000 0.000000 17.890000 ( 17.890401) bit_elimination_for 5.680000 0.010000 5.690000 ( 5.680348) bit_elimination_until 5.040000 0.010000 5.050000 ( 5.054189) bit_elimination_while 5.080000 0.020000 5.100000 ( 5.093165) pre_compute_16 4.360000 0.010000 4.370000 ( 4.364988) "LOTTO" FINISHED DONE
答案 4 :(得分:1)
将字符串拆分为8,在128条目查找表中查找每个条目并对它们求和?
我知道......这太荒谬了......只是分享一些想法; - )