Tuesday, 28 April 2009

PROJECT EULER #72

Link to Project Euler problem 72

Consider the fraction, n/d, where n and d are positive integers. If n<d and HCF(n,d)=1, it is called a reduced proper fraction.

1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8

It can be seen that there are 21 elements in this set.
How many elements would be contained in the set of reduced proper fractions for d <= 1,000,000?


This was really beautiful. First I brute-forced it, using 35 minutes and then I searched the solution-forum for a better algorithm, without success. Then my mentor suggested this approach, which is so elegant that it deserves a name: The Sieve of Senehtsotare...16 seconds!


using System;
using System.Collections.Generic;

namespace ProjectEuler
{
class Program
{
static void Main()
{
//Problem 72
DateTime start = DateTime.Now;
int number = 1000000;
BigInt sum = new BigInt("1");
var tots = GenerateTotients(number);
foreach (KeyValuePair<int, int> pair in tots)
sum += pair.Value;
Console.WriteLine(sum-2);
TimeSpan time = DateTime.Now - start;
Console.WriteLine("This took {0}", time);
Console.ReadKey();
}
public static List<List<int>> GeneratePrimeFactors(int n)
{
var primes = GeneratePrimes(n);
var primeFactors = new List<List<int>>();
primeFactors.Add(new List<int>{0});
for (int i = 1; i <= n; i++)
primeFactors.Add(new List<int>());
if (n==1) return primeFactors;
foreach (int i in primes)
for (int j = i; j <= n; j += i)
primeFactors[j].Add(i);
return primeFactors;
}
public static Dictionary<int, int> GenerateTotients(int n)
{
var totients = new Dictionary<int, int>{{0,0}};
List<List<int>> primeFactors = GeneratePrimeFactors(n);
for (int i = 1; i < primeFactors.Count; i++)
{
double totient = i;
foreach (int j in primeFactors[i])
totient *= 1 - 1/(double) j;
totients.Add(i,(int)totient);
}
return totients;
}
public static bool IsPrime(int n)
{
if (n < 2) return false;
if (n == 2) return true;
for (long i = 2; i <= Math.Sqrt(n); i++)
if (n % i == 0) return false;
return true;
}
public static List<int> GeneratePrimes(int n)
{
var primeNumbers = new List<int> { 2, 3 };
for (int i = 5; i <= n; i += 2)
if (IsPrime(i))
primeNumbers.Add(i);
return primeNumbers;
}
}
}

Monday, 27 April 2009

PROJECT EULER #71

Link to Project Euler problem 71

Consider the fraction, n/d, where n and d are positive integers. If nd and HCF(n,d)=1, it is called a reduced proper fraction.
If we list the set of reduced proper fractions for d 8 in ascending order of size, we get:


1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8

It can be seen that 2/5 is the fraction immediately to the left of 3/7.
By listing the set of reduced proper fractions for d 1,000,000 in ascending order of size, find the numerator of the fraction immediately to the left of 3/7.


This turned out to be too easy, althought the wording of the problem does say

By listing the set of reduced proper fractions......

and I didn't do that...


using System;

namespace ProjectEuler
{
class Program
{
static void Main()
{
//Problem 71
DateTime start = DateTime.Now;
int a=2,b=5,c=3,d=7;
while (b+d<=1000000)
{
a = a + c;
b = b + d;
}
Console.WriteLine(a + "/"+b);
TimeSpan time = DateTime.Now - start;
Console.WriteLine("This took {0}", time);
Console.ReadKey();
}
}
}

Saturday, 25 April 2009

PROJECT EULER #70

Link to Project Euler problem 70

Euler's Totient function, φ(n) [sometimes called the phi function], is used to determine the number of positive numbers less than or equal to n which are relatively prime to n. For example, as 1, 2, 4, 5, 7, and 8, are all less than nine and relatively prime to nine, φ(9)=6.
The number 1 is considered to be relatively prime to every positive number, so φ(1)=1.

Interestingly, φ(87109)=79180, and it can be seen that 87109 is a permutation of 79180.

Find the value of n, 1 < n < src="http://projecteuler.net/index.php?section=problems&id=70" style="display: none;" alt="^(">7), for which φ(n) is a permutation of n and the ratio n/φ(n) produces a minimum.

Have to admit that I tried to brute-force this one and fell asleep on the couch. On awakening the answer lay there but the program would have happily run for some days (?) before completing. Armed with advice from the forum I learned that there are many ways to skin a totient...

Euler (from projecteuler.net) wrote:

Given n=p1e1p2e2...p1e1, φ(n)=n(1-1/p1)(1-1/p2)...(1-1/pk).

Now for n/φ(n) to be minimised, φ(n) must be as close to n as possible; that is, we want to maximise φ(n).

When evaluating φ(n), we note that each time we multiply by (1-1/pi), it gets smaller, so we need to minimise the number of distinct prime factors in n.

If n were prime, it would end in 1,3,7, or 9, and subtracting 1 only changes the last digit (to 0,2,6, or 8), so it could not be a permutation.

Hence n=p1*p2 and we only need to search through a list of known prime pairs.

In addition, φ(p1*p2)=p1*p2*(1-1/p1)(1-1/p2)=(p1-1)(p2-1), so we can compute φ(n) more efficiently.

using System;
using System.Collections.Generic;

namespace project_euler
{
class Program
{
private static List<int> primes = GenerateRangeOfPrimes(1000,5000);

static void Main()
{
//Problem 70
DateTime start = DateTime.Now;
decimal min = 100;
int number = 1;

for (int j = primes.Count - 1; j >= 0; j--)
for (int k = j - 1; k >= 1; k--)
{
int i = primes[j]*primes[k];
if (i < 10000000)
{
int totient = (primes[j] - 1)*(primes[k] - 1);
if (IsPermutation(i, totient))
if ((decimal) i/totient < min)
{
min = (decimal) i/totient;
number = i;
Console.WriteLine(number + ": " + min + " p1: " + primes[k] + " p2: " + primes[j]);
}
}
}
TimeSpan time = DateTime.Now - start;
Console.WriteLine("This took {0}", time);
Console.ReadKey();
}

private static bool IsPermutation(int m,int n)
{
char[] c = m.ToString().ToCharArray();
char[] d = n.ToString().ToCharArray();
Array.Sort(c);
Array.Sort(d);
string s = new string(c);
string t = new string(d);
if (s == t) return true;
return false;
}

private static bool IsPrime(int n)
{
if (n < 2) return false;
if (n == 2) return true;
for (long i = 2; i <= Math.Sqrt(n); i++)
if (n % i == 0) return false;
return true;
}

private static List<int> GenerateRangeOfPrimes(int m,int n)
{
var p = new List<int> { 2, 3 };
for (int i = 5; i <= n; i += 2)
if (IsPrime(i))
p.Add(i);
while (p[0] < m)
p.Remove(p[0]);
return p;
}
}
}

Friday, 24 April 2009

PROJECT EULER #69

Link to Project Euler problem 69

Euler's Totient function, φ(n) [sometimes called the phi function], is used to determine the number of numbers less than n which are relatively prime to n. For example, as 1, 2, 4, 5, 7, and 8, are all less than nine and relatively prime to nine, φ(9)=6.

n Relatively Prime φ(n) n/φ(n)
2 1 1 2
3 1,2 2 1.5
4 1,3 2 2
5 1,2,3,4 4 1.25
6 1,5 2 3
7 1,2,3,4,5,6 6 1.1666...
8 1,3,5,7 4 2
9 1,2,4,5,7,8 6 1.5
10 1,3,7,9 4 2.5

It can be seen that n=6 produces a maximum n/φ(n) for n ≤ 10.

Find the value of n ≤ 1,000,000 for which n/φ(n) is a maximum.

Brute-forcing the totients up to a million would take hours (?) so I found a more elegant formula:
\varphi(n)= n \cdot \prod_{p|n} \left( 1-\frac{1}{p} \right)


using System;
using System.Collections.Generic;

namespace project_euler
{
class Program
{
private static List<int> primes = GeneratePrimes((int)Math.Sqrt(1000000));
static void Main()
{
//Problem 69
DateTime start = DateTime.Now;
double max = 0;
int number = 1;
for (int i = 1000000; i >= 2; i--)
if (i / Totient(i) > max)
{
max = i / Totient(i) > max ? i / Totient(i) : max;
number = i;
}
Console.WriteLine(number);
TimeSpan time = DateTime.Now - start;
Console.WriteLine("This took {0}", time);
Console.ReadKey();
}

public static double Totient(int n)
{
double totient = n;
var primeFactorsOfN = new List<int>();
foreach (int i in primes)
{
if (n % i == 0 && n != i) primeFactorsOfN.Add(i);
if (i > Math.Sqrt(n)) break;
}
foreach (int i in primeFactorsOfN)
totient *= 1 - 1 / (double)i;
return totient;
}

public static bool IsPrime(int n)
{
if (n < 2) return false;
if (n == 2) return true;
for (long i = 2; i <= Math.Sqrt(n); i++)
if (n % i == 0) return false;
return true;
}

public static List<int> GeneratePrimes(int n)
{
var primes = new List<int> { 2, 3 };
for (int i = 5; i <= n; i += 2)
if (IsPrime(i))
primes.Add(i);
return primes;
}
}
}

PROJECT EULER #68

Link to Project Euler problem 68

Consider the following "magic" 3-gon ring, filled with the numbers 1 to 6, and each line adding to nine.


Working clockwise, and starting from the group of three with the numerically lowest external node (4,3,2 in this example), each solution can be described uniquely. For example, the above solution can be described by the set: 4,3,2; 6,2,1; 5,1,3.

It is possible to complete the ring with four different totals: 9, 10, 11, and 12. There are eight solutions in total.

TotalSolution Set
94,2,3; 5,3,1; 6,1,2
94,3,2; 6,2,1; 5,1,3
102,3,5; 4,5,1; 6,1,3
102,5,3; 6,3,1; 4,1,5
111,4,6; 3,6,2; 5,2,4
111,6,4; 5,4,2; 3,2,6
121,5,6; 2,6,4; 3,4,5
121,6,5; 3,5,4; 2,4,6

By concatenating each group it is possible to form 9-digit strings; the maximum string for a 3-gon ring is 432621513.

Using the numbers 1 to 10, and depending on arrangements, it is possible to form 16- and 17-digit strings. What is the maximum 16-digit string for a "magic" 5-gon ring?


Thanks to Michael, my employer and mentor, I managed to see the wisdom of creating a data structure (an Ngon) to hold the data. Well there's hope for me yet. This is only the second time during these problems that I've made an object as part of the solution. The permutation code is lifted from here, although I must add that I read Knuth's "The Art of Computer Programming - pre-fascicle 3A" to understand the concepts involved.

using System;
using System.Collections.Generic;

namespace ProjectEuler
{
class Program
{
static void Main()
{
//Problem 68
DateTime start = DateTime.Now;
var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var result = Permute(numbers);
List<long> allResults = new List<long>();
foreach (int[] ints in result)
{
Ngon newNgon = new Ngon(ints);
if (newNgon.IsMagic)
allResults.Add(newNgon.SolutionSet);
}
allResults.Sort();
Console.WriteLine(allResults[allResults.Count - 1]);
TimeSpan time = DateTime.Now - start;
Console.WriteLine("This took {0}", time);
Console.ReadKey();
}

public static List<int[]> Permute(int[] values)
{
List<int[]> permList = new List<int[]>();
permuteWorker(values, 0, values.Length, ref permList);
return permList;
}

private static void permuteWorker(int[] values, int start, int n, ref List<int[]> permList)
{
if (start == n - 1)
{
int[] thisValues = new int[values.Length];
values.CopyTo(thisValues, 0);
permList.Add(thisValues);
}
else
{
for (int i = start; i < n; i++)
{
int tmp = values[i];
values[i] = values[start];
values[start] = tmp;
permuteWorker(values, start + 1, n, ref permList);
values[start] = values[i];
values[i] = tmp;
}
}
}

public class Ngon
{
public List<int> Numbers { get; set; }
//we have a pentagon, with point at top, and 5 'external' numbers
//extending the sides of the pentagon to create 5 lines of numbers.
//we number the internal points 0 to 4, and the external ones 5 to 9.
//point 9 is always = 10. Numbers are clockwise with point 4 and point 9 at top.
public Ngon(int[] numbers)
{
Point0 = numbers[0];
Point1 = numbers[1];
Point2 = numbers[2];
Point3 = numbers[3];
Point4 = numbers[4];
Point5 = numbers[5];
Point6 = numbers[6];
Point7 = numbers[7];
Point8 = numbers[8];
Point9 = 10;
}
public int Point0 { get; private set; }
public int Point1 { get; private set; }
public int Point2 { get; private set; }
public int Point3 { get; private set; }
public int Point4 { get; private set; }
public int Point5 { get; private set; }
public int Point6 { get; private set; }
public int Point7 { get; private set; }
public int Point8 { get; private set; }
public int Point9 { get; private set; }

public int Line1Sum { get { return Point9 + Point4 + Point0; } }
public int Line2Sum { get { return Point5 + Point0 + Point1; } }
public int Line3Sum { get { return Point6 + Point1 + Point2; } }
public int Line4Sum { get { return Point7 + Point2 + Point3; } }
public int Line5Sum { get { return Point8 + Point3 + Point4; } }
public int Line1
{
get
{
int[] i = new int[3] { Point9, Point4, Point0 };
return 100 * i[0] + 10 * i[1] + i[2];
}
}
public int Line2
{
get
{
int[] i = new int[3] { Point5, Point0, Point1 };
return 100 * i[0] + 10 * i[1] + i[2];
}
}
public int Line3
{
get
{
int[] i = new int[3] { Point6, Point1, Point2 };
return 100 * i[0] + 10 * i[1] + i[2];
}
}
public int Line4
{
get
{
int[] i = new int[3] { Point7, Point2, Point3 };
return 100 * i[0] + 10 * i[1] + i[2];
}
}
public int Line5
{
get
{
int[] i = new int[3] { Point8, Point3, Point4 };
return 100 * i[0] + 10 * i[1] + i[2];
}
}
public bool IsMagic
{
get
{
if (Line1Sum == Line2Sum && Line1Sum == Line3Sum &&
Line1Sum == Line4Sum && Line1Sum == Line5Sum)
return true;
return false;
}
}

public long SolutionSet
{
get
{
int[] solutionSet = new int[5];
List<int> temp = new List<int>();
int min = 1000, index = 0;
temp.Add(Line1);
temp.Add(Line2);
temp.Add(Line3);
temp.Add(Line4);
temp.Add(Line5);
for (int i = 0; i < temp.Count; i++)
if (temp[i] < min)
{
min = temp[i];
index = i;
}
for (int i = 0; i < index; i++)
temp.Add(temp[i]);
temp.CopyTo(index, solutionSet, 0, 5);
string solution = "";
for (int i = 0; i < solutionSet.Length; i++)
solution += solutionSet[i];
return long.Parse(solution);
}
}
}
}
}

