我刚刚开始学习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));
}
答案 0 :(得分:2)
开始的简单方法是考虑如何在单个值中旋转位。假设您有x
,这是一个N
位值,您希望将其旋转k
个位置。 (我只会看向上/向左旋转,很容易转换为向下/向右)。首先要注意的是,如果k
= N
,则x不变。因此,在旋转之前,我们希望减少k
模N
以避免完全旋转。
接下来我们应该观察到,在旋转期间,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;
}