我一直在绊倒这个问题(例如在this question中)。给定基本整数类型阵列形式的2D位矩阵/板/阵列,例如,一个<html ng-app="angularTypeahead">
的数组。为简单起见,我们可以假设一个方阵,例如,在64位long
的平台上有64 long
个值的数组。
让long
x[i]
成为输入数组。为0 <= i < 64
计算数组y[i]
,以便:
0 <= i <= 64
此处(x[i] >> j) & 1 == (y[j] >> i) & 1
是x >> i
按x
位的按位右移,i
是按位的,而&
是x[i]
的值数组i
中的第1个位置。
如何实现最有效地将数组x
映射到数组x
的函数?
主要是我正在寻找非破坏性方法,这使得输入数组y
保持不变。
使用的编程语言应该对整数类型进行数组和按位运算。许多语言都满足这些要求。 C / C ++和Java解决方案看起来非常相似,所以让我们选择这些语言。
答案 0 :(得分:4)
这似乎是问题Bitwise transpose of 8 bytes的概括。这个问题只是大约8x8换位,所以你要问的是有点不同。但是,您的问题也会在本书的第7.3节({3}}中得到解答(您可能能够在Google图书上看到Hacker's Delight)。在那里呈现的代码显然源于the relevant pages。
Guy Steele仅包含本书针对Hacker's Delight website和8x8案例的源代码,但后者简单地概括为您的64x64案例:
#include <stdint.h>
void
transpose64(uint64_t a[64]) {
int j, k;
uint64_t m, t;
for (j = 32, m = 0x00000000FFFFFFFF; j; j >>= 1, m ^= m << j) {
for (k = 0; k < 64; k = ((k | j) + 1) & ~j) {
t = (a[k] ^ (a[k | j] >> j)) & m;
a[k] ^= t;
a[k | j] ^= (t << j);
}
}
}
这种方法的工作原理是该函数连续交换较小的位块,从32x32块开始(不将位移到那些块中),然后在那些32x32块内交换相应的块16x16块等。保存块大小的变量是j
。因此,外循环j
连续取值32,16,8,4,2和1,这意味着外循环运行六次。内部循环遍历 half 位线,即变量k
中给定位等于零的行。当j
为32时,那些是0-31行,当j
为16时,那些是0-15和32-47等等。循环的内部部分一起运行6 * 32 = 192次在这个内部部分内部发生的是掩码m
确定应该交换的比特,在t
xor或那些比特被计算,并且xor-ed比特列表用于适当地更新两个地方的位。
本书(和网站)也有这个代码的一个版本,其中这些循环都已展开,并且掩码m
未计算,但只是分配。我想这取决于寄存器的数量和指令缓存的大小是否有所改善?
为了测试这是否有效,假设我们定义了一些位模式,比如说:
uint64_t logo[] = {
0b0000000000000000000000000000000000000000000100000000000000000000,
0b0000000000000000000000000000000000000000011100000000000000000000,
0b0000000000000000000000000000000000000000111110000000000000000000,
0b0000000000000000000000000000000000000001111111000000000000000000,
0b0000000000000000000000000000000000000000111111100000000000000000,
0b0000000000000000000000000000000000000000111111100000000000000000,
0b0000000000000000000000000000000000000000011111110000000000000000,
0b0000000000000000000000000000000000000000001111111000000000000000,
0b0000000000000000000000000000000000000000001111111100000000000000,
0b0000000000000000000000000000000010000000000111111100000000000000,
0b0000000000000000000000000000000011100000000011111110000000000000,
0b0000000000000000000000000000000111110000000001111111000000000000,
0b0000000000000000000000000000001111111000000001111111100000000000,
0b0000000000000000000000000000011111111100000000111111100000000000,
0b0000000000000000000000000000001111111110000000011111110000000000,
0b0000000000000000000000000000000011111111100000001111111000000000,
0b0000000000000000000000000000000001111111110000001111111100000000,
0b0000000000000000000000000000000000111111111000000111111100000000,
0b0000000000000000000000000000000000011111111100000011111110000000,
0b0000000000000000000000000000000000001111111110000001111111000000,
0b0000000000000000000000000000000000000011111111100001111111100000,
0b0000000000000000000000001100000000000001111111110000111111100000,
0b0000000000000000000000001111000000000000111111111000011111110000,
0b0000000000000000000000011111110000000000011111111100001111100000,
0b0000000000000000000000011111111100000000001111111110001111000000,
0b0000000000000000000000111111111111000000000011111111100110000000,
0b0000000000000000000000011111111111110000000001111111110000000000,
0b0000000000000000000000000111111111111100000000111111111000000000,
0b0000000000000000000000000001111111111111100000011111110000000000,
0b0000000000000000000000000000011111111111111000001111100000000000,
0b0000000000000000000000000000000111111111111110000011000000000000,
0b0000000000000000000000000000000001111111111111100000000000000000,
0b0000000000000000000000000000000000001111111111111000000000000000,
0b0000000000000000000000000000000000000011111111111100000000000000,
0b0000000000000000000111000000000000000000111111111100000000000000,
0b0000000000000000000111111110000000000000001111111000000000000000,
0b0000000000000000000111111111111100000000000011111000000000000000,
0b0000000000000000000111111111111111110000000000110000000000000000,
0b0000000000000000001111111111111111111111100000000000000000000000,
0b0000000000000000001111111111111111111111111111000000000000000000,
0b0000000000000000000000011111111111111111111111100000000000000000,
0b0000001111110000000000000001111111111111111111100000111111000000,
0b0000001111110000000000000000000011111111111111100000111111000000,
0b0000001111110000000000000000000000000111111111100000111111000000,
0b0000001111110000000000000000000000000000001111000000111111000000,
0b0000001111110000000000000000000000000000000000000000111111000000,
0b0000001111110000000000000000000000000000000000000000111111000000,
0b0000001111110000001111111111111111111111111111000000111111000000,
0b0000001111110000001111111111111111111111111111000000111111000000,
0b0000001111110000001111111111111111111111111111000000111111000000,
0b0000001111110000001111111111111111111111111111000000111111000000,
0b0000001111110000001111111111111111111111111111000000111111000000,
0b0000001111110000001111111111111111111111111111000000111111000000,
0b0000001111110000000000000000000000000000000000000000111111000000,
0b0000001111110000000000000000000000000000000000000000111111000000,
0b0000001111110000000000000000000000000000000000000000111111000000,
0b0000001111110000000000000000000000000000000000000000111111000000,
0b0000001111110000000000000000000000000000000000000000111111000000,
0b0000001111111111111111111111111111111111111111111111111111000000,
0b0000001111111111111111111111111111111111111111111111111111000000,
0b0000001111111111111111111111111111111111111111111111111111000000,
0b0000001111111111111111111111111111111111111111111111111111000000,
0b0000001111111111111111111111111111111111111111111111111111000000,
0b0000001111111111111111111111111111111111111111111111111111000000,
};
然后我们调用transpose32
函数并打印结果位模式:
#include <stdio.h>
void
printbits(uint64_t a[64]) {
int i, j;
for (i = 0; i < 64; i++) {
for (j = 63; j >= 0; j--)
printf("%c", (a[i] >> j) & 1 ? '1' : '0');
printf("\n");
}
}
int
main() {
transpose64(logo);
printbits(logo);
return 0;
}
然后这将作为输出:
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000011111111111111111111111
0000000000000000000000000000000000000000011111111111111111111111
0000000000000000000000000000000000000000011111111111111111111111
0000000000000000000000000000000000000000011111111111111111111111
0000000000000000000000000000000000000000011111111111111111111111
0000000000000000000000000000000000000000011111111111111111111111
0000000000000000000000000000000000000000000000000000000000111111
0000000000000000000000000000000000000000000000000000000000111111
0000000000000000000000000000000000000000000000000000000000111111
0000000000000000000000000000000000000000000000000000000000111111
0000000000000000000000000000000000000000000000000000000000111111
0000000000000000000000000000000000000000000000000000000000111111
0000000000000000000000000000000000000011000000011111100000111111
0000000000000000000000000000000000111111000000011111100000111111
0000000000000000000000000000000000111111000000011111100000111111
0000000000000000000000000000000000111111000000011111100000111111
0000000000000000000000000100000000011111000000011111100000111111
0000000000000000000000011110000000011111100000011111100000111111
0000000000000000000001111110000000011111100000011111100000111111
0000000000000000000001111111000000011111100000011111100000111111
0000000000000000000000111111000000011111100000011111100000111111
0000000000000000000000111111100000001111110000011111100000111111
0000000000000000000000011111100000001111110000011111100000111111
0000000000000100000000011111110000001111110000011111100000111111
0000000000001110000000001111110000001111110000011111100000111111
0000000000011110000000001111111000001111110000011111100000111111
0000000001111111000000000111111000000111111000011111100000111111
0000000000111111100000000111111100000111111000011111100000111111
0000000000111111110000000011111100000111111000011111100000111111
0000000000011111111000000011111100000111111000011111100000111111
0000000000001111111100000001111110000011111000011111100000111111
0000000000000111111100000001111110000011111100011111100000111111
0000000000000011111110000000111111000011111100011111100000111111
0001000000000001111111000000111111000011111100011111100000111111
0011110000000001111111100000111111100011111100011111100000111111
0111111000000000111111110000011111100001111100011111100000111111
0111111110000000011111111000011111110001111110011111100000111111
1111111111000000001111111000001111110001111110011111100000111111
0011111111100000000111111100001111111001111110011111100000111111
0001111111111000000011111110000111111001111110011111100000111111
0000111111111100000011111111000111111100111100000000000000111111
0000001111111110000001111111100011111100000000000000000000111111
0000000111111111100000111111110011111000000000000000000000111111
0000000011111111110000011111110001100000000000000000000000111111
0000000000111111111000001111111000000000000000000000000000111111
0000000000011111111110000111111000000000000000000000000000111111
0000000000001111111111000111110000000000011111111111111111111111
0000000000000011111111100011100000000000011111111111111111111111
0000000000000001111111111001000000000000011111111111111111111111
0000000000000000111111111100000000000000011111111111111111111111
0000000000000000001111111100000000000000011111111111111111111111
0000000000000000000111111000000000000000011111111111111111111111
0000000000000000000011110000000000000000000000000000000000000000
0000000000000000000000100000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
正如我们所希望的那样,这很好地翻转了。
这实际上并不是您要求的,因为您要求提供此代码的非 - 破坏性版本。您可以通过将32x32块的第一次交换从x
转换为y
来实现此目的。例如,您可能会执行以下操作:
void
non_destructive_transpose64(uint64_t x[64], uint64_t y[64]) {
int j, k;
uint64_t m, t;
for (k = 0; k < 64; k += 2) {
((uint32_t *) y)[k] = ((uint32_t *) x)[k ^ 64 + 1];
((uint32_t *) y)[k + 1] = ((uint32_t *) x)[k + 1];
}
for (; k < 128; k += 2) {
((uint32_t *) y)[k] = ((uint32_t *) x)[k];
((uint32_t *) y)[k + 1] = ((uint32_t *) x)[k ^ 64];
}
for (j = 16, m = 0x0000FFFF0000FFFF; j; j >>= 1, m ^= m << j) {
for (k = 0; k < 64; k = ((k | j) + 1) & ~j) {
t = (y[k] ^ (y[k | j] >> j)) & m;
y[k] ^= t;
y[k | j] ^= (t << j);
}
}
}
与其他版本的代码不同,无论架构的字节顺序如何, 都无法正常工作。此外,我知道C标准不允许您以uint64_t
的数组的形式访问uint32_t
数组。但是,我喜欢它,当你这样做时,移动块周围循环的第一次迭代不需要移位或xors。
答案 1 :(得分:0)
在C ++ 8x8矩阵中将是这样,但是您可以轻松地对其进行更改以使其更通用(不仅仅是8x8)。 另外,我将main包含在1个测试向量中只是为了获得一种感觉:
#include <iostream>
#include <string>
#include <vector>
std::vector<long> rotate(std::vector<long>& v) {
std::vector<long> temp = { 0,0,0,0,0,0,0,0 };
for (unsigned int i = 0; i<8; i++) {
int number = v[i];
for (unsigned int j = 0; j<8; j++) {
int z = (number & (1 << (7-j)));
if (z != 0) {
temp[j] |= (1 << (7 - i));
}
}
}
return temp;
}
int main()
{
std::vector<long> v = { 0, 1, 2, 3, 4, 5, 6, 7 };
std::vector<long> rotated = rotate(v);
for (unsigned int i = 0; i<8; i++) {
std::cout << rotated.at(i) << " ";
}
return 0;
}
因此,如果Java需要它,则可以轻松转换它,因为Java也提供位运算符。