|
1 | 1 | #include "test_framework/generic_test.h" |
| 2 | + |
| 3 | +void printBin(unsigned long long val, size_t len, const std::string& suffix) { |
| 4 | + std::string binval; |
| 5 | + for (size_t i = 0; i < len; i++) { |
| 6 | + binval.append(val & 1 ? "1" : "0"); |
| 7 | + val >>= 1; |
| 8 | + } |
| 9 | + reverse(binval.begin(), binval.end()); |
| 10 | + printf("%s (%s)\n", binval.c_str(), suffix.c_str()); |
| 11 | +} |
| 12 | + |
| 13 | +bool sameWeight(unsigned long long left, unsigned long long right) { |
| 14 | + int leftWeight = 0; |
| 15 | + int rightWeight = 0; |
| 16 | + while (left > 0 || right > 0) { |
| 17 | + if (left & 1) { |
| 18 | + leftWeight++; |
| 19 | + } |
| 20 | + left >>= 1; |
| 21 | + if (right & 1) { |
| 22 | + rightWeight++; |
| 23 | + } |
| 24 | + right >>= 1; |
| 25 | + } |
| 26 | + return rightWeight == leftWeight; |
| 27 | +} |
| 28 | + |
| 29 | +unsigned long long ClosestIntSameBitCountBruteForce(unsigned long long x) { |
| 30 | + /* |
| 31 | + Approach 1: look for the best weight explicitly knowing the value |
| 32 | + If we have a constant time approach for measuring the weight of a number, |
| 33 | + we could just try repeatedly calculating x++ and x-- until we find a value |
| 34 | + of the same weight. This is a brute force approach that technically works |
| 35 | + here but can become quite inefficient. |
| 36 | + */ |
| 37 | + unsigned long long nearestUp = x + 1; |
| 38 | + unsigned long long nearestDown = x - 1; |
| 39 | + |
| 40 | + while (true) { |
| 41 | + if (sameWeight(x, nearestUp)) { |
| 42 | + return nearestUp; |
| 43 | + } |
| 44 | + if (sameWeight(x, nearestDown)) { |
| 45 | + return nearestDown; |
| 46 | + } |
| 47 | + nearestUp++; |
| 48 | + nearestDown--; |
| 49 | + } |
| 50 | +} |
| 51 | + |
2 | 52 | unsigned long long ClosestIntSameBitCount(unsigned long long x) { |
3 | | - // TODO - you fill in here. |
4 | | - return 0; |
| 53 | + /* |
| 54 | + Approach 2: look for the best value by explicitly knowing the weight |
| 55 | + To create y, we need to unset one bit in x, and set one unset bit in x. |
| 56 | + Unsetting is subtracting, and setting is adding. We want the net change |
| 57 | + between y and x to be as small as possible, so we want these two bits to be as |
| 58 | + close as possible. If we flip the first set bit we find, then the optimal |
| 59 | + choice is to flip the first unset bit to its right; this may not exist though. |
| 60 | + Consider 0xff, 255. Although changing this to 0xfe = 254 subtracts the |
| 61 | + smallest amount possible, the difference after we set a new bit will be huge, |
| 62 | + e.g. 0x1fe = 510. So what we want is to flip two differing bits that are as |
| 63 | + close as possible, i.e. adjacent. The book phrases this as "look for the |
| 64 | + least significant pair of differing bits and swap them". |
| 65 | + */ |
| 66 | + int i = 1, j = 0; |
| 67 | + while (((x >> i) & 1) == ((x >> j) & 1)) { |
| 68 | + i++; |
| 69 | + j++; |
| 70 | + } |
| 71 | + int mask = (1LL << i) | (1LL << j); |
| 72 | + return x ^ mask; |
5 | 73 | } |
6 | 74 |
|
7 | 75 | int main(int argc, char* argv[]) { |
|
0 commit comments