【问题标题】:Calculate the parity of a byte in Ruby在Ruby中计算一个字节的奇偶校验
【发布时间】:2011-05-27 18:47:12
【问题描述】:

在 Ruby 中,计算一个字节是奇校验还是偶校验的最佳方法是什么?我有一个可用的版本:

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

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

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

谢谢, 丹

【问题讨论】:

    标签: ruby des 3des parity


    【解决方案1】:

    一个包含 255 个条目的数组的查找表可能是最快的“In Ruby”解决方案。

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

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

    【讨论】:

      【解决方案2】:

      你看过RubyDES library吗?这可能会消除编写您自己的实现的需要。

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

      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
      

      【讨论】:

        【解决方案3】:
        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
        

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

        【讨论】:

          【解决方案4】:

          除非你所拥有的不够快,否则保持它。简洁明了,性能比你想象的要好。

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

          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 字节的速率计算奇偶校验。

          【讨论】:

            【解决方案5】:

            将您的原始解决方案与 memoization 结合使用如何?这只会为每个整数值计算一次。

            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
            

            【讨论】:

              【解决方案6】:

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

              为了测试你的字节,

              掩盖左半字节并进行查找,记住数字。 做。向右移动 4 并进行第二次查找,将结果编号与前一个编号相加以提供总和。

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

              发邮件给我一个简单的函数来做 8 个字节的奇偶校验。 3DES 使用 3 组 8 字节。

              【讨论】:

                猜你喜欢
                • 2013-06-25
                • 1970-01-01
                • 2017-06-22
                • 2015-06-05
                • 2019-11-22
                • 2015-01-18
                • 2018-01-23
                • 2012-07-02
                • 2015-06-29
                相关资源
                最近更新 更多