簡體   English   中英

反轉位數組中的位順序

[英]Reverse the order of bits in a bit array

我有一個很長的位序列存儲在一個無符號長整數數組中,就像這樣

struct bit_array
{
    int size; /* nr of bits */
    unsigned long *array; /* the container that stores bits */
}

我試圖設計一種算法來反轉*數組中的位順序。 問題:

  • size可以是任何東西,即不一定是8或32等的倍數,因此輸入數組中的第一位可以在輸出數組中的unsigned long內的任何位置結束;
  • 算法應該是平台無關的,即適用於任何sizeof(unsigned long)

代碼,偽代碼,算法描述等 - 比bruteforce(“一點一滴”)方法更好的方法是受歡迎的。

我最喜歡的解決方案是填充一個在單個字節上進行位反轉的查找表(因此是256個字節的條目)。

您將表應用於輸入操作數的1到4個字節,並使用交換。 如果大小不是8的倍數,則需要通過最終右移來調整。

這可以很好地擴展到更大的整數。

例:

11 10010011 00001010 -> 01010000 11001001 11000000 -> 01 01000011 00100111

要將數字拆分為可移植的字節,您需要使用按位屏蔽/移位; 將結構或字節數組映射到整數可以使其更有效。

對於粗暴的性能,您可以考慮一次最多映射16位,但這看起來不太合理。

我喜歡查找表的想法。 它仍然是log(n)組位技巧的典型任務,可能非常快。 喜歡:

unsigned long reverseOne(unsigned long x) {
  x = ((x & 0xFFFFFFFF00000000) >> 32) | ((x & 0x00000000FFFFFFFF) << 32);
  x = ((x & 0xFFFF0000FFFF0000) >> 16) | ((x & 0x0000FFFF0000FFFF) << 16);
  x = ((x & 0xFF00FF00FF00FF00) >> 8)  | ((x & 0x00FF00FF00FF00FF) << 8);
  x = ((x & 0xF0F0F0F0F0F0F0F0) >> 4)  | ((x & 0x0F0F0F0F0F0F0F0F) << 4);
  x = ((x & 0xCCCCCCCCCCCCCCCC) >> 2)  | ((x & 0x3333333333333333) << 2);
  x = ((x & 0xAAAAAAAAAAAAAAAA) >> 1)  | ((x & 0x5555555555555555) << 1);
  return x;
}

根本的想法是,當我們的目標是顛倒某個序列的順序時,我們可以交換該序列的頭部和尾部,然后分別反轉每一半(這里通過遞歸地對每一半應用相同的過程來完成)。

這是一個更便攜的版本,支持4,8,16或32字節的unsigned long寬度。

#include <limits.h>

#define ones32 0xFFFFFFFFUL
#if (ULONG_MAX >> 128)
#define fill32(x) (x|(x<<32)|(x<<64)|(x<<96)|(x<<128)|(x<<160)|(x<<192)|(x<<224))
#define patt128 (ones32|(ones32<<32)|(ones32<<64) |(ones32<<96))
#define patt64  (ones32|(ones32<<32)|(ones32<<128)|(ones32<<160))
#define patt32  (ones32|(ones32<<64)|(ones32<<128)|(ones32<<192))
#else
#if (ULONG_MAX >> 64)
#define fill32(x) (x|(x<<32)|(x<<64)|(x<<96))
#define patt64  (ones32|(ones32<<32))
#define patt32  (ones32|(ones32<<64))
#else
#if (ULONG_MAX >> 32)
#define fill32(x) (x|(x<<32))
#define patt32  (ones32)
#else
#define fill32(x) (x)
#endif
#endif
#endif

