【问题标题】:uninterlace bits from 16 bit value来自 16 位值的非交错位
【发布时间】:2009-09-26 22:09:17
【问题描述】:

我有一个 16 位值,其位“交错”。

我想得到一个包含 8 个项目(值 0 到 3)的数组,按此顺序存储位:

  • 项目 0:位 7 和 15
  • 项目 1:位 6 和 14
  • 第 2 项:第 5 位和第 13 位
  • ...
  • 第 7 项:位 0 和 8

这是一个简单的解决方案:

function uninterlace(n) {
  return [((n>>7)&1)|((n>>14)&2), // bits 7 and 15
          ((n>>6)&1)|((n>>13)&2), // bits 6 and 14
          ((n>>5)&1)|((n>>12)&2), // bits 5 and 13
          ((n>>4)&1)|((n>>11)&2), // bits 4 and 12
          ((n>>3)&1)|((n>>10)&2), // bits 3 and 11
          ((n>>2)&1)|((n>> 9)&2), // bits 2 and 10
          ((n>>1)&1)|((n>> 8)&2), // bits 1 and 9
          ((n>>0)&1)|((n>> 7)&2)];// bits 0 and 8
}

有人知道更好(更快)的方法吗?

编辑:

注意事项:

  • 构建预先计算的表不是一种选择。
  • 无法使用汇编程序或特定于 CPU 的优化

【问题讨论】:

  • 我真的很喜欢你的做法。位移非常快。

标签: algorithm language-agnostic boolean bits shift


【解决方案1】:

比手写展开循环更快?我怀疑。

使用for-loop 可以减少代码的重复性,但这不会让它运行得更快。

【讨论】:

    【解决方案2】:
    def uninterlace(n) {
        mask = 0x0101 // 0b0000_0001_0000_0001
        slide = 0x7f  // 0b0111_1111
        return [(((n >> 0) & mask) + slide) >> 7,
                (((n >> 1) & mask) + slide) >> 7,
                (((n >> 2) & mask) + slide) >> 7,
                (((n >> 3) & mask) + slide) >> 7,
                (((n >> 4) & mask) + slide) >> 7,
                (((n >> 5) & mask) + slide) >> 7,
                (((n >> 6) & mask) + slide) >> 7,
                (((n >> 7) & mask) + slide) >> 7]
    }
    

    每个条目只有四个操作,而不是 5 个。诀窍在于重用移位的值。 slide 的加法将相关位移动到彼此相邻的位置,移位 7 将它们置于低位。使用+ 可能是一个弱点。

    一个更大的弱点可能是每个条目的操作必须完全按顺序完成,从进入处理器的管道到离开它会产生 4 条指令的延迟。这些可以完全流水线化,但仍然会有一些延迟。该问题的版本公开了一些指令级并行性,并且在有足够的执行资源的情况下,每个条目可能只有 3 条指令的延迟。

    也许可以将多个提取组合成更少的操作,但我还没有看到一种方法来做到这一点。事实上,交错确实使这具有挑战性。

    编辑:一种两遍方法来对称地处理低位和高位位,使用交错的 0,将它们彼此移开一个,或者结果可能更快,并且可以扩展到更长的位串。

    根据佩德罗的评论更正slide。很抱歉占用您的时间在我糟糕的基础转换技能上。原来是0xef,把0位放错地方了。

    【讨论】:

    • 对不起,这是个好主意,但你的函数给了我不同的结果。对于 0x1234 (00010010-00110100) 的输入,结果应为:0,0,1,3,0,1,2,0。但是你的函数返回:1,3,1,1,3,1,1,1(它是反转+1)。反向问题很容易解决,但我不知道如何避免 +1。
    • 我发帖后才发现反了,但懒得编辑,觉得解决起来很容易。我需要更仔细地查看 +1 问题。
    • 如果有帮助的话...数据是从字节数组中获取的,然后加入一个 16 位整数,因此函数的输入也可以是两个 8 位值。
    • 已解决。幻灯片错了,应该是0x7F(bin 01111111)
    • 这就是为什么语言需要二进制文字的语法
    【解决方案3】:

    好的,现在每个项目有 3 个操作(经过测试并且有效)。

    这是 Novelocrat 答案的变体。它使用可变掩码和幻灯片。

    function uninterlace(n) {     
         return [((n & 0x8080) + 0x3FFF) >> 14,
                 ((n & 0x4040) + 0x1FFF) >> 13,
                 ((n & 0x2020) + 0x0FFF) >> 12,
                 ((n & 0x1010) + 0x07FF) >> 11,
                 ((n & 0x0808) + 0x03FF) >> 10,
                 ((n & 0x0404) + 0x01FF) >> 9,
                 ((n & 0x0202) + 0x00FF) >> 8,
                 ((n & 0x0101) + 0x007F) >> 7];
    }
    

    【讨论】:

    • 干得好。我应该多考虑一下我的答案。
    【解决方案4】:

    一个包含 128 个条目乘以 2 的预先计算好的小表怎么样?

    int[128] b1 = { 2, 3, 3, .. 3};
    int[128] b0 = { 0, 1, 1, .. 1};
    
    function uninterlace(n) {
      return [(n & 0x8000) ? b1 : b0)[n & 0x80],
              (n & 0x4000) ? b1 : b0)[n & 0x40],
              (n & 0x2000) ? b1 : b0)[n & 0x20],
              (n & 0x1000) ? b1 : b0)[n & 0x10],
              (n & 0x0800) ? b1 : b0)[n & 0x08],
              (n & 0x0400) ? b1 : b0)[n & 0x04],
              (n & 0x0200) ? b1 : b0)[n & 0x02],
              (n & 0x0100) ? b1 : b0)[n & 0x01]
             ];
    }
    

    这使用位掩码和表查找而不是移位和加法,并且可能更快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-30
      • 2016-06-24
      • 1970-01-01
      • 1970-01-01
      • 2012-07-01
      相关资源
      最近更新 更多