计算 Ruby 中字节的奇偶校验

发布于 2024-10-08 04:07:54 字数 250 浏览 9 评论 0原文

在 Ruby 中计算一个字节是否具有奇数或偶数奇偶校验的最佳方法是什么?我有一个版本可以工作:

result = "AB".to_i(16).to_s(2).count('1').odd?
=> true

将数字转换为字符串并计算“1”似乎是计算奇偶校验的一种糟糕方法。还有更好的方法吗?

我希望能够计算 3DES 密钥的奇偶校验。最终,我想将偶数字节转换为奇数字节。

谢谢, 担

What's the best way to calculate if a byte has odd or even parity in Ruby? I've got a version working:

result = "AB".to_i(16).to_s(2).count('1').odd?
=> true

Converting a number to a string and counting the "1"s seems a poor way of calculating parity though. Any better methods?

I want to be able to calculate the parity of a 3DES key. Eventually, I'll want to convert even bytes to odd.

Thanks,
Dan

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

香橙ぽ 2024-10-15 04:07:54

除非你现有的速度不够快,否则就保留它。清晰简洁,性能比你想象的还要好。

我们将针对数组查找(我测试过的最快方法)对所有内容进行基准测试:

ODD_PARITY = [
  false,
  true,
  true,
  ...
  true,
  false,
]

def odd_parity?(hex_string)
  ODD_PARITY[hex_string.to_i(16)]
end
  • 数组查找以每秒 640,000 字节的速率计算奇偶校验。
  • Bowsersenior 的 C 代码以每秒 640,000 字节的速率计算奇偶校验。
  • 您的代码以每秒 284,000 字节的速率计算奇偶校验。
  • Bowsersenior 的本机代码以每秒 171,000 字节的速率计算奇偶校验。
  • Theo 的缩短代码以每秒 128,000 字节的速率计算奇偶校验。

Unless what you have is not fast enough, keep it. It's clear and succinct, and its performance is better than you think.

We'll benchmark everything against array lookup, the fastest method I tested:

ODD_PARITY = [
  false,
  true,
  true,
  ...
  true,
  false,
]

def odd_parity?(hex_string)
  ODD_PARITY[hex_string.to_i(16)]
end
  • Array lookup computes the parity at a rate of 640,000 bytes per second.
  • Bowsersenior's C code computes parity at a rate of 640,000 bytes per second.
  • Your code computes parity at a rate of 284,000 bytes per second.
  • Bowsersenior's native code computes parity at a rate of 171,000 bytes per second.
  • Theo's shortened code computes parity at a rate of 128,000 bytes per second.
乖乖兔^ω^ 2024-10-15 04:07:54

您是否看过 RubyDES 库?这可能会消除您自己编写实现的需要。

要计算奇偶校验,您可以使用如下内容:

require 'rubygems'
require 'inline'  # RubyInline (install with `gem install RubyInline`)

class Fixnum
  # native ruby version: simpler but slow
  # algorithm from: 
  #   http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel      
  def parity_native
    (((self * 0x0101010101010101) & 0x8040201008040201) % 0x1FF) & 1
  end

  class << self
    # inline c version using RubyInline to create c extension
    # 4-5 times faster than native version
    # use as class method: 
    #   Fixnum.parity(0xAB)
    inline :C do |builder|
      builder.c <<-EOC
      int parity_c(int num) {  
        return (
            ((num * 0x0101010101010101ULL) & 0x8040201008040201ULL) % 0x1FF
          ) & 1;
      }
      EOC
    end
  end

  def parity
    self.class.parity_c(self)
  end

  def parity_odd?
    1 == parity
  end
  def parity_even?
    0 == parity
  end
end

0xAB.parity        # => 1 
0xAB.parity_odd?   # => true 
0xAB.parity_even?  # => false
(0xAB + 1).parity  # => 0

根据简单的基准测试,内联 c 版本比本机 ruby​​ 版本快 3-4 倍

require 'benchmark'
n = 10000
Benchmark.bm do |x|
  x.report("inline c") do
    n.times do 
      (0..255).map{|num| num.parity}
    end
  end

  x.report("native ruby") do
    n.times do 
      (0..255).map{|num| num.parity_native}
    end
  end
end
# inline c     1.982326s
# native ruby  7.044330s

Have you taken a look at the RubyDES library? That may remove the need to write your own implementation.

To calculate parity, you can use something like the following:

require 'rubygems'
require 'inline'  # RubyInline (install with `gem install RubyInline`)