unsigned long reverseOne(unsigned long x) {
#if (ULONG_MAX >> 32)
#if (ULONG_MAX >> 64)
#if (ULONG_MAX >> 128)
  x = ((x & ~patt128) >> 128) | ((x & patt128) << 128);
#endif
  x = ((x & ~patt64) >> 64) | ((x & patt64) << 64);
#endif
  x = ((x & ~patt32) >> 32) | ((x & patt32) << 32);
#endif
  x = ((x & fill32(0xffff0000UL)) >> 16) | ((x & fill32(0x0000ffffUL)) << 16);
  x = ((x & fill32(0xff00ff00UL)) >> 8)  | ((x & fill32(0x00ff00ffUL)) << 8);
  x = ((x & fill32(0xf0f0f0f0UL)) >> 4)  | ((x & fill32(0x0f0f0f0fUL)) << 4);
  x = ((x & fill32(0xccccccccUL)) >> 2)  | ((x & fill32(0x33333333UL)) << 2);
  x = ((x & fill32(0xaaaaaaaaUL)) >> 1)  | ((x & fill32(0x55555555UL)) << 1);
  return x;
}

這里可以找到的相關主題的集合中,單個數組條目的位可以如下反轉。

unsigned int v;     // input bits to be reversed
unsigned int r = v; // r will be reversed bits of v; first get LSB of v
int s = sizeof(v) * CHAR_BIT - 1; // extra shift needed at end

for (v >>= 1; v; v >>= 1)
{   
  r <<= 1;
  r |= v & 1;
  s--;
}
r <<= s; // shift when v's highest bits are zero

之后可以通過重新排列各個位置來完成整個陣列的反轉。

您必須定義unsigned long的位順序。 您可以假設位n對應於array[x] & (1 << n)但這需要指定。 如果是這樣,如果要使用數組作為字節而不是unsigned long,則需要處理字節順序(小端或大端)。

我肯定會首先實施蠻力並測量速度是否是一個問題。 如果在大型陣列上沒有大量使用,則無需浪費時間嘗試優化它。 優化版本可能很難正確實現。 無論如何最終都會嘗試,可以使用強力版本來驗證測試值的正確性,並對優化版本的速度進行基准測試。

大小不是sizeof(long)倍數這一事實是問題中最難的部分。 這可能導致大量的位移。

但是,如果您可以引入新的struct成員,則不必這樣做:

struct bit_array
{
    int size; /* nr of bits */
    int offset; /* First bit position */
    unsigned long *array; /* the container that stores bits */
}

偏移量會告訴您在數組開頭要忽略多少位。

那你只需要做以下步驟:

  1. 反向數組元素。
  2. 交換每個元素的位。 在其他答案中有許多黑客,但您的編譯器也可能提供內在函數來執行更少的指令(如某些ARM內核上的RBIT指令)。
  3. 計算新的起始偏移量。 這等於最后一個元素的未使用位。

我會將問題分成兩部分。

首先,我會忽略這樣一個事實,即使用的位數不是32的倍數。我會使用給定方法之一來交換整個數組。

偽代碼:

for half the longs in the array:
    take the first longword;
    take the last longword;
    swap the bits in the first longword
    swap the bits in the last longword;

    store the swapped first longword into the last location;
    store the swapped last longword into the first location;

然后修復一個事實,即前幾位(調用數字n )實際上是longs末尾的垃圾位:

for all of the longs in the array:
    split the value in the leftmost n bits and the rest;
    store the leftmost n bits into the righthand part of the previous word;
    shift the rest bits to the left over n positions (making the rightmost n bits zero);
    store them back;

您可以嘗試將其折疊成整個陣列中的一個傳遞。 像這樣的東西:

for half the longs in the array:
    take the first longword;
    take the last longword;
    swap the bits in the first longword
    swap the bits in the last longword;

    split both value in the leftmost n bits and the rest;

    for the new first longword:
        store the leftmost n bits into the righthand side of the previous word;
        store the remaining bits into the first longword, shifted left;

    for the new last longword:
        remember the leftmost n bits for the next iteration;
        store the remembered leftmost n bits, combined with the remaining bits, into the last longword;

    store the swapped first longword into the last location;
    store the swapped last longword into the first location;

我從這里的邊緣情況(第一個和最后一個長字)中抽象出來,你可能需要根據每個長字內部的位排序方式來反轉移位方向。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM