This is a question about a Project Euler problem. This is where you can find the description of the problem: https://projecteuler.net/problem=29
OK, first of all, let me clarify that I have solved this problem, I am just looking for an alternative solution that is based more on maths.
Second, in order to not spoil the problem for anyone that has not solved, please if you have not solved it, don't continue. :)
So, I solved this problem using Python and because it has support for big numbers and list comprehensions, I was able to come up with an one liner:
print(len(set([a ** b for a in range(2, 101) for b in range(2, 101)])))
Now, I am trying to solve it in C, by using more mathematical knowledge(C natively has no support for big numbers, or list comprehensions). I came across this thread: PROJECT EULER #29 where the accepted answer gave me some ideas and I came up with this code:
int main(void) {
int found[1000]; // an array where I hold the found values(0 or 1)
int e, b, i, count, x;
count = 0; // number of duplicates
x = 3;
for(i = 0; i < 1000; i++)
found[i] = 0;
for(e = 1; (int)pow(x, e) <= 100; e++) {
for(b = 2; b <= 10; b++) {
if(found[e * b]) // if the value has been found, then we have duplicate
count++;
found[e * b] = 1; // mark that we found the value
}
}
printf("count: %d\n", count);
return 0;
}
With this code, I am doing what you can see on the bottom of the answer
above, where he shows some diagrams on how the to find the duplicates for
x = 3, based on what he has explained previously. I am trying to do the same thing. Now, if you run my code,
it correctly outputs 13, based on the diagrams of the above answer, which is the number of the duplicates.
So, I tried to extend it to solve the actual project euler problem because if I am able to find the number of duplicates, then I will just subtract it from the number 99 * 99(which is the possible power combinations because 2 <= a <= 100 and 2 <= b <= 100) and that would be the answer. The result was that:
int main(void) {
int found[1000];
int e, b, i, count, x;
count = 0;
for(x = 2; x <= 100; x++) {
for(i = 0; i < 1000; i++)
found[i] = 0;
for(e = 1; (int)pow(x, e) <= 100; e++) {
for(b = 2; b <= 100; b++) {
if(found[e * b])
count++;
found[e * b] = 1;
}
}
}
printf("count: %d\n", count);
return 0;
}
If you pay attention, the changes are that I loop for all the xs from 2 to 100 and b is not going from 2 to 10 but from 2 to 100.
However, the program prints 814, which is incorrect. It should be 618.
Any help is highly appreciated! I am probably counting some duplicates twice, but where? What is wrong with the code? Also, if you have any mathematical explanations that would help construct a new algorithm, this is highly appreciated too!
EDIT:
Something that I forgot to mention is that if instead of putting:
for(x = 2; x <= 100; x++)
I do:
for(x = 2; x <= 6; x++)
i.e. stop to 6, it prints the correct answer. And that is even more bizzarre.
EDIT2:
Let me also note that for 8 and 9(instead of 100) it gives correct results. 44 and 54 respectively.
the observation to find overlapped numbers is as flow
first lets make the range from 2 to 10 so the numbers will be like
22, 23, 24, 25, 26, 27, 28, 29, 210
32, 33, 34, 35, 36, 37, 38, 39, 310
42, 43, 44, 45, 46, 47, 48, 49, 410
52, 53, 54, 55, 56, 57, 58, 59, 510
62, 63, 66, 65, 66, 67, 68, 69, 610
72, 73, 77, 75, 77, 77, 78, 79, 710
82, 83, 88, 85, 88, 88, 88, 89, 810
92, 93, 94, 95, 96, 97, 99, 99, 910
102, 103, 104, 105, 106, 107, 108, 109, 1010
the key point is 42 = (22)2 = 24
so
42, 43, 44, 45, 46, 47, 48, 49, 410 will be
24, 26, 28, 210, 212, 214, 216, 218, 220
did you notice that until 210 we still have repeated numbers, after that we start to have a new number
so rewrite the numbers above using this observation it will be
22, 23, 24, 25, 26, 27, 28, 29, 210
32, 33, 34, 35, 36, 37, 38, 39, 310
24, 25, 28, 210, 212, 214, 216, 218, 220
52, 53, 54, 55, 56, 57, 58, 59, 510
62, 63, 64, 65, 66, 67, 68, 69, 610
72, 73, 74, 75, 76, 77, 78, 79, 710
26, 29, 212, 215, 218, 221, 224, 227, 230
34, 36, 38, 310, 312, 314, 316, 318, 320
102, 103, 104, 105, 106, 107, 108, 109, 1010
so we need to keep track of the number we get the powers for it, for examlpe we start with 22 the number is 2, the power is 2 the gap to increase power is 1.
the code for it is:
vector<int> calcCache(int rangeStart, int rangeEnd)
{
int maxBase = rangeEnd*rangeEnd;
int maxStartPow = 1;
while (maxBase > 0)
{
maxBase /= 2;
maxStartPow++;
}
maxStartPow /= 2;
vector<bool> seen(maxStartPow*rangeEnd, false);
int i = rangeStart;
vector<int> cahce;
int maxprev = 0;
int gap = 1;
int startpow = 2 * gap;
int j = pow(i, startpow);
int diff = rangeEnd - rangeStart;
int maxCurrent = diff*gap + startpow;
while (j <= rangeEnd*rangeEnd)
{
int currPow = startpow;
int k = 0;
int currRes = 0;
while (k <= diff)
{
if (!seen[currPow])
{
currRes++;
}
seen[currPow] = true;
currPow += gap;
k++;
}
cahce.push_back(currRes);
maxprev = currPow - gap;
gap++;
startpow = 2 * gap;
j = pow(i, startpow);
}
return cahce;
}
int distinctpowers(int rangeStart, int rangeEnd)
{
vector<bool> arr(rangeEnd*rangeEnd + 1, false);
int res = 0;
vector<int> cache = calcCache(rangeStart, rangeEnd);
for (int i = rangeStart; i <= rangeEnd; i++)
{
if (!arr[i*i])
{
int maxprev = 0;
int gap = 1;
int startpow = 2 * gap;
int j = pow(i, startpow);
int diff = rangeEnd - rangeStart;
int maxCurrent = diff*gap + startpow;
while (j <= rangeEnd*rangeEnd)
{
int currPow = startpow;
res += cache[gap - 1];
maxprev = currPow - gap;
arr[j] = true;
gap++;
startpow = 2 * gap;
j = pow(i, startpow);
}
}
}
return res;
}
you can add a lot of enhancement to this code like using bit vector instead of bool array.
32, 33, 34, 35, 36, 37, 38, 39, 310
34, 36, 38, 310, 312, 314, 316, 318, 320
52, 53, 54, 55, 56, 57, 58, 59, 510
62, 63, 64, 65, 66, 67, 68, 69, 610
72, 73, 74, 75, 76, 77, 78, 79, 710
102, 103, 104, 105, 106, 107, 108, 109, 1010
did you notice that the powers sequence is repeating it self for each new base, with maximum numbers in the sequence for base 2.
so we need to save our result for base 2 and reuse it with another base this is the idea of cache.
one more thing in the cache is that you need to figure out how many lines with base 2 you have. so start with the max base which is 10*10 each time divide by 2 until it becomes zero but this will give you the maximum power of base 2 as start of the last line which is 6 as our last 2 6 and you start powers from 2 till 6 each line increase by 2 so we need to divide our result by 2
int maxBase = rangeEnd*rangeEnd;
int maxStartPow = 1;
while (maxBase > 0)
{
maxBase /= 2;
maxStartPow++;
}
maxStartPow /= 2;
after that we need to keep track with powers we have seen and the maximum power you can encounter is the maxStartPow*rangeEnd.
vector<bool> seen(maxStartPow*rangeEnd, false);
then we start to go line by line in our base which in this case 2, each line remember the powers we have seen, and when we see new power we increase our result for this line.
the most important part of this code is that, after computing each line we need to store it as we will reuse it in our main problem.
int maxprev = 0;
int gap = 1;
int startpow = 2 * gap;
int j = pow(i, startpow);
int diff = rangeEnd - rangeStart;
int maxCurrent = diff*gap + startpow;
while (j <= rangeEnd*rangeEnd)
{
int currPow = startpow;
int k = 0;
int currRes = 0;
while (k <= diff)
{
if (!seen[currPow])
{
currRes++;
}
seen[currPow] = true;
currPow += gap;
k++;
}
cahce.push_back(currRes);
maxprev = currPow - gap;
gap++;
startpow = 2 * gap;
j = pow(i, startpow);
}
after that we back to our distinictPowers function and go base by base, each base go line by line and reuse our calculation from the cache function
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With