Bit manipulation, permutate bits

2020-07-18 07:51发布

I am trying to make a loop that loops through all different integers where exactly 10 of the last 40 bits are set high, the rest set low. The reason is that I have a map with 40 different values, and I want to sum all different ways ten of these values can be multiplied. (This is just out of curiosity, so it's really the "bitmanip"-loop that is of interest, not the sum as such.)

If I were to do this with e.g. 2 out of 4 bits, it would be easy to set all manually,

0011 = 3,
0101 = 5,
1001 = 9,
0110 = 6,
1010 = 10,
1100 = 12,

but with 10 out of 40 I can't seem to find a method to generate these effectively. I tried, starting with 1023 ( = 1111111111 in binary), finding a good way to manipulate this, but with no success. I've been trying to do this in C++, but it's really the general method(if any) that is of interest. I did some googling, but with little success, if anyone has a good link, that would of course be appreciated too. :)

5条回答
forever°为你锁心
2楼-- · 2020-07-18 08:05

You can use any standard implementation of a choose/combination algorithm. Basically you want to choose 10 bits, out of 40, that will be set to 1.

That said, 40 choose 10 is 847,660,528. And that number will be multiplied by however many possible "tail" bits that aren't in the top 40. Presumably the tail bits aren't subject to any rules, so if there are k bits, that would be another 2k factor.

This algorithm, even once you implement it, will be very slow. It may be a good idea to think of a better approach to solve whatever problem you have.

Related questions

查看更多
家丑人穷心不美
3楼-- · 2020-07-18 08:08

There is a very non-obvious way to do this efficiently: Gosper's method of finding the next higher integer with the same number of 1 bits, from HAKMEM item 175.

lowest_1_bit = prev_combo & -prev_combo;
tmp = prev_combo + lowest_1_bit;
new_combo = (((prev_combo ^ tmp) >> 2) / lowest_1_bit) | tmp;
  • the first line finds the rightmost 1 bit;
  • the second turns the rightmost run of 1 bits to 0, and the 0 just to the left of the run to 1;
  • the third line replaces the 1 bits that were lost by the second line, at the bottom of the word.

Now (assuming you're using a 64-bit integer type) you can start with 1023 and just apply this repeatedly (until the result exceeds 1<<40).

查看更多
趁早两清
4楼-- · 2020-07-18 08:08

It's easier if you rewrite this as a set of nested loops indicating bit positions:

0011 = 0 1
0101 = 0 2
1001 = 0 3
0110 = 1 2
1010 = 1 3
1100 = 2 3

That is to say, the position P1 of the first bit runs from 0 to 3-1, and the position of the second bit P2 runs repeatedly from P1+1 to 3. Turning this into a generic recursive function is left as an exercise.

查看更多
啃猪蹄的小仙女
5楼-- · 2020-07-18 08:10

A bit more complicated, but done purely by bit manipulation. Your example:

#define WIDTH 4
#define BITS 2

void printbits(long pattern) {
  long bit;
  for (bit = 1L << WIDTH - 1; bit; bit >>= 1)
    putchar(pattern & bit ? 49 : 48);
  putchar('\n');
}

void movebits(pattern, bit) {
  long mask = 3L << bit;
  while (((pattern ^= mask) & mask) && (mask < 1L << WIDTH)) {
    mask <<= 1;
    printbits(pattern);
    if (bit)
      movebits(pattern, bit - 1);
  }
}

int main() {
  long pattern = (1L << BITS) - 1L, mask;
  printbits(pattern);
  movebits(pattern, BITS - 1);
}

Your real application:

#define WIDTH 40
#define BITS 10

and, as polygenelubricants says, be prepared to wait for bit :) Of course, you will replace printbits with something more useful to you...

(Edited for insufficient testing :/ Damn typos...)

查看更多
手持菜刀,她持情操
6楼-- · 2020-07-18 08:16

You can simply use next_permutation. Here's a sample to reproduce your 2 out of 4 case (the order varies slightly):

#include <iostream>
#include <algorithm>
using namespace std;
int main () {
 int bits[] = {0,0,1,1};

 do {
  for (int i = 0; i < 4; ++i) cout << bits[i] << " ";
  cout << endl;
 } while ( next_permutation (bits,bits+4) );
 return 0;
}
查看更多
登录 后发表回答