Wednesday, July 29, 2009

Grind Report 29

Problem 29

Consider all integer combinations of a^(b) for 2 <= a <= 5 and 2 <= b <= 5:
2^(2)=4, 2^(3)=8, 2^(4)=16, 2^(5)=32
3^(2)=9, 3^(3)=27, 3^(4)=81, 3^(5)=243
4^(2)=16, 4^(3)=64, 4^(4)=256, 4^(5)=1024
5^(2)=25, 5^(3)=125, 5^(4)=625, 5^(5)=3125

If they are then placed in numerical order, with any repeats removed, we get the following sequence of 15 distinct terms:

4, 8, 9, 16, 25, 27, 32, 64, 81, 125, 243, 256, 625, 1024, 3125

How many distinct terms are in the sequence generated by a^(b) for 2 <= a <= 100 and 2 <= b <= 100?

*WARNING: This contains the solution!*

While attempting this problem I came up with two solutions - but only one actually completely worked... As usual, using C++ runs into some problems here because of the limitation on integer size.

My first solution (the semifunctional one) placed the focus on the repeated terms. I used the fact that an exponent can be broken up such as

2^32 = (2^4)^8 = 16^8

With this it is possible to find which terms are repeated without ever actually calculating the value of a^b. So with this it is easy to simply keep a boolean collection of the different combinations and if one has been encountered before. Such as in the example above when 2^32 (a = 2, b = 32) is run it will see that this is equal to 4^16 and 16^8. Consequently [4][16] and [16][8] both need to be marked as repeats. At the end it is as easy as counting the repeats and subtracting them from the max number of possible terms (99x99).

Here is the code but keep in mind that this code does not return the correct answer. While attempting to debug the solution I came across another, much simplier, solution. That I'll discuss after this.

#include <iostream>
#include <vector>

using std::vector;
using std::cout;

int PowerWithinMAX(int a, int b);

int main()
{
vector<vector<bool>> encountered;
//create the list of encountered combinations - vector<vector<bool>>
for (int i = 0; i < MAX + 1; ++i)
encountered.push_back(vector<bool>(MAX + 1, false));

int repeatCount = 0;
for (int a = MIN; a <= MAX; ++a)
{
for (int b = MIN; b <= MAX; ++b)
{
//if a^b has previously been encountered before
//then increment repeat count and continue
if (encountered[a][b])
{
++repeatCount;
continue;
}
//check to see if b can be broken up into pieces
//ie. 2^32 = (2^4)^8 = 16^8
//if so then mark that as being encountered ([16][8])
for (int i = 2; i < b / 2; ++i)
{
//if i is a divisor of b
if (b % i == 0)
{
//check to see if the power is within the max
int c = PowerWithinMAX(a, i);
if (c)
encountered[c][b / i] = true;
}
}
}
}
int ans = 0;
for (int i = 0; i <= MAX; ++i)
for (int j = 0; j <= MAX; ++j)
if (encountered[i][j])
++ans;

int answer = (MAX - MIN + 1) * (MAX - MIN + 1) - ans;
cout << answer;
}

int PowerWithinMAX(int a, int b)
{
int solution = 1;
//calculates a^b until solution is > 100
while (solution <= MAX && b > 0)
{
solution *= a;
b--;
}
//if b > 0 (true) then the solution is greater than MAX
if (solution > MAX)
return 0;
else
return solution;
}

The working method using some more standard library goodness to take care of mostly everything. Every possible combination of a^b is calculated using pow(a, b) and then stored into a set. Once done the set.size() is the unique number of terms. The reason why this works is that when using double it is true that it is not precise enough to give the correct term, but it is precise enough that all terms are unique unless they are actually a repeated term.

Because this is so simple the code is pretty small.

#include <iostream>
#include <set>
#include <math.h>

using std::set;
using std::cout;

const int MIN = 2;
const int MAX = 100;

int main()
{
set<double> answer;
for (double a = MIN; a <= MAX; ++a)
for (double b = MIN; b <= MAX; ++b)
answer.insert(pow(a, b));
cout << answer.size();
}

I will probably go back and fix up the first solution and get it working eventually. I am curious what the problem with the algorithm is. In the meantime I'll continue to move onward.

*saved game*

Monday, July 27, 2009

Grind Report 28

Problem 28

