artificialidiot
artificialidiot

Reputation: 5379

Counting, reversed bit pattern

I am trying to find an algorithm to count from 0 to 2n-1 but their bit pattern reversed. I care about only n LSB of a word. As you may have guessed I failed.

For n=3:

000 -> 0
100 -> 4
010 -> 2
110 -> 6
001 -> 1
101 -> 5
011 -> 3
111 -> 7

You get the idea.

Answers in pseudo-code is great. Code fragments in any language are welcome, answers without bit operations are preferred.

Please don't just post a fragment without even a short explanation or a pointer to a source.

Edit: I forgot to add, I already have a naive implementation which just bit-reverses a count variable. In a sense, this method is not really counting.

Upvotes: 8

Views: 2836

Answers (13)

Marek Basovník
Marek Basovník

Reputation: 147

Let assume number 1110101 and our task is to find next one.

1) Find zero on highest position and mark position as index.

11101010 (4th position, so index = 4)

2) Set to zero all bits on position higher than index.

00001010

3) Change founded zero from step 1) to '1'

00011010

That's it. This is by far the fastest algorithm since most of cpu's has instructions to achieve this very efficiently. Here is a C++ implementation which increment 64bit number in reversed patern.

#include <intrin.h>
unsigned __int64 reversed_increment(unsigned __int64 number) 
{
  unsigned long index, result;
  _BitScanReverse64(&index, ~number); // returns index of the highest '1' on bit-reverse number (trick to find the highest '0')
  result = _bzhi_u64(number, index); // set to '0' all bits at number higher than index position
  result |= (unsigned __int64) 1 << index; // changes to '1' bit on index position
  return result;
}

Its not hit your requirements to have "no bits" operations, however i fear there is now way how to achieve something similar without them.

Upvotes: 0

eXtranium
eXtranium

Reputation: 1

Edit: Of course original poster's question was about to do increment by (reversed) one, which makes things more simple than adding two random values. So nwellnhof's answer contains the algorithm already.


Summing two bit-reversal values

Here is one solution in php:

function RevSum ($a,$b) {

    // loop until our adder, $b, is zero
    while ($b) {

        // get carry (aka overflow) bit for every bit-location by AND-operation
        // 0 + 0 --> 00   no overflow, carry is "0"
        // 0 + 1 --> 01   no overflow, carry is "0"
        // 1 + 0 --> 01   no overflow, carry is "0"
        // 1 + 1 --> 10   overflow! carry is "1"

        $c = $a & $b;


        // do 1-bit addition for every bit location at once by XOR-operation
        // 0 + 0 --> 00   result = 0
        // 0 + 1 --> 01   result = 1
        // 1 + 0 --> 01   result = 1
        // 1 + 1 --> 10   result = 0 (ignored that "1", already taken care above)

        $a ^= $b;


        // now: shift carry bits to the next bit-locations to be added to $a in
        // next iteration.
        // PHP_INT_MAX here is used to ensure that the most-significant bit of the
        // $b will be cleared after shifting. see link in the side note below.

        $b = ($c >> 1) & PHP_INT_MAX;

    }

    return $a;
}

Side note: See this question about shifting negative values.

And as for test; start from zero and increment value by 8-bit reversed one (10000000):

$value = 0;
$add = 0x80;    // 10000000 <-- "one" as bit reversed

for ($count = 20; $count--;) {      // loop 20 times
    printf("%08b\n", $value);       // show value as 8-bit binary
    $value = RevSum($value, $add);  // do addition
}

... will output:

 00000000
 10000000
 01000000
 11000000
 00100000
 10100000
 01100000
 11100000
 00010000
 10010000
 01010000
 11010000
 00110000
 10110000
 01110000
 11110000
 00001000
 10001000
 01001000
 11001000

Upvotes: 0

nwellnhof
nwellnhof

Reputation: 33658

Here's a solution from my answer to a different question that computes the next bit-reversed index without looping. It relies heavily on bit operations, though.

The key idea is that incrementing a number simply flips a sequence of least-significant bits, for example from nnnn0111 to nnnn1000. So in order to compute the next bit-reversed index, you have to flip a sequence of most-significant bits. If your target platform has a CTZ ("count trailing zeros") instruction, this can be done efficiently.

Example in C using GCC's __builtin_ctz:

