在保留模式的同时移位

时间:2017-08-09 19:31:21

标签: c# bit-manipulation

如果之前已经被问过/回答过,我很抱歉,但老实说我甚至不确定如何正确地说出这个问题。我有以下位模式:

0110110110110110110110110110110110110110110110110110110110110110

我试图进行一次保留我的基本模式的转变;我的第一直觉是使用正确的旋转((x >> count) | (x << (-count & 63))),但我的位模式的不对称导致:

0011011011011011011011011011011011011011011011011011011011011011 &lt; ---错误

问题是最重要的(最左边)位最终为0而不是所需的1:

1011011011011011011011011011011011011011011011011011011011011011 &lt; --- right

我正在寻找这个函数的口语名称吗?如果没有,我怎么能实现这个想法呢?

其他信息:

  • 虽然问题与语言无关,但我目前正试图用C#来解决这个问题。
  • 我使用的位模式完全可以预测,并且总是具有相同的结构;模式以单个零开始,后跟n - 1个(其中n是奇数),然后无限重复。
  • 我想在没有条件操作的情况下完成这项任务,因为他们首先打败了使用按位操作的目的,但也许我别无选择......

4 个答案:

答案 0 :(得分:2)

你的数字结构如下:

B16  B15  B14  B13  B12  B11  B10  B09  B08  B07  B06  B05  B04  B03  B02  B01  B00

  ?    0    1    1    0    1    1    0    1    1    0    1    1    0    1    1    0

?需要在班次后出现在MSB(B15,或B63,或其他)中。它从何而来?好吧,找到最近的副本n位于右侧:

B13    0    1    1    0    1    1    0    1    1    0    1    1    0    1    1    0
 ^--------------/

如果您的字词宽度为w,则为1 << (w-n)

                 *

所以你可以这样做:

var selector = 1 << (w-n);
var rotated = (val >> 1) | ((val & selector) << (n-1));

但你可能想要多班。然后我们需要建立一个更宽的面具:

  ?    0    1    1    0    1    1    0    1    1    0    1    1    0    1    1    0
            *    *    *    *    *

在这里,我选择假装n = 6,它只需要是基本n的倍数,并且大于shift。现在:

var selector = ((1UL << shift) - 1) << (w - n);
var rotated = (val >> shift) | ((val & selector) << (n - shift));

使用您的模式进行演示:http://rextester.com/UWYSW47054

根据需要,很容易看到输出具有句点3:

  1:B6DB6DB6DB6DB6DB
  2:DB6DB6DB6DB6DB6D
  3:6DB6DB6DB6DB6DB6
  4:B6DB6DB6DB6DB6DB
  5:DB6DB6DB6DB6DB6D
  6:6DB6DB6DB6DB6DB6
  7:B6DB6DB6DB6DB6DB
  8:DB6DB6DB6DB6DB6D
  9:6DB6DB6DB6DB6DB6
 10:B6DB6DB6DB6DB6DB
 11:DB6DB6DB6DB6DB6D

答案 1 :(得分:1)

@BenVoigt捅戳后

无网点答案

  • 通过执行(n&amp; 1);
  • 获取最后一位 b
  • 返回n&gt;&gt; 1 | b&lt;&lt; ((sizeof(n) - 1)。

原始答案:

  • 通过执行(n&amp; 1);
  • 获取最后一位 b
  • 如果 b 为1,则将数字右移1位并按位或将其与1&lt;&lt; (sizeof(n) - 1);
  • 如果 b 为0,则右移数字1位。

答案 2 :(得分:1)

不要存储大量重复的模式,只需存储一次重复并对索引应用模运算

byte[] pattern = new byte[] { 0, 1, 1 };

// Get a "bit" at index "i", shifted right by "shift"
byte bit = pattern[(i - shift + 1000000 * byte.Length) % byte.Length];

+ 1000000 * byte.Length必须大于预期的最大转变,并确保我们得到一个有效的总和。

这使您可以存储几乎任何长度的图案。

优化将是存储模式的镜像版本。然后你可以左移而不是右移。这将简化指数计算

byte bit = pattern[(i + shift) % byte.Length];

答案 3 :(得分:1)

问题在评论中有所改变。

对于所有合理的n,在最小的预计算之后可以有效地解决以下问题:

  

给定偏移量k,从位于(零,n-1个)模式之后的位流中的该位置开始获得64位。

显然,该模式的重复周期为n,因此,对于n的每个给定值,只需生成ulong个不同的n。这可以明确地完成,在预处理中构造所有这些(它们可以以任何明显的方式构造,它并不重要,因为它只发生一次),或者通过仅存储而更隐蔽地保留。 n的每个值的两个 ulongs(这在n < 64的假设下工作,见下文),然后通过一些移位/ ORing从它们中提取范围。无论哪种方式,使用offset % n来计算要检索的模式(因为偏移量以可预测的方式增加,不需要实际的模运算 [1] )。

即使使用第一种方法,内存消耗也是合理的,因为此优化只是对低n的优化:特别是对于n > 64,每个字平均少于1个零,所以&#34;老式的方式&#34;访问n的每个倍数并重置该位开始跳过工作,而上述技巧仍会访问每个字,并且不能再一次重置多个位。

[1]:如果同时有多个n正在进行中,可能的策略是将数组offsets保留在offsets[n] = offset % n,其中可能是int next = offsets[n] + _64modn[n]; // 64 % n precomputed offsets[n] = next - (((n - next - 1) >> 31) & n); 更新根据:(未经测试)

n

每当next >= n时都会减去n的想法。只需要进行一次减法,因为偏移量和添加到偏移量的东西已经减去模n

这个偏移量增量可以使用System.Numerics.Vectors来完成,与实际硬件相比,它功能很差但是能够做到这一点。它无法进行转换(是的,它很奇怪),但它可以无分支方式实现比较。

每个值n执行一次传递更容易,但以缓存不友好的方式触及大量内存。同时做很多不同的offset % 3可能也不是很好。我想你只需要记下那个......

此外,您可以考虑对某些低数字进行硬编码,类似offset % variable的效果非常高(与template< template <typename...> class Container, typename ... Ts > void printMap(Container<int, long, Ts...> inputMap, bool additionalParam = false) 不同)。这确实需要手动循环展开,这有点烦人,但它实际上更简单,只是在代码行方面很大。