【发布时间】:2023-03-21 20:22:01
【问题描述】:
这个问题:How to de-interleave bits (UnMortonizing?) 有一个很好的答案来提取莫顿数的两半之一(只是奇数位),但我需要一个提取两个部分(奇数位和偶数位)的解决方案尽可能少的操作。
为了我的使用,我需要取一个 32 位整数并提取两个 16 位整数,其中一个是偶数位,另一个是奇数位右移 1 位,例如
input, z: 11101101 01010111 11011011 01101110
output, x: 11100001 10110111 // odd bits shifted right by 1
y: 10111111 11011010 // even bits
似乎有很多解决方案使用带有幻数的移位和掩码来生成莫顿数(即交错位),例如Interleave bits by Binary Magic Numbers,但我还没有找到任何相反的方法(即去交错)。
更新
在重新阅读 Hacker's Delight 关于完美随机播放/取消随机播放的部分后,我发现了一些有用的示例,我将其改编如下:
// morton1 - extract even bits
uint32_t morton1(uint32_t x)
{
x = x & 0x55555555;
x = (x | (x >> 1)) & 0x33333333;
x = (x | (x >> 2)) & 0x0F0F0F0F;
x = (x | (x >> 4)) & 0x00FF00FF;
x = (x | (x >> 8)) & 0x0000FFFF;
return x;
}
// morton2 - extract odd and even bits
void morton2(uint32_t *x, uint32_t *y, uint32_t z)
{
*x = morton1(z);
*y = morton1(z >> 1);
}
我认为这仍然可以改进,无论是在其当前的标量形式还是通过利用 SIMD,所以我仍然对更好的解决方案(标量或 SIMD)感兴趣。
【问题讨论】:
-
您链接到的交织解决方案使用的操作是您链接到的去交织解决方案的两倍。如果这是可以接受的,您可以通过两次应用解交织解决方案来实现相同的性能。我认为没有比这更好的了,因为两种解决方案都使用相同的原理,并且有一半位为 0 的阶段,所以它们一次只能处理一半的信息,所以如果你想要所有信息你需要两个去。当然,如果你有 64 位整数,你可以一次性完成;然后您可以将其中一个奇偶校验位移动到高 32 位。
-
我又玩了一些——我没有想出更好的解决方案,但我确实做了一个有趣的观察:如果可以的话,您可以有效地将 AaBbCcDd.. 更改为 ABabCDcd..有效地将 0aB00cD0.. 更改为 0Ba00Dc0.. -- 因此您可以减少此步骤以有效交换两个位,这意味着实现映射 0->0、3->3、1->2、2->1。我能想到的对两位(mod 4)的可逆运算是:加 0、1、2 或 3,与 1 或 3 进行异或运算,或乘以 3。但这些只会生成 S_4 的 8 元素子组,它不会'不包括所需的排列。
-
我假设“交错操作”是指将 32 位字的高 16 位视为奇数位,将低 16 位视为偶数位,并通过交错获得新的 32 位字他们?抽象的答案是,是的,它是循环的,因为它是双射运算,并且存在有限数量的不同 32 位字 :-) 但更实际地说,循环长度是 5:交错操作循环二进制中的数字位索引的表示,最低有效位变为最高有效位,对于 32 位字,有 5 位数字循环。
-
我的另一个想法,开箱即用:您需要以正确顺序排列的奇数位和偶数位吗?或者您是否可以重组其余代码(例如通过使用不同的查找表),以便您可以以不同的顺序接受它们?因为让它们以不同的顺序排列很容易:odd = x & 0xaaaaaaaa;奇数 = (奇数 | (奇数 >>> 17)) & 0xffff;偶数 = x & 0x55555555;偶数 = (偶数 | (偶数 >>> 15)) & 0xffff;
-
@joriki:不幸的是,我需要以正确的顺序排列这些位 - 我将使用它们作为数组的索引,我需要按 Morton 顺序遍历该数组。
标签: bit-manipulation z-order-curve