Thursday, 23 April 2009

PROJECT EULER #67

Link to Project Euler problem 67

By starting at the top of the triangle below and moving to adjacent numbers on the row below, the maximum total from top to bottom is 23.
3

7 5
2 4 6
8 5 9 3
That is, 3 + 7 + 4 + 9 = 23.
Find the maximum total from top to bottom in
triangle.txt (right click and 'Save Link/Target As...'), a 15K text file containing a triangle with one-hundred rows.
NOTE: This is a much more difficult version of
Problem 18. It is not possible to try every route to solve this problem, as there are 299 altogether! If you could check one trillion (1012) routes every second it would take over twenty billion years to check them all. There is an efficient algorithm to solve it. ;o)

I solved this when I did problem 18 just because I found a good algorithm back then.

using System;
using System.Collections.Generic;
using System.IO;

namespace ProjectEuler
{
class Program
{
static void Main()
{
//Problem 67
DateTime start = DateTime.Now;
StreamReader sr = new StreamReader(@"../../triangle.txt");
//Treat the triangle as a 2 dimensional array[][]
List<List<int>> triangle = new List<List<int>>();
//make the triangle
for (int i = 0; i < 100; i++)
{
string s = sr.ReadLine();
char[] c = { ' ' };
string[] sa = s.Split(c);
List<int> line = new List<int>();
foreach (string s1 in sa)
line.Add(int.Parse(s1));
triangle.Add(line);
}
//row
for (int i = 1; i < 100; i++)
//column
for (int j = 0; j <= i; j++)
//do the edges
if (j == 0)
triangle[i][0] = triangle[i][0] + triangle[i - 1][0];
else if (j == triangle[i].Count - 1)
triangle[i][triangle[i].Count - 1] = triangle[i][triangle[i].Count - 1] +
triangle[i - 1][triangle[i - 1].Count - 1];
//do the middle
else
triangle[i][j] = triangle[i - 1][j - 1] + triangle[i][j] > triangle[i - 1][j] + triangle[i][j]
? triangle[i - 1][j - 1] + triangle[i][j]
: triangle[i - 1][j] + triangle[i][j];
int max = 0;
foreach (int i in triangle[triangle.Count - 1])
max = i > max ? i : max;
Console.WriteLine(max);
TimeSpan time = DateTime.Now - start;
Console.WriteLine("This took {0}", time);
Console.ReadKey();
}
}
}

PROJECT EULER #66

Link to Project Euler problem 66

