As part of a test I was given, I am required to code the instructions below:
A function F(
x) is defined over integersx>= 0 as:F(x) = ∑ [(x | i) - (x & i)] (summation ∑ is over i),
with
isatisfying the following two constraints:a. 0 <= i <= x
and
b. [(x | i) - (x & i)] = x - i
Given an integer
K, the task is to determine the number of non-negative integersxsuch that F(x) <=K.As an example, for
K= 6, the answer is 5. The approach is the following:
- x=0: F(0) = 0, as (0|0 - 0&0) = 0. F(0) <= K --> increment answer += 1,
ans=1- x=1: F(1) = (1|0 - 1&0) + (1|1 - 1&1) = 1 + 0 = 1 <= K ->
ans=2- x=2: F(2) = (2|0 - 2&0) + (2|1 - 2&1) + (2|2 - 2&2) = 2 + [3-0 != 2-1 2nd constrain on x not respected] + 0 = 2 ->
ans=3- x=3: F(3) = 6 ->
ans=4- x=4: F(4) = 4 ->
ans=5- x>4: F(x) >
Ksoansstays =5.So the answer for
K=6isans = 5: there are 5 non-negative integers that respect the condition F(x) <=K= 6.
with the constraint that K be < 10^(18).
This is what I came up with, adhering with the maths of the problem as much as possible:
#include <iostream>
uint64_t computeConstraint(uint64_t n, uint64_t i) {
return (n | i) - (n & i);
}
uint64_t computeFunction(uint64_t n) {
uint64_t result{};
for (uint64_t i=0; i<=n; ++i) {
if (computeConstraint(n, i) == n - i) {
result += n - i;
}
}
return result;
}
uint64_t countValidF(uint64_t K) {
uint64_t counter{};
for (uint64_t n=0; n<=K; ++n) {
if (computeFunction(n) <= K) {
++counter;
}
}
return counter;
}
int main() {
uint64_t K;
std::cin >> K;
uint64_t ans{countValidF(K)};
std::cout << ans << '\n';
}
Now, I am given some bugged code which is purportedly a faster alternative. My problem is that I fail to identify on what algorithm this is based, so I am unable to correct it.
The task of the test was to correct the following code, in order for it to yield an implementation that can manage large (10^18) values of K faster than the brute implementation I present in the first code snippet.
The code:
#include <iostream>
uint64_t nCr[60][60];
uint64_t count(uint64_t n, uint64_t cnt, int possible_msb_pos) {
if (n==0) {
if (cnt==0) {
return 1;
}
return 0;
}
uint64_t msb_pos = 0;
for (int i=possible_msb_pos; i>0; --i) {
if ((n & (1LL << i))) {
msb_pos = i;
break;
}
}
if (cnt < 0) {
return 0;
}
if (msb_pos < cnt) {
return 0;
}
uint64_t ans = 0;
ans += nCr[msb_pos-1][cnt];
ans += count(n | ((1LL << (msb_pos - 1))), cnt - 1, msb_pos);
return ans;
}
int main() {
for (int i=0; i<60; ++i) {
nCr[i][0] = 1;
}
for (int i=1; i<60; ++i) {
for (int j=1; j<60; ++j) {
nCr[i][j] = nCr[i-1][j] + nCr[i-1][j-1];
}
}
uint64_t K;
std::cin >> K;
uint64_t ans = 1;
for (int bit_count=1; bit_count<60; ++bit_count) {
ans += count( K/(1LL << (bit_count - 1)), bit_count, 60 );
}
std::cout << ans << '\n';
}
This second program yields an output of 3 if fed with K=6, while the first snippet of code correctly yields 5 for the same input.
It seems that the latter program calculates a table of binomial coefficients in array nCr using Pascal's triangle.
There is also a fair amount of bitshifting going on.
However despite these observations, I have been unable so far to discover the algorithm behind it.
Hence I do not really know where to look when trying to debug it.
My hope here is if anyone versed in bitwise computations, or who might have experience with this specific problem, may be able to explain the logic behind the second code snippet I was given.
using li = unsigned long long int;that's an immediate red flag with respect to the (lack of) quality of the source that is teaching you C++. Anyway you should use a debugger first and confirm line by line that each line of code is actually doing what you think it is doing.unsigned long long intisuint64_t.