旋转C中的位数组

时间:2010-09-18 16:25:57

标签: c performance char rotation buffer

我刚刚开始学习C,而且我想要编写一些代码时遇到一些问题。

基本上我有一个这样的结构,它是一个位数组,包含数组中的位数,以及一个指向存储位的字符缓冲区的指针。

旋转位数组的策略是简单地获取旋转次数(修改长度以避免完全旋转)并使用简单的反转算法来旋转数组。

编辑:

但是,我的问题是我想旋转实际缓冲区中的位。

我还希望能够在整个位数组中旋转位的子序列。因此,对于1101101,我可能想要从索引2开始并在索引5结束的子序列旋转(从左侧开始索引)。我不完全确定如何使用我的char缓冲区来执行此操作。

感谢您的帮助!

struct arrayBits{
   size_t numBits;
   char *buf;
 }

buf数组保存8位整数,而不是我之前提到的bool。

我可以访问和设置单个位的方法只是索引到保存我想要的位的字节(因此对于数组ab,ab-> buf [index_of_desired_bit / 8]然后执行一些按位运算因性能原因而改变价值。

编辑:感谢大家提出的所有建议。我看了所有这些,我相信我更了解代码。这是我最后编写的代码,但是,我认为它存在一些问题。

虽然它通过了我的一些基本测试用例,但它似乎在98775位大小的随机填充上运行得有点太快了。我的意思是,在某些情况下我的代码完全失败并崩溃了吗?测试用例在完整的98775位阵列上连续进行三次旋转。一次旋转-98775/4(< - 这是一个size_t,所以环绕?),一次旋转98775/4,然后最后一次旋转98775/2。

我有什么遗漏或者我没有看到的问题吗?

/*Reverse a bit array*/
/*v1.1: basic bit reversal w/o temp variable*/
static void arrayReversal(bitarray_t *ba, size_t begin, size_t end){
while(begin < end)
{
    bitarray_set(ba, begin, (bitarray_get(ba, begin) ^ bitarray_get(ba, end))); /*x = x ^ y*/
    bitarray_set(ba,  end,   (bitarray_get(ba, begin) ^ bitarray_get(ba, end))); /*y = x ^ y*/
    bitarray_set(ba, begin, (bitarray_get(ba, begin) ^ bitarray_get(ba, end))); /*x = x ^ y*/
    begin++;
    end--;
}

}


/*Main Rotation Routine*/
void bitarray_rotate(bitarray_t *ba, size_t bit_off, size_t bit_len, ssize_t bit_right_amount) {
assert(bit_off + bit_len <= ba->bit_sz);
assert(bit_off + bit_len > 0);

if(bit_off + bit_len > ba->bit_sz || bit_off + bit_len < 0) 
    {
        printf("\nError: Indices out of bounds\n");
        return;
    }

/*Find index to split bitarray on*/
if(bit_len == 0) return; //Rotate only 1 bit i.e. no rotation

size_t reversal_index;
reversal_index  = modulo(-bit_right_amount, bit_len);

if(reversal_index == 0) return; //No rotation to do

/*3 bit string reversals*/

assert(reversal_index - 1 + bit_off < ba->bit_sz);
/* Reverse A*/
arrayReversal(ba, bit_off, reversal_index - 1 + bit_off); 
assert(reversal_index + bit_off < ba->bit_sz);

/*Reverse B*/
arrayReversal(ba, reversal_index + bit_off, (bit_off + bit_len - 1));

/*Reverse ArBr*/    
arrayReversal(ba, bit_off, (bit_off + bit_len -1));

}

5 个答案:

答案 0 :(得分:2)

开始的简单方法是考虑如何在单个值中旋转位。假设您有x,这是一个N位值,您希望将其旋转k个位置。 (我只会看向上/向左旋转,很容易转换为向下/向右)。首先要注意的是,如果k = N,则x不变。因此,在旋转之前,我们希望减少kN以避免完全旋转。

接下来我们应该观察到,在旋转期间,k高位将移动到值的底部,而低N-k位将向上移动k个位置。这与前k - 位向下移动N-k个位置相同。我们这样说的原因是C有移位算子,但不是旋转。

在psuedo-C中,我们可以说:

#define N sizeof(type)*8
type rotate(type x, int k) {
  type lower = x & ((1 << (N-k)) - 1);
  type upper = x >> (N-k) & ((1 <<k)-1);
  return upper | lower;
}

这将处理简单的原子情况,只需在适当的情况下用char或int替换type。如果type未签名,则不需要upper值的掩码。

接下来要考虑的是在一组值中旋转。如果您认为上面的代码将两半的值粘合在一起,那么对于更复杂的情况,我们需要将数组中不同位置的上下部分粘合在一起。如果k很小,那么这些地方在数组中是相邻的,但是当k>N我们正在旋转多个中间词时。

特别是如果我们向上移动k个位置,那么我们会从数组中的k/N字移开位,N位可以跨越floor(k/N)和{ {1}}数组中的位置。好的,现在我们已经准备好把它们放在一起了。对于数组中的每个字,新的ceil(k/N)位将是N-(k mod N)个字的低位,而新的低floor(k/N)位将是(k mod N)的高位。话语。

在同一个伪C中(即用您正在使用的内容替换ceil(k/N)),我们可以说:

type

无论如何,这比我打算写的要多得多,所以我现在就放弃了。将它转换为在单个数组上就地工作的形式应该很容易,但是您可能希望首先修复#define N sizeof(type)*8 #define ARR_SIZE ... type rotate(type *x, int k,type *out) { int r = k % N; int upperOff = k/N; int lowerOff = (k+N-1)/N; for(int i=0; i<ARR_SIZE; i++) { int lowerPos = (i + ARR_SIZE - lowerOff) % ARR_SIZE int upperPos = (i + ARR_SIZE - upperOff) % ARR_SIZE type lower = x[lowerPos] & ((1 << (N-k)) - 1) type upper = x[upperPos] >> (N-k) & ((1 <<k)-1) out[i] = upper | lower; } } 的类型和范围,以便绑定临时存储。

如果你在这方面有任何问题,那么一个地方就是位图精灵图形。例如,这个旋转问题用于在8位游戏中实现滚动很多很多个月。

答案 1 :(得分:0)

我建议您使用位级操作(&gt;&gt;,&lt;&lt;,〜,&amp;,|),而不是使用int浪费空间。即便如此,使用int数组,旋转,传递左和&amp;正确的子串索引:

void rotate ( struct arrayBits a, int left , int right )
{

   int i;
   int first_bit;

   if(*( a.buf + right ) == 1) first_bit = 1; 
   else first_bit = 0;

   for( i = left+1 ; i <= right ; i++ )
   {
      *( a.buf + i )=*( a.buf + i - 1 ); 
   }

   *a.buf = first_bit;    

}

示例:

如果struct_array是010101,

rotate (struct_array,0,5); =&gt;将整个字符串1 int旋转到右侧

o / p: 101010

rotate (struct_array,2,4); =&gt;将substring 1 int旋转到右边

o / p:01 001 1

要反转位数组,请调用子串上的rotate()函数size_of_substring次。

答案 2 :(得分:0)

您不需要额外的缓冲区来旋转(仅用于输出)。 您应该为一个旋转实现一个函数并循环它,例如:(右移变化)

char *itoa2(char *s,size_t i)
{
  *s=0;
  do {
    memmove(s+1,s,strlen(s)+1);
    *s='0'+(i&1);
  } while( i>>=1 );
  return s;
}

size_t bitrotateOne(size_t i)
{
  return i>>1 | (i&1) << (sizeof i<<3)-1;
}

...

size_t i=12,num=17;
char b[129];
while( num-- )
{
  i = bitrotateOne(i);
  puts( itoa2(b,i) );
}

答案 3 :(得分:0)

我建议指向缓冲区中某个位的起始点而不是旋转的指针/偏移量。随意重载任何可能有用的运算符,运算符[]会浮现在脑海中。 rotate(n)只是偏移+ = n操作。但我发现你评论的目的是 - “但是,我的问题是我想旋转实际的缓冲区”令人困惑。

答案 4 :(得分:0)

由于您的标准非常复杂,我认为最简单的方法是逐步完成每个位并设置新阵列中的位置。你可以通过复制整个字符来加速某些操作,如果它超出了移位的位,但我想不出如何可靠地进行移位考虑所有变量,因为移位序列的开始和结束可以是在字节的中间,所以可以结束整个位。关键是在旧数组中获取新位位置:

j = (i < startBit || i >= startBit + length) ? i : 
    ((i - startBit + shiftRightCount) % length) + startBit;

代码:

#include "stdafx.h"
#include <stdlib.h>
#include <string.h>

typedef struct {
   size_t numBits;
   unsigned char *buf;
} ARRAYBITS;

// format is big endian, shiftint left 8 bits will shift all bytes to a lower index
ARRAYBITS rotateBits(ARRAYBITS *pOriginalBits, int startBit, int length, int shiftRightCount);
void setBit(unsigned char *buf, int bit, bool isSet);
bool checkBit(unsigned char *buf, int bit);
ARRAYBITS fromString(char *onesAndZeros);
char *toString(ARRAYBITS *pBits);

int _tmain(int argc, _TCHAR* argv[])
{
    char input[1024];
    ARRAYBITS bits = fromString("11110000110010101110"); // 20 bits
    ARRAYBITS bitsA = rotateBits(&bits, 0, bits.numBits, 1);
    ARRAYBITS bitsB = rotateBits(&bits, 0, bits.numBits, -1);
    ARRAYBITS bitsC = rotateBits(&bits, 6, 8, 4);
    ARRAYBITS bitsD = rotateBits(&bits, 6, 8, -2);
    ARRAYBITS bitsE = rotateBits(&bits, 6, 8, 31);
    ARRAYBITS bitsF = rotateBits(&bits, 6, 8, -31);
    printf("Starting   : %s\n", toString(&bits));
    printf("All right 1: %s\n", toString(&bitsA));
    printf("All left 1 : %s\n", toString(&bitsB));
    printf("\n");
    printf("           :       ********\n");
    printf("Starting   : %s\n", toString(&bits));
    printf("6,8,4      : %s\n", toString(&bitsC));
    printf("6,8,-2     : %s\n", toString(&bitsD));
    printf("6,8,31     : %s\n", toString(&bitsE));
    printf("6,8,-31    : %s\n", toString(&bitsF));
    gets(input);
}

ARRAYBITS rotateBits(ARRAYBITS *pOriginalBits, int startBit, int length, int shiftRightCount)
{
    // 0-8 == 1, 9-16 == 2, 17-24 == 3
    ARRAYBITS newBits;
    int i = 0, j = 0;
    int bytes = 0;
    while (shiftRightCount < 0)
        shiftRightCount += length;
    shiftRightCount = shiftRightCount % length;

    newBits.numBits = pOriginalBits->numBits;
    if (pOriginalBits->numBits <= 0)
        return newBits;

    bytes = ((pOriginalBits->numBits -1) / 8) + 1;
    newBits.buf = (unsigned char *)malloc(bytes);
    memset(newBits.buf, 0, bytes);
    for (i = 0; i < pOriginalBits->numBits; i++) {
        j = (i < startBit || i >= startBit + length) ? i : ((i - startBit + shiftRightCount) % length) + startBit;
        if (checkBit(pOriginalBits->buf, i))
        {
            setBit(newBits.buf, j, true);
        }
    }
    return newBits;
}

void setBit(unsigned char *buf, int bit, bool isSet)
{
    int charIndex = bit / 8;
    unsigned char c = 1 << (bit & 0x07);
    if (isSet)
        buf[charIndex] |= c;
    else
        buf[charIndex] &= (c ^ 255);
}

bool checkBit(unsigned char *buf, int bit)
{
    // address of char is (bit / 8), bit within char is (bit & 7)
    int index = bit / 8;
    int b = bit & 7;
    int value = 1 << b;
    return ((buf[index] & value) > 0);
}

ARRAYBITS fromString(char *onesAndZeros)
{
    int i;
    ARRAYBITS bits;
    int charCount;

    bits.numBits = strlen(onesAndZeros);
    charCount = ((bits.numBits -1) / 8) + 1;
    bits.buf = (unsigned char *)malloc(charCount);
    memset(bits.buf, 0, charCount);
    for (i = 0; i < bits.numBits; i++)
    {
        if (onesAndZeros[i] != '0')
            setBit(bits.buf, i, true);
    }
    return bits;
}

char *toString(ARRAYBITS *pBits)
{
    char *buf = (char *)malloc(pBits->numBits + 1);
    int i;
    for (i = 0; i < pBits->numBits; i++)
    {
        buf[i] = checkBit(pBits->buf, i) ? '1' : '0';
    }
    buf[i] = 0;
    return buf;
}