Consider quadratic Diophantine equations of the form:
x2 – Dy2 = 1
For example, when D = 13, the minimal solution in x is 6492 – 13 x 1802 = 1.
It can be assumed that there are no solutions in positive integers when D is square.
By finding minimal solutions in x for D = {2, 3, 5, 6, 7}, we obtain the following:
32 – 2 x 22 = 1
22 – 3 x 12 = 1
92 – 5 x 42 = 1
52 – 6 x 22 = 1
82 – 7 x 32 = 1
Hence, by considering minimal solutions in x for D <= 7, the largest x is obtained when D = 5. Find the value of D 1000 in minimal solutions of x for which the largest value of x is obtained.

This was the synthesis of the two previous questions, with a bit of study at wolfram.com (here and here) and some pencil and paper walkthroughs it was do-able. I guess I've used 15 hours to do these three problems. Uncommenting the commented statements prints the [a0;(a1,a2,....ar,ar+1)] series and the pr / qr or p2r+1 / q2r+1 (depending on the parity of the series) convergents for each number.


using System;
using System.Collections.Generic;

namespace ProjectEuler
{
class Program
{
static void Main()
{
//Problem 66
DateTime start = DateTime.Now;
//first the code from problem 64
Dictionary<int, List<int>> numbers = new Dictionary<int, List<int>>();
for (int number = 2; number <= 1000; number++)
{
bool repeat;
int a = (int)Math.Sqrt(number);
List<int> sequence = new List<int> { a };
numbers.Add(number, sequence);
int x = 1;
do
{
if (Math.Sqrt(number) - (int)Math.Sqrt(number) == 0) break;
int b = number - a * a;
int integerPart = x * ((int)Math.Sqrt(number) + a) / b;
numbers[number].Add(integerPart);
a = -(a - integerPart * (b / x));
x = (b / x);
repeat = integerPart == 2 * numbers[number][0] ? true : false;
} while (!repeat);
}
//then the modified code from problem 65
BigInt maxNumerator=0;
int d = 0;
for (int i = 2; i <= 1000; i++)
{
if (Math.Sqrt(i) - (int)Math.Sqrt(i)!=0)
{
int r = numbers[i].Count-1;
BigInt numerator_2 = 0, numerator_1 = 1, numerator = 0;
BigInt denominator_2 = 1, denominator_1 = 0, denominator = 0;
//var sequence = numbers[i];

if ((r) % 2 != 0)//is odd
{
//Console.Write("r is even ");
//Console.Write("Sqrt " + i + ": [");
//Console.Write(sequence[0] + ";(");
//for (int j = 1; j < sequence.Count; j++)
// if (j < sequence.Count - 1)
// Console.Write(sequence[j] + ",");
// else
// Console.WriteLine(sequence[j] + ")]");
//Console.WriteLine();
for (int j = 0; j < numbers[i].Count; j++)
{
numerator = numbers[i][j] * numerator_1 + numerator_2;
numerator_2 = numerator_1;
numerator_1 = numerator;
denominator = numbers[i][j] * denominator_1 + denominator_2;
denominator_2 = denominator_1;
denominator_1 = denominator;
}
for (int j = 1; j < numbers[i].Count-1; j++)
{
numerator = numbers[i][j] * numerator_1 + numerator_2;
numerator_2 = numerator_1;
numerator_1 = numerator;
denominator = numbers[i][j] * denominator_1 + denominator_2;
denominator_2 = denominator_1;
denominator_1 = denominator;
}
}
else//is even
{
//Console.Write("r is odd");
//Console.Write("Sqrt " + i + ": [");
//Console.Write(sequence[0] + ";(");
//for (int j = 1; j < sequence.Count; j++)
// if (j < sequence.Count - 1)
// Console.Write(sequence[j] + ",");
// else
// Console.WriteLine(sequence[j] + ")]");
//Console.WriteLine();
for (int j = 0; j < numbers[i].Count-1; j++)
{
numerator = numbers[i][j] * numerator_1 + numerator_2;
numerator_2 = numerator_1;
numerator_1 = numerator;
denominator = numbers[i][j] * denominator_1 + denominator_2;
denominator_2 = denominator_1;
denominator_1 = denominator;
}
}
if (numerator > maxNumerator)
{
maxNumerator = numerator;
d = i;
}
//Console.WriteLine("x = " + numerator.ToString());
//Console.WriteLine("y = " + denominator.ToString());
//Console.WriteLine("-----------------------------------------");
}
}
Console.WriteLine(d);
TimeSpan time = DateTime.Now - start;
Console.WriteLine("This took {0}", time);
Console.ReadKey();
}
}
}