Calculating the next higher number with the same number of set bits?

This decision is set on the geeksforgeeks website.

I want to know if there is a better and simpler solution? This is a little hard to understand. Just the algorithm will be fine.

0
source share
3 answers

I am sure that this algorithm is as efficient and understandable as your related algorithm.

The strategy here is to understand that the only way to make a number larger without increasing its number is to transfer 1, but if you transfer several 1, then you must add them back.

  • Given the number 1001 1100

  • , , 0010 0111. : shifts = 2;

  • , , 0000 0100. . shifts += 3; bits = 3;

  • 5 3 , . .

  • 1. 0000 0101. 2 . bits -= 1

  • 3 , 0. 0010 1000. , shifts - bits == 3 shifts -= 3

  • . , . 1010 0011. . bits -= 2; shifts -= 2; bits == 0; shifts == 0

... current_val, shifts_owed, bits_owed

0000 0110
0000 0110, 0, 0 # Start
0000 0011, 1, 0 # Shift right till odd
0000 0000, 3, 2 # Shift right till even
0000 0001, 3, 1 # Set LSB
0000 0100, 1, 1 # Shift left 0's
0000 1001, 0, 0 # Shift left 1's

0011 0011
0011 0011, 0, 0 # Start
0011 0011, 0, 0 # Shift right till odd
0000 1100, 2, 2 # Shift right till even
0000 1101, 2, 1 # Set LSB
0001 1010, 1, 1 # Shift left 0's
0011 0101, 0, 0 # Shift left 1's

+2

, . :

  • ( , 1).
  • , .

, . , 2 ( 1 ). , , 1 . , .

, , . .

: "", , mpre , , , .

+2

, , geeksforgeeks (. : fooobar.com/questions/1461985/...) @QuestionC, , , ( Intel i5), .

g-for-g , , . , .

: - , 0; - , . : k , k , . ( 0: , 0 0.)

- :

template<typename UnsignedInteger>
UnsignedInteger next_combination_1(UnsignedInteger comb) {
  UnsignedInteger last_one = comb & -comb;
  UnsignedInteger last_zero = (comb + last_one) &~ comb;
  if (last_zero)
    return comb + last_one + ((last_zero / last_one) >> 1) - 1;
  else if (last_one)
    return UnsignedInteger(-1) / last_one;
  else
    return 0;
}

- ,

template<typename UnsignedInteger>
UnsignedInteger next_combination_2(UnsignedInteger comb) {
  UnsignedInteger last_one = comb & -comb;
  UnsignedInteger last_zero = (comb + last_one) &~ comb;
  UnsignedInteger ones = (last_zero - 1) & ~(last_one - 1);
  if (ones) while (!(ones & 1)) ones >>= 1;
  comb += last_one;
  if (comb) comb += ones >> 1; else comb = ones;
  return comb;
}

template<typename UnsignedInteger>
UnsignedInteger next_combination_3(UnsignedInteger comb) {
  if (comb) {
    // Shift the trailing zeros, keeping a count.
    int zeros = 0; for (; !(comb & 1); comb >>= 1, ++zeros);
    // Adding one at this point turns all the trailing ones into
    // trailing zeros, and also changes the 0 before them into a 1.
    // In effect, this is steps 3, 4 and 5 of QuestionC solution,
    // without actually shifting the 1s.
    UnsignedInteger res = comb + 1U;
    // We need to put some ones back on the end of the value.
    // The ones to put back are precisely the ones which were at
    // the end of the value before we added 1, except we want to
    // put back one less (because the 1 we added counts). We get
    // the old trailing ones with a bit-hack.
    UnsignedInteger ones = comb &~ res;
    // Now, we finish shifting the result back to the left
    res <<= zeros;
    // And we add the trailing ones. If res is 0 at this point,
    // we started with the largest value, and ones is the smallest
    // value.
    if (res) res += ones >> 1;
    else res = ones;
    comb = res;
  }
  return comb;
}

( , -, .)

, 32- . ( , i-, , 0 32.):

#include <iostream>
int main(int argc, char** argv) {
  uint64_t count = 0;
  for (int i = 0; i <= 32; ++i) {
    unsigned comb = (1ULL << i) - 1;
    unsigned start = comb;
    do {
      comb = next_combination_x(comb);
      ++count;
    } while (comb != start);
  }
  std::cout << "Found " << count << " combinations; expected " << (1ULL << 32) << '\n';
  return 0;
}

:

1. Bit-hack with division: 43.6 seconds
2. Bit-hack with shifting: 15.5 seconds 
3. Shifting algorithm:     19.0 seconds
+2
source

Source: https://habr.com/ru/post/1679348/


All Articles