Ruby:计算二进制数中的1的数量

时间:2009-10-28 19:59:00

标签: ruby optimization

我有一个二进制数字(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慢。我只是想尽力改进算法。我不是在寻找原始速度。

也许包含?方法是瓶颈?

5 个答案:

答案 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条目查找表中查找每个条目并对它们求和?

我知道......这太荒谬了......只是分享一些想法; - )