Starting with the number 1 and moving to the right in a clockwise direction a 5 by 5 spiral is formed as follows:

21 22 23 24 25
20 7 8 9 10
19 6 1 2 11
18 5 4 3 12
17 16 15 14 13

It can be verified that the sum of both diagonals is 101.

What is the sum of both diagonals in a 1001 by 1001 spiral formed in the same way?


*WARNING: This contains the solution!*

This is by far my most compact solution so far and like any compact algorithm, there was a decent amount of thought that went into it. So here is my attempt to explain everything...

One of the first things I did was write out the corners by themselves as well as expand the grid to be 9x9 so I could get a better view of what exactly was going on. After drawing them out and staring at them I saw that the way to get the corners is to just add multiples of two to the previous number...


3 5 7 9 13 17 21 25... //corner value
2 2 2 2 4 4 4 4 6 6 6 6 8 8 8 8 //amount to add to the previous corner

That's not exactly a beautiful example but I think it will suffice. With just this it would be very easy to write a program to do this; however, I took it a step further. Instead of considering each corner, I figured why not just consider the average of the corners and multiply that by 4. It makes sense if you think about it, but it actually turns out that the average of the corners is all of the numbers directly to the left of 1 (and including 1). In the example you can see 3 averages: 1, 6, 19. As I just said, to get the answer we just need to sum of the averages and multiply by 4.

One way to get to each average is to follow the bottom left diagonal and move n numbers up, where n is the number of spirals moved over from the center. And now the question shifts to how to find the corners in the bottom left diagonal. Using the previously gathered information I derived that at any given spiral n the value of the bottom left corner is

n
* 4 + ((8 * (n - 1) * n) / 2) + 1

n * 2 * 2 //is that for the given spiral we need to find the 2nd corner
((8 * (n - 1) * n) / 2) //this sums up all of the previous corners
1 //because we start with the center 1

This is a little complex and I could go into it more but I doubt it is of that much interest. Here is my code solution.

#include <iostream>

using std::cout;

const int SPIRALSIZE = 1001;

int main()
{
long long average = 1;
for (int i = 1; i * 2 + 1 >= 1001; ++i)
average += (i * 4 + ((8 * (i - 1) * i) / 2) + i + 1);
cout << average * 4;
}

*saved game*

Sunday, July 26, 2009

Grind Report 27

Problem 27

Euler published the remarkable quadratic formula:

n² + n + 41

It turns out that the formula will produce 40 primes for the consecutive values n = 0 to 39. However, when n = 40, 40^(2) + 40 + 41 = 40(40 + 1) + 41 is divisible by 41, and certainly when n = 41, 41² + 41 + 41 is clearly divisible by 41.

Using computers, the incredible formula n² - 79n + 1601 was discovered, which produces 80 primes for the consecutive values n = 0 to 79. The product of the coefficients, -79 and 1601, is -126479.

Considering quadratics of the form:

n² + an + b, where |a| < 1000 and |b| < 1000

where |n| is the modulus/absolute value of n
e.g. |11| = 11 and |-4| = 4

Find the product of the coefficients, a and b, for the quadratic expression that produces the maximum number of primes for consecutive values of n, starting with n = 0.


*WARNING: This contains the solution!*

After reading the question a few times through to make sure I understood it completely I caught onto a subtle hint. In both examples a and b are prime numbers. After thinking about it a little it became obvious that this is necessary to produce a string of prime numbers with n. Such as if b is not prime then n=0 automatically fails. This also concludes that b must be positive.

With this and reusing my prime code I went ahead and solve this problem. I did run into one problem that once I figured it out I realized it was right there in my face the whole time. This was that I am considering all a and b that index the prime numbers up to 1000.

for (int a = 0; primes[a] < 1000; ++a)

I thought that this was clever when I wrote it. But apparently it was too clever for me because later on I completely forgot I was using a and b as an index to the prime numbers. Consequently I wrote the quadratic equation like this...

n * n + a * n + b

instead of indexing the primes array like I was suppose to.

n * n + primes[a] * n + primes[b]

I learned my lesson here. Not to mention that this mistake was a little humbling, I have not made one of these for awhile and so this was much needed. Here is the code. (the IsPrime function is not optimal)

#include <iostream>
using std::cout;

