我有一个一个64位整数,我需要在8 x 8区域内旋转90度(最好是直接位操作)。我无法弄清楚任何方便的算法。例如,这个:
// 0xD000000000000000 = 1101000000000000000000000000000000000000000000000000000000000000
1 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
旋转后成为:
// 0x101000100000000 = 0000000100000001000000000000000100000000000000000000000000000000
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
我想知道是否有任何解决方案而无需使用任何预先计算的哈希表?
答案 0 :(得分:13)
不使用任何查找表,我看不到比单独处理每个位更好的事情:
unsigned long r = 0;
for (int i = 0; i < 64; ++i) {
r += ((x >> i) & 1) << (((i % 8) * 8) + (7 - i / 8));
}
答案 1 :(得分:13)
v = (v & 0x000000000f0f0f0fUL) << 004 | (v & 0x00000000f0f0f0f0UL) << 040 | (v & 0xf0f0f0f000000000UL) >> 004 | (v & 0x0f0f0f0f00000000UL) >> 040; v = (v & 0x0000333300003333UL) << 002 | (v & 0x0000cccc0000ccccUL) << 020 | (v & 0xcccc0000cccc0000UL) >> 002 | (v & 0x3333000033330000UL) >> 020; v = (v & 0x0055005500550055UL) << 001 | (v & 0x00aa00aa00aa00aaUL) << 010 | (v & 0xaa00aa00aa00aa00UL) >> 001 | (v & 0x5500550055005500UL) >> 010;
答案 2 :(得分:4)
使用O(log n)移位操作可以有效地执行位反转。如果将64位UINT解释为8x8位数组,则位反转对应于180度旋转。
这些班次中的一半有效地执行水平反射;另一半执行垂直反射。为了获得90度和270度的旋转,正交(即垂直或水平)反射可以与对角线反射相结合,但后者仍然是一个尴尬的位。
typedef unsigned long long uint64;
uint64 reflect_vert (uint64 value)
{
value = ((value & 0xFFFFFFFF00000000ull) >> 32) | ((value & 0x00000000FFFFFFFFull) << 32);
value = ((value & 0xFFFF0000FFFF0000ull) >> 16) | ((value & 0x0000FFFF0000FFFFull) << 16);
value = ((value & 0xFF00FF00FF00FF00ull) >> 8) | ((value & 0x00FF00FF00FF00FFull) << 8);
return value;
}
uint64 reflect_horiz (uint64 value)
{
value = ((value & 0xF0F0F0F0F0F0F0F0ull) >> 4) | ((value & 0x0F0F0F0F0F0F0F0Full) << 4);
value = ((value & 0xCCCCCCCCCCCCCCCCull) >> 2) | ((value & 0x3333333333333333ull) << 2);
value = ((value & 0xAAAAAAAAAAAAAAAAull) >> 1) | ((value & 0x5555555555555555ull) << 1);
return value;
}
uint64 reflect_diag (uint64 value)
{
uint64 new_value = value & 0x8040201008040201ull; // stationary bits
new_value |= (value & 0x0100000000000000ull) >> 49;
new_value |= (value & 0x0201000000000000ull) >> 42;
new_value |= (value & 0x0402010000000000ull) >> 35;
new_value |= (value & 0x0804020100000000ull) >> 28;
new_value |= (value & 0x1008040201000000ull) >> 21;
new_value |= (value & 0x2010080402010000ull) >> 14;
new_value |= (value & 0x4020100804020100ull) >> 7;
new_value |= (value & 0x0080402010080402ull) << 7;
new_value |= (value & 0x0000804020100804ull) << 14;
new_value |= (value & 0x0000008040201008ull) << 21;
new_value |= (value & 0x0000000080402010ull) << 28;
new_value |= (value & 0x0000000000804020ull) << 35;
new_value |= (value & 0x0000000000008040ull) << 42;
new_value |= (value & 0x0000000000000080ull) << 49;
return new_value;
}
uint64 rotate_90 (uint64 value)
{
return reflect_diag (reflect_vert (value));
}
uint64 rotate_180 (uint64 value)
{
return reflect_horiz (reflect_vert (value));
}
uint64 rotate_270 (uint64 value)
{
return reflect_diag (reflect_horiz (value));
}
在上面的代码中,reflect_diag()函数仍然需要很多移位。我怀疑可以用更少的班次来实现这个功能,但我还没有找到办法做到这一点。
答案 3 :(得分:2)
如果您要快速执行此操作,则不应该反对查找表。
我将64位整数分解为N位块,并在位置选择的转置值表中查找N位块。如果选择N = 1,则需要在两个插槽的表中进行64次查找,这相对较慢。如果选择N = 64,则需要一个表和一个查找,但表格很大: - }
N = 8似乎是一个很好的妥协。您需要8个256个条目的表。代码看起来像这样:
// value to transpose is in v, a long
long r; // result
r != byte0transpose[(v>>56)&0xFF];
r != byte1transpose[(v>>48)&0xFF];
r != byte2transpose[(v>>40)&0xFF];
r != byte3transpose[(v>>32)&0xFF];
r != byte4transpose[(v>>24)&0xFF];
r != byte5transpose[(v>>16)&0xFF];
r != byte6transpose[(v>>08)&0xFF];
r != byte7transpose[(v>>00)&0xFF];
每个表包含预先计算的值,这些值在64位转置结果中“扩展”输入中的连续位。理想情况下,您可以脱机计算此值 只需初始化表条目。
如果你不关心速度,那么标准数组转置 算法会起作用;只需将64位索引,好像它是一个位数组。
我有一种潜在的怀疑,一个人可能能够使用计算换位 有点笨拙的黑客。
答案 4 :(得分:2)
要扩展我对Ira的答案的评论,您可以使用:
#define ROT_BIT_0(X) X, (X)|0x1UL
#define ROT_BIT_1(X) ROT_BIT_0(X), ROT_BIT_0((X) | 0x100UL)
#define ROT_BIT_2(X) ROT_BIT_1(X), ROT_BIT_1((X) | 0x10000UL)
#define ROT_BIT_3(X) ROT_BIT_2(X), ROT_BIT_2((X) | 0x1000000UL)
#define ROT_BIT_4(X) ROT_BIT_3(X), ROT_BIT_3((X) | 0x100000000UL)
#define ROT_BIT_5(X) ROT_BIT_4(X), ROT_BIT_4((X) | 0x10000000000UL)
#define ROT_BIT_6(X) ROT_BIT_5(X), ROT_BIT_5((X) | 0x1000000000000UL)
#define ROT_BIT_7(X) ROT_BIT_6(X), ROT_BIT_6((X) | 0x100000000000000UL)
static unsigned long rot90[256] = { ROT_BIT_7(0) };
unsigned long rotate90(unsigned long v)
{
unsigned long r = 0;
r |= rot90[(v>>56) & 0xff];
r |= rot90[(v>>48) & 0xff] << 1;
r |= rot90[(v>>40) & 0xff] << 2;
r |= rot90[(v>>32) & 0xff] << 3;
r |= rot90[(v>>24) & 0xff] << 4;
r |= rot90[(v>>16) & 0xff] << 5;
r |= rot90[(v>>8) & 0xff] << 6;
r |= rot90[v & 0xff] << 7;
return r;
}
当然,这取决于'无符号长'是64位,并且旋转假定 这些位是行主要顺序,msb是右上角,这个问题似乎就是这样......
答案 5 :(得分:2)
使用IA32 SIMD非常容易,有一个方便的操作码可以从64位值中提取每八位(这是使用DevStudio 2005编写的):
char
source [8] = {0, 0, 0, 0, 0, 0, 0, 0xd0},
dest [8];
__asm
{
mov ch,3
movq xmm0,qword ptr [source]
Rotate2:
lea edi,dest
mov cl,8
Rotate1:
pmovmskb eax,xmm0
psllq xmm0,1
stosb
dec cl
jnz Rotate1
movq xmm0,qword ptr [dest]
dec ch
jnz Rotate2
}
它将数据旋转三次(-270度),因为+90有点棘手(需要更多考虑)
答案 6 :(得分:0)
如果你把它视为一个二维数组,那么你有解决方案吗? 只需将行设为新列即可。 第一行是最后一列,第二行是前一列,依此类推。
至少在视觉上看起来像是你的解决方案。
答案 7 :(得分:0)
可能是那样的
for(int i = 0; i < 8; i++)
{
for(int j = 0; j < 8; j++)
{
new_image[j*8+8-i] = image[i*8+j];
}
}
答案 8 :(得分:0)
如果if-powered loop是可以接受的,则bit的公式很简单:
8>>Column - Row - 1
列和行是0索引。
这为您提供了这种映射:
7 15 23 31 39 47 55 63
6 14 22 ...
5 ...
4 ...
3 ...
2 ...
1 ...
0 8 16 24 32 40 48 54