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

Mau*_*lin 6 ruby optimization

我有一个二进制数(52位)表示为字符串"01100011 ...."

计算1的数量最快的方法是什么?

"01100011....".count("1") 
Run Code Online (Sandbox Code Playgroud)

显然有效但如果这个操作需要进行数千次,则非常耗时.

好的,还有一些信息.我正在尝试为单词创建位向量,如下所示

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
Run Code Online (Sandbox Code Playgroud)

bit_vec方法被调用大约170K次.我将位向量存储在散列中,并使用它们通过对位向量进行异或并计算1的数量(更多1 = =更少的相似性)来查找给定单词的相似单词.如果count方法不使用String#scan,还有什么可以使用它?

我知道Ruby比C或Java慢.我只是想尽力改进算法.我不是在寻找原始速度.

也许包括?方法是瓶颈?

Geo*_*lly 9

O(n)无论如何,你都会有表演.试试这个简单的ruby命令.测量它是否真的是一个问题.

这个简单的脚本,用time ruby test.rb0.058 CPU秒测量.这是一个旧的1.25 Ghz处理器.你真的确定这个操作太慢了吗?

10000.times do
  "0100010000100001111101101000111110000001101001010".count("1")
end
Run Code Online (Sandbox Code Playgroud)

如果这还不够快,请写一个C扩展名.尽量避免使用条件.写这样:

count = 0;
for (i = stringLength; i; i++) {
    count += string[i] - '0';     // no conditional used.
}
Run Code Online (Sandbox Code Playgroud)

但老实说,如果你需要那种速度,红宝石对你来说是错误的语言.有红宝石这么多不同的事情,需要很多比简单的更多的时间.count("1").


Bob*_*man 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
Run Code Online (Sandbox Code Playgroud)

基准: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
Run Code Online (Sandbox Code Playgroud)

基准测试:10,000,000次迭代365.59次(每秒27,353.27次迭代)

附加物:

你的代码:

基准:1,000,000次迭代78.25秒(每秒12,779.56次迭代)

这段代码:

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
Run Code Online (Sandbox Code Playgroud)

注意:你说你正在处理一个52位的数字,所以我假设你关心大写和小写字母(26 + 26 = 52).我首先选择检查大写字母,因为这是它们在几乎所有字符集中出现的方式,使计算更容易一些.

基准测试:1,000,000次迭代的24.86秒(每秒40,231.60次迭代)

3.14倍加速.