class Fixnum
  # native ruby version: simpler but slow
  # algorithm from: 
  #   http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel      
  def parity_native
    (((self * 0x0101010101010101) & 0x8040201008040201) % 0x1FF) & 1
  end

  class << self
    # inline c version using RubyInline to create c extension
    # 4-5 times faster than native version
    # use as class method: 
    #   Fixnum.parity(0xAB)
    inline :C do |builder|
      builder.c <<-EOC
      int parity_c(int num) {  
        return (
            ((num * 0x0101010101010101ULL) & 0x8040201008040201ULL) % 0x1FF
          ) & 1;
      }
      EOC
    end
  end

  def parity
    self.class.parity_c(self)
  end

  def parity_odd?
    1 == parity
  end
  def parity_even?
    0 == parity
  end
end

0xAB.parity        # => 1 
0xAB.parity_odd?   # => true 
0xAB.parity_even?  # => false
(0xAB + 1).parity  # => 0

According to simple benchmarks, the inline c version is 3-4 times faster than the native ruby version

require 'benchmark'
n = 10000
Benchmark.bm do |x|
  x.report("inline c") do
    n.times do 
      (0..255).map{|num| num.parity}
    end
  end

  x.report("native ruby") do
    n.times do 
      (0..255).map{|num| num.parity_native}
    end
  end
end
# inline c     1.982326s
# native ruby  7.044330s
习ぎ惯性依靠 2024-10-15 04:07:54

具有 255 个条目的数组查找表可能是最快的“Ruby”解决方案。

在 CI 中会屏蔽和转移。或者,如果我有 SSE4,我会使用带有内联汇编器的 POPCNT 指令。如果您需要高性能,请用 C 编写一个本机扩展来执行上述任一操作。

http://en.wikipedia.org/wiki/SSE4

Probably a lookup table of an Array with 255 entries would be fastest "In Ruby" solution.

In C I would mask and shift. Or if I have SSE4 I would use the POPCNT instruction with inline assembler. If you need this to be high performance write a native extension in C which does either of the above.

http://en.wikipedia.org/wiki/SSE4

九歌凝 2024-10-15 04:07:54

将您的原始解决方案与记忆一起使用怎么样?这只会为每个整数值计算一次。

class Fixnum
  # Using a class variable for simplicity, and because subclasses of
  # Fixnum—while very uncommon—would likely want to share it. 
  @@parity = ::Hash.new{ |h,i| h[i] = i.to_s(2).count('1').odd? }
  def odd_parity?
    @@parity[self]
  end
  def even_parity?
    !@@parity[self]
  end
end

"AB".to_i(16).odd_parity?
#=> true

How about using your original solution with memoization? This will only calculate once for each integer value.

class Fixnum
  # Using a class variable for simplicity, and because subclasses of
  # Fixnum—while very uncommon—would likely want to share it. 
  @@parity = ::Hash.new{ |h,i| h[i] = i.to_s(2).count('1').odd? }
  def odd_parity?
    @@parity[self]
  end
  def even_parity?
    !@@parity[self]
  end
end

"AB".to_i(16).odd_parity?
#=> true
苦妄 2024-10-15 04:07:54
x = 'AB'.to_i(16)
p = 0
until x == 0
  p += x & 1
  x = x >> 1
end
puts p # => 5

可以缩短为☺

x = 'AB'.to_i(16)
p = x & 1
p += x & 1 until (x >>= 1) == 0

如果你想要一些不可读的东西,

x = 'AB'.to_i(16)
p = 0
until x == 0
  p += x & 1
  x = x >> 1
end
puts p # => 5

which can be shortened to

x = 'AB'.to_i(16)
p = x & 1
p += x & 1 until (x >>= 1) == 0

if you want something that is unreadable ☺

节枝 2024-10-15 04:07:54

我将构建一个包含 16 个条目的表(作为 16 个字符的表),对应于字节的每个半字节。条目为 0,1,1,2,1,2,....4

要测试您的字节,

请屏蔽左侧半字节并进行查找,记住数字。
做。右移 4 并进行第二次查找,将结果数字与前一个数字相加以提供总和。

然后测试总和的低位。如果为1,则该字节为奇数,如果为0,则该字节为偶数。如果结果为偶数,则使用异或指令翻转高位。
这种查找方法比通过单次移位将字节中的位相加要快得多。

请给我发电子邮件,索要一个简单的函数来执行 8 字节的奇偶校验。 3DES 使用 3 组 8 字节。

I would construct a single table of 16 entries (as a 16 character table), corresponding to each nibble (half) of a bytes. Entries are 0,1,1,2,1,2,....4

To test your byte,

Mask out the left nibble and do a lookup, memorizing the number.
Do. a shift to the right by 4 and do a second lookup, adding the result number to the previous one to provide a sum.

Then test the low order bit from the sum. If it is 1, the byte is odd, if it is a 0, the byte is even. If result is even, you flip the high order bit, using the xor instruction.
THis lookup method is much faster than adding up the bits in a byte by single shifts.

email me for a simple function to do the parity for 8 bytes. 3DES uses 3 groups of 8 bytes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文