实现以下目标最有效的算法是什么:
0010 0000 => 0000 0100
从MSB->LSB转换为LSB->MSB。所有位都必须反转;也就是说,这不是字节交换。
实现以下目标最有效的算法是什么:
0010 0000 => 0000 0100
从MSB->LSB转换为LSB->MSB。所有位都必须反转;也就是说,这不是字节交换。
当前回答
好吧,这肯定不会是一个像Matt J的答案,但希望它仍然有用。
size_t reverse(size_t n, unsigned int bytes)
{
__asm__("BSWAP %0" : "=r"(n) : "0"(n));
n >>= ((sizeof(size_t) - bytes) * 8);
n = ((n & 0xaaaaaaaaaaaaaaaa) >> 1) | ((n & 0x5555555555555555) << 1);
n = ((n & 0xcccccccccccccccc) >> 2) | ((n & 0x3333333333333333) << 2);
n = ((n & 0xf0f0f0f0f0f0f0f0) >> 4) | ((n & 0x0f0f0f0f0f0f0f0f) << 4);
return n;
}
这与Matt的最佳算法完全相同,除了有一个叫做BSWAP的小指令,它交换64位数字的字节(而不是位)。所以b7 b6 b5 b4 b3 b2 b1 b0变成了b0 b1 b2 b3 b4 b5 b6 b7。由于我们处理的是32位数字,所以需要将字节交换后的数字向下移动32位。这只留给我们交换每个字节的8位的任务,这是完成的,瞧!我们做完了。
计时:在我的机器上,Matt的算法每次试验只需0.52秒。我的每次试验大约耗时0.42秒。我认为快20%还不错。
如果你担心指令BSWAP的可用性,维基百科列出了指令BSWAP是与1989年推出的80846一起添加的。值得注意的是,维基百科还指出,这条指令只适用于32位寄存器,这显然不是我的机器上的情况,它只适用于64位寄存器。
此方法同样适用于任何整型数据类型,因此可以通过传递所需的字节数来简单地推广该方法:
size_t reverse(size_t n, unsigned int bytes)
{
__asm__("BSWAP %0" : "=r"(n) : "0"(n));
n >>= ((sizeof(size_t) - bytes) * 8);
n = ((n & 0xaaaaaaaaaaaaaaaa) >> 1) | ((n & 0x5555555555555555) << 1);
n = ((n & 0xcccccccccccccccc) >> 2) | ((n & 0x3333333333333333) << 2);
n = ((n & 0xf0f0f0f0f0f0f0f0) >> 4) | ((n & 0x0f0f0f0f0f0f0f0f) << 4);
return n;
}
它可以被称为:
n = reverse(n, sizeof(char));//only reverse 8 bits
n = reverse(n, sizeof(short));//reverse 16 bits
n = reverse(n, sizeof(int));//reverse 32 bits
n = reverse(n, sizeof(size_t));//reverse 64 bits
编译器应该能够优化掉额外的形参(假设编译器内联了函数),对于sizeof(size_t)情况,右移将被完全删除。注意,如果传递sizeof(char), GCC至少不能删除BSWAP和右移。
其他回答
假设你有一个比特数组,怎么样: 1. 从MSB开始,将比特一个一个地推入堆栈。 2. 从这个堆栈弹出位到另一个数组(如果你想节省空间,也可以是同一个数组),将第一个弹出位放入MSB,然后从那里继续到较低的有效位。
Stack stack = new Stack();
Bit[] bits = new Bit[] { 0, 0, 1, 0, 0, 0, 0, 0 };
for (int i = 0; i < bits.Length; i++)
{
stack.push(bits[i]);
}
for (int i = 0; i < bits.Length; i++)
{
bits[i] = stack.pop();
}
对于喜欢递归的人来说,这是另一个解决方案。
这个想法很简单。 将输入除以一半并交换两部分,继续直到达到单个位。
Illustrated in the example below.
Ex : If Input is 00101010 ==> Expected output is 01010100
1. Divide the input into 2 halves
0010 --- 1010
2. Swap the 2 Halves
1010 0010
3. Repeat the same for each half.
10 -- 10 --- 00 -- 10
10 10 10 00
1-0 -- 1-0 --- 1-0 -- 0-0
0 1 0 1 0 1 0 0
Done! Output is 01010100
这里有一个递归函数来求解。(注意,我使用了unsigned int,所以它可以用于sizeof(unsigned int)*8位的输入。
递归函数有两个参数-需要位的值 要反转的值和值中的比特数。
int reverse_bits_recursive(unsigned int num, unsigned int numBits)
{
unsigned int reversedNum;;
unsigned int mask = 0;
mask = (0x1 << (numBits/2)) - 1;
if (numBits == 1) return num;
reversedNum = reverse_bits_recursive(num >> numBits/2, numBits/2) |
reverse_bits_recursive((num & mask), numBits/2) << numBits/2;
return reversedNum;
}
int main()
{
unsigned int reversedNum;
unsigned int num;
num = 0x55;
reversedNum = reverse_bits_recursive(num, 8);
printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);
num = 0xabcd;
reversedNum = reverse_bits_recursive(num, 16);
printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);
num = 0x123456;
reversedNum = reverse_bits_recursive(num, 24);
printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);
num = 0x11223344;
reversedNum = reverse_bits_recursive(num,32);
printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);
}
输出如下:
Bit Reversal Input = 0x55 Output = 0xaa
Bit Reversal Input = 0xabcd Output = 0xb3d5
Bit Reversal Input = 0x123456 Output = 0x651690
Bit Reversal Input = 0x11223344 Output = 0x22cc4488
对于其他可能遇到这个问题的网络搜索者,这里有一个总结(针对C和JavaScript)。
对于JavaScript的完整解决方案,我们可以首先生成表:
const BIT_REVERSAL_TABLE = new Array(256)
for (var i = 0; i < 256; ++i) {
var v = i, r = i, s = 7;
for (v >>>= 1; v; v >>>= 1) {
r <<= 1;
r |= v & 1;
--s;
}
BIT_REVERSAL_TABLE[i] = (r << s) & 0xff;
}
这给了我们BIT_REVERSAL_TABLE,这是@MattJ发布的:
const BIT_REVERSAL_TABLE = new Uint8Array([
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
])
8位、16位和32位无符号整数的算法可以在这里找到:
function reverseBits8(n) {
return BIT_REVERSAL_TABLE[n]
}
function reverseBits16(n) {
return (BIT_REVERSAL_TABLE[(n >> 8) & 0xff] |
BIT_REVERSAL_TABLE[n & 0xff] << 8)
}
function reverseBits32(n) {
return (BIT_REVERSAL_TABLE[n & 0xff] << 24) |
(BIT_REVERSAL_TABLE[(n >>> 8) & 0xff] << 16) |
(BIT_REVERSAL_TABLE[(n >>> 16) & 0xff] << 8) |
BIT_REVERSAL_TABLE[(n >>> 24) & 0xff];
}
注意,32位版本不能在JavaScript中工作(必须转换为使用bigint,这很简单),但应该可以在64位语言中工作:
log8(0b11000100)
log16(0b1110001001001100)
log32(0b11110010111110111100110010101011)
// 0b11000100 => 0b00100011
// 0b1110001001001100 => 0b0011001001000111
// doesn't work in JS it seems:
// 0b11110010111110111100110010101011 => 0b0-101010110011000010000010110001
function log8(n) {
console.log(`${bits(n, 8)} => ${bits(reverseBits8(n), 8)}`)
}
function log16(n) {
console.log(`${bits(n, 16)} => ${bits(reverseBits16(n), 16)}`)
}
function log32(n) {
console.log(`${bits(n, 32)} => ${bits(reverseBits32(n), 32)}`)
}
function bits(n, size) {
return `0b${n.toString(2).padStart(size, '0')}`
}
注意:这个解决方案适用于JavaScript的32位:
function reverseBits32(n) {
let res = 0;
for (let i = 0; i < 32; i++) {
res = (res << 1) + (n & 1);
n = n >>> 1;
}
return res >>> 0;
}
所有3个基于表格的解决方案都可以在C中正常工作。下面是一个粗略的C版本:
#include <stdlib.h>
static uint8_t* BIT_REVERSAL_TABLE;
uint8_t*
make_bit_reversal_table() {
uint8_t *table = malloc(256 * sizeof(uint8_t));
uint8_t i;
for (i = 0; i < 256 ; ++i) {
uint8_t v = i;
uint8_t r = i;
uint8_t s = 7;
for (v = v >> 1; v; v = v >> 1) {
r <<= 1;
r |= v & 1;
--s;
}
table[i] = (r << s) & 0xff;
}
return table;
}
uint8_t
reverse_bits_8(uint8_t n) {
return BIT_REVERSAL_TABLE[n];
}
uint16_t
reverse_bits_16(uint16_t n)
{
return (BIT_REVERSAL_TABLE[(n >> 8) & 0xff]
| BIT_REVERSAL_TABLE[n & 0xff] << 8);
}
uint32_t
reverse_bits_32(uint32_t n) {
return (BIT_REVERSAL_TABLE[n & 0xff] << 24)
| (BIT_REVERSAL_TABLE[(n >> 8) & 0xff] << 16)
| (BIT_REVERSAL_TABLE[(n >> 16) & 0xff] << 8)
| BIT_REVERSAL_TABLE[(n >> 24) & 0xff];
}
int
main(void) {
BIT_REVERSAL_TABLE = make_bit_reversal_table();
return 0;
}
我的简单解决方案
BitReverse(IN)
OUT = 0x00;
R = 1; // Right mask ...0000.0001
L = 0; // Left mask 1000.0000...
L = ~0;
L = ~(i >> 1);
int size = sizeof(IN) * 4; // bit size
while(size--){
if(IN & L) OUT = OUT | R; // start from MSB 1000.xxxx
if(IN & R) OUT = OUT | L; // start from LSB xxxx.0001
L = L >> 1;
R = R << 1;
}
return OUT;
我认为下面是我所知道的最简单的方法。MSB是输入,LSB是“反向”输出:
unsigned char rev(char MSB) {
unsigned char LSB=0; // for output
_FOR(i,0,8) {
LSB= LSB << 1;
if(MSB&1) LSB = LSB | 1;
MSB= MSB >> 1;
}
return LSB;
}
// It works by rotating bytes in opposite directions.
// Just repeat for each byte.