const int MAX = 1000;
const int PRIMECOUNT = 200;
int primes[PRIMECOUNT];

bool IsPrime(long number);
void populatePrimes();

int main()
{
populatePrimes();

int answer = 0;
int highest = 0;
//consider all prime numbers up to 1000 for a
for (int a = 0; primes[a] < MAX; ++a)
{
//also consider the negative version
for (int i = -1; i <= 1; i += 2)
{
int primeA = primes[a] * i;
//consider all prime numbers up to b
for (int b = 3; primes[b] < MAX; ++b)
{
int quadratic;
int n = 0;
//continue calculating the quadratic and checking to see if it is prime
//until it is no longer prime
do
{
quadratic = n * n + primeA * n + primes[b];
++n;
} while (IsPrime(quadratic));
//check to see if the highest n has been reached
if (n > highest)
{
highest = n;
answer = primeA * primes[b];
}
}
}
}
cout << answer;
}

bool IsPrime(long number)
{
for (int i = 0; i < PRIMECOUNT; ++i)
{
if (primes[i] == number)
return true;
if (primes[i] > number)
return false;
}
return false;
}

void populatePrimes()
{
primes[0] = 2;
primes[PRIMECOUNT - 1] = -1;
int number = 3;
int foundCount = 1;
bool isPrime = false;
while (primes[PRIMECOUNT - 1] == -1)
{
//assume a number is prime until proven guilty
isPrime = true;
for (int i = 0; i < foundCount; ++i)
{
//break if the number is divisible by an already found prime number (guilty)
if (number % primes[i] == 0)
{
isPrime = false;
break;
}
//check to see if the current prime number squred is greater than or equal to the provided number
//if so then break. This means the number is a prime.
if (primes[i] * primes[i] >= number)
break;
}
//add the prime number
if (isPrime)
primes[foundCount++] = number;
//increment the number by 2 (to stay odd)
number += 2;
}
}

*saved game*

Grind Report 26

Problem 26

A unit fraction contains 1 in the numerator. The decimal representation of the unit fractions with denominators 2 to 10 are given:
^(1)/_(2)= 0.5
^(1)/_(3)= 0.(3)
^(1)/_(4)= 0.25
^(1)/_(5)= 0.2
^(1)/_(6)= 0.1(6)
^(1)/_(7)= 0.(142857)
^(1)/_(8)= 0.125
^(1)/_(9)= 0.(1)
^(1)/_(10)= 0.1

Where 0.1(6) means 0.166666..., and has a 1-digit recurring cycle. It can be seen that ^(1)/_(7) has a 6-digit recurring cycle.

Find the value of d < src="http://projecteuler.net/index.php?section=problems&id=26" style="display: none;" alt="^(">1)/_(d) contains the longest recurring cycle in its decimal fraction part.

*WARNING: This contains the solution!*

Ready to revisit 4th grade? This problem is a little tricky because of one thing - floating point precession. The precision required is far beyond our float or double we have available in C++ and so something more is required.


This "more" is actually from 4th grade. Do you remember long hand division? If not you can refresh your memory at the Long Division Wikipedia page - don't worry I wont tell anyone ;).

This is a very basic example; however, it shows something very important. That is if the remainder is the same as a previous encountered remainder then the number has a recurring cycle. Also, something not shown here, is the fact that if the remainder is 0 then the number does not have a recurring cycle. By just using these two pieces of knowledge this problem becomes fairly simple. All that is needed is to implement long division and check for if 0 or a previous remainder is encountered (this method requires storing the previous remainders).

Here is an implementation that solves the problem very quickly.


#include <iostream>

using std::cout;

const int MAX = 1000;

int main()
{
int answer;
int highest = 0;
for (int i = 1; i < MAX; ++i)
{
int count = 0;
int remainder = 1;
bool remainders[MAX] = {false};
while (remainder != 0)
{
//keep dropping 0s to the remainder until i can divide into it at least once
while (!(int)(remainder / i))
remainder *= 10;
//get the new remainder
remainder %= i;

//found a cycle
if (remainders[remainder])
break;
//store the remainder
remainders[remainder] = true;
count++;
}
//if there was a cycle and the count is greater than the previous highest
//then set the count as the new highest
if (remainder && count > highest)
{
highest = count;
answer = i;
}
}
cout << answer << ": " << highest;
}

*saved game*