void iter_reversed(unsigned bits) {
    unsigned n = 1 << bits;

    for (unsigned i = 0, j = 0; i < n; i++) {
        printf("%x\n", j);

        // Compute a mask of LSBs.
        unsigned mask = i ^ (i + 1);
        // Length of the mask.
        unsigned len = __builtin_ctz(~mask);
        // Align the mask to MSB of n.
        mask <<= bits - len;
        // XOR with mask.
        j ^= mask;
    }
}

Without a CTZ instruction, you can also use integer division:

void iter_reversed(unsigned bits) {
    unsigned n = 1 << bits;

    for (unsigned i = 0, j = 0; i < n; i++) {
        printf("%x\n", j);

        // Find least significant zero bit.
        unsigned bit = ~i & (i + 1);
        // Using division to bit-reverse a single bit.
        unsigned rev = (n / 2) / bit;
        // XOR with mask.
        j ^= (n - 1) & ~(rev - 1);
    }
}

Upvotes: 4

vaishuraj
vaishuraj

Reputation: 1

When you reverse 0 to 2^n-1 but their bit pattern reversed, you pretty much cover the entire 0-2^n-1 sequence

Sum = 2^n * (2^n+1)/2

O(1) operation. No need to do bit reversals

Upvotes: 0

Svante
Svante

Reputation: 51531

With n as your power of 2 and x the variable you want to step:

(defun inv-step (x n)       ; the following is a function declaration
  "returns a bit-inverse step of x, bounded by 2^n"    ; documentation
  (do ((i (expt 2 (- n 1))  ; loop, init of i
          (/ i 2))          ; stepping of i
       (s x))               ; init of s as x
      ((not (integerp i))   ; breaking condition
       s)                   ; returned value if all bits are 1 (is 0 then)
    (if (< s i)                         ; the loop's body: if s < i
        (return-from inv-step (+ s i))  ;     -> add i to s and return the result
        (decf s i))))                   ;     else: reduce s by i

I commented it thoroughly as you may not be familiar with this syntax.

edit: here is the tail recursive version. It seems to be a little faster, provided that you have a compiler with tail call optimization.

(defun inv-step (x n)
  (let ((i (expt 2 (- n 1))))
    (cond ((= n 1)
           (if (zerop x) 1 0))         ; this is really (logxor x 1)                                                 
          ((< x i)
           (+ x i))
          (t
           (inv-step (- x i) (- n 1))))))

Upvotes: 0

Alexander
Alexander

Reputation: 9370

How about adding 1 to the most significant bit, then carrying to the next (less significant) bit, if necessary. You could speed this up by operating on bytes:

  1. Precompute a lookup table for counting in bit-reverse from 0 to 256 (00000000 -> 10000000, 10000000 -> 01000000, ..., 11111111 -> 00000000).
  2. Set all bytes in your multi-byte number to zero.
  3. Increment the most significant byte using the lookup table. If the byte is 0, increment the next byte using the lookup table. If the byte is 0, increment the next byte...
  4. Go to step 3.

Upvotes: 0

Evan Teran
Evan Teran

Reputation: 90533

Here's a solution which doesn't actually try to do any addition, but exploits the on/off pattern of the seqence (most sig bit alternates every time, next most sig bit alternates every other time, etc), adjust n as desired:

#define FLIP(x, i) do { (x) ^= (1 << (i)); } while(0)

int main() {
    int n   = 3;
    int max = (1 << n);
    int x   = 0;

    for(int i = 1; i <= max; ++i) {
        std::cout << x << std::endl;
        /* if n == 3, this next part is functionally equivalent to this:
         *
         * if((i % 1) == 0) FLIP(x, n - 1);
         * if((i % 2) == 0) FLIP(x, n - 2);
         * if((i % 4) == 0) FLIP(x, n - 3);
         */
        for(int j = 0; j < n; ++j) {
            if((i % (1 << j)) == 0) FLIP(x, n - (j + 1));
        }                       
    }
}

Upvotes: 0

Andru Luvisi
Andru Luvisi

Reputation: 25338

Here's an answer in Perl. You don't say what comes after the all ones pattern, so I just return zero. I took out the bitwise operations so that it should be easy to translate into another language.

sub reverse_increment {
  my($n, $bits) = @_;

  my $carry = 2**$bits;
  while($carry > 1) {
    $carry /= 2;
    if($carry > $n) {
      return $carry + $n;
    } else {
      $n -= $carry;
    }
  }
  return 0;
}

Upvotes: 0

Alnitak
Alnitak

Reputation: 339985

This is, I think easiest with bit operations, even though you said this wasn't preferred

Assuming 32 bit ints, here's a nifty chunk of code that can reverse all of the bits without doing it in 32 steps:

 unsigned int i;
 i = (i & 0x55555555) <<  1 | (i & 0xaaaaaaaa) >>  1;
 i = (i & 0x33333333) <<  2 | (i & 0xcccccccc) >>  2;
 i = (i & 0x0f0f0f0f) <<  4 | (i & 0xf0f0f0f0) >>  4;
 i = (i & 0x00ff00ff) <<  8 | (i & 0xff00ff00) >>  8;
 i = (i & 0x0000ffff) << 16 | (i & 0xffff0000) >> 16;
 i >>= (32 - n);

Essentially this does an interleaved shuffle of all of the bits. Each time around half of the bits in the value are swapped with the other half.

The last line is necessary to realign the bits so that bin "n" is the most significant bit.

Shorter versions of this are possible if "n" is <= 16, or <= 8

Upvotes: 3

Bill K
Bill K

Reputation: 62789

This solution was originally in binary and converted to conventional math as the requester specified.

It would make more sense as binary, at least the multiply by 2 and divide by 2 should be << 1 and >> 1 for speed, the additions and subtractions probably don't matter one way or the other.

If you pass in mask instead of nBits, and use bitshifting instead of multiplying or dividing, and change the tail recursion to a loop, this will probably be the most performant solution you'll find since every other call it will be nothing but a single add, it would only be as slow as Alnitak's solution once every 4, maybe even 8 calls.

int incrementBizarre(int initial, int nBits)
    // in the 3 bit example, this should create 100
    mask=2^(nBits-1)
    // This should only return true if the first (least significant) bit is not set
    // if initial is 011 and mask is 100
    //                3               4, bit is not set
    if(initial < mask)
        // If it was not, just set it and bail.
        return initial+ mask // 011 (3) + 100 (4) = 111 (7)
    else
        // it was set, are we at the most significant bit yet?
        // mask 100 (4) / 2 = 010 (2), 001/2 = 0 indicating overflow
        if(mask / 2) > 0
            // No, we were't, so unset it (initial-mask) and increment the next bit
            return incrementBizarre(initial - mask, mask/2)
        else
            // Whoops we were at the most significant bit.  Error condition
            throw new OverflowedMyBitsException()

Wow, that turned out kinda cool. I didn't figure in the recursion until the last second there.

It feels wrong--like there are some operations that should not work, but they do because of the nature of what you are doing (like it feels like you should get into trouble when you are operating on a bit and some bits to the left are non-zero, but it turns out you can't ever be operating on a bit unless all the bits to the left are zero--which is a very strange condition, but true.

Example of flow to get from 110 to 001 (backwards 3 to backwards 4):

mask 100 (4), initial 110 (6); initial < mask=false; initial-mask = 010 (2), now try on the next bit
mask 010 (2), initial 010 (2); initial < mask=false; initial-mask = 000 (0), now inc the next bit
mask 001 (1), initial 000 (0); initial < mask=true;  initial + mask = 001--correct answer

Upvotes: 2

Assaf Lavie
Assaf Lavie

Reputation: 76103

Maybe increment from 0 to N (the "usual" way") and do ReverseBitOrder() for each iteration. You can find several implementations here (I like the LUT one the best). Should be really quick.

Upvotes: 0

Steve Jessop
Steve Jessop

Reputation: 279385

At each step, find the leftmost 0 digit of your value. Set it, and clear all digits to the left of it. If you don't find a 0 digit, then you've overflowed: return 0, or stop, or crash, or whatever you want.

This is what happens on a normal binary increment (by which I mean it's the effect, not how it's implemented in hardware), but we're doing it on the left instead of the right.

Whether you do this in bit ops, strings, or whatever, is up to you. If you do it in bitops, then a clz (or call to an equivalent hibit-style function) on ~value might be the most efficient way: __builtin_clz where available. But that's an implementation detail.

Upvotes: 2

Adam Liss
Adam Liss

Reputation: 48330

void reverse(int nMaxVal, int nBits)
{
   int thisVal, bit, out;

   // Calculate for each value from 0 to nMaxVal.
   for (thisVal=0; thisVal<=nMaxVal; ++thisVal)
   {
      out = 0;

      // Shift each bit from thisVal into out, in reverse order.
      for (bit=0; bit<nBits; ++bit)
         out = (out<<1) + ((thisVal>>bit) & 1)

   }
   printf("%d -> %d\n", thisVal, out);
}

Upvotes: 0

Related Questions