Project Euler – Problem 83 Solution

The problem description is here, and click here to see all my other Euler solutions in F#.

 

This is a more difficult version of problem 82, and now you can move in all four directions!

As before, we start by loading the input data into a 2D array:

Prob83_01

and initialize another matrix of the same size to hold the minimum sum leading to each cell:

Prob83_02v2

What we have done differently this time though, is to choose initial defaults:

  • for the top-left corner (0, 0) the minimum sum is itself
  • for all other cells at (row, col) we’ll always traverse right and then down
    • if row = 0, then this equates to only move right until we reach col
    • if col = 0, then this equates to only move down until we reach row

e.g.

Prob83_03

Next, as we did for the problem 82, we’ll add a couple of helper to help us traverse both matrices:

Prob83_04

and we’ll find the new minimum sum to each cell by taking the minimum from the four directions:

Prob83_05

Unlike before, we can’t isolate to optimizing one column at a time, and instead we’ll need to traverse the whole matrix. For better cache locality, we’ll traverse row first and recursively optimize the whole matrix until no more optimization is possible:

Prob83_06

Finally, find the final value for the bottom-right corner of the sum matrix:

Prob83_07

This solution runs in 73ms on my laptop.

 

The source code for this solution is here.

Project Euler – Problem 82 Solution

The problem description is here, and click here to see all my other Euler solutions in F#.

 

This is a more difficult version of problem 81, but still, as you can’t move left so we can still optimize one column at a time.

First, let’s read the input file into a 2D array:

Prob82_01

and initialize another matrix with the same size to store the minimum sum to each of the cells:

Prob82_02

In order to avoid miscalculations due to uninitialized cells (i.e. if we had initialized them with 0), I’ve initialized the cells by assuming that we have moved right all the way, hence this line:

    else matrix.[row, 0..col] |> Array.sum

which adds up all the cells to the left of, and including, the (row, col) cell.

Next, we’ll add a couple of helper functions to work out the new minimum sum to the cell at (row, col) if we had moved up, down, or right:

Prob82_03

We can now use these functions to optimize the columns one at a time.

The tricky thing here is that we might need a few passes to converge onto the true minimum sum path to each of the cells. For example, in order to work out the minimum sum at (row, col) we need to know the minimum sum at (row-1, col) and (row+1, col). But to work out the minimum sum at (row-1, col) we need to know the minimum sum at (row, col)!

So, instead, we’ll recursively try to optimize each cell in the column until it can’t be optimized anymore:

Prob82_04

At the end of the snippet above, we optimized the columns one at a time from left to right. (I could have equally included this as part of the recursive function, but it would introduce another level of nesting which is why I opted against it)

And finally, look for the minimum sum on the right-most column in the sum matrix to answer the question:

Prob82_05

This solution runs in 9ms on my laptop.

 

The source code for this solution is here.

Project Euler – Problem 102 Solution

The problem description is here, and click here to see all my other Euler solutions in F#.

 

After reading the question, a quick search on how to test if a point is in a triangle turned up this useful SO answer. Translating the algorithm to F# is pretty trivial:

Prob102_01

I saved the triangle.txt file alongside the script file, so to make it easier to load and parse. The content of the file looks like this:

-340,495,-153,-910,835,-947

-175,41,-421,-714,574,-645

-547,712,-352,579,951,-786

419,-864,-83,650,-399,171

so for each line, we have the 3 (x,y) coordinates separated by comma.

F# 4 also added a chunkBySize combinator to the Array module, which comes in handy here:

Prob102_02

This solution runs for 20ms on my machine.

 

The source code for this solution is here.

Project Euler – Problem 75 Solution

The problem description is here, and click here to see all my other Euler solutions in F#.

 

I based my solution on Euclid’s formula for generating Pythagorean triples.

Prob75_01_wiki

And given that max L is 1,500,000, the maximum value for m we need to consider is \sqrt{\frac{L}{2}} . Because L = a + b + c and a^2 + b^2 = c^2 , we can deduce that c < \frac{L}{2} ; and since c = m^2 + n^2 we also have m < \sqrt{c} and therefore m < \sqrt{\frac{L}{2}} .

Prob75_01

The above makes use of a recursive function to calculate the GCD (based on Euclidean Algorithm):

Prob75_04

For efficiency, we’ll create a cache to store the number of ways L can be used to create integer sided right-angle triangle. As we iterate through the m and n pairs we generated above, we’ll take advantage of the fact that if a^2 + b^2 = c^2 then ka^2 + kb^2 = kc^2 must also be true and increment multiples of L by one.

Prob75_02

Finally, to work out the answer:

Prob75_03

This solution runs for about 350ms on my machine.

 

The source code for this solution is here.

Project Euler – Problem 68 Solution

Problem

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.

image

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?

Solution

(see full solution here)

 

Before we go into details on the solution, let’s first structure the question in a way that’s easy for us to compute.

To construct the magic circle, be it for a 3-gon or a 5-gon ring, we can slice up the numbers into pairs – e.g. A => [1; 2], B => [3; 4], C => [5; 6], D => [7; 8], E => [9; 10] – and the problem becomes finding ways in which the numbers can be permuted such that:

  1. a0 is the smallest amongst a0, b0, c0, d0, and e0
  2. the sums of a0 + a1 + b1, b0 + b1 + c1, … are the same

For example:

image

 

First, we’ll find the different ways the numbers 1 to 10 can be permuted, and for each permutation slice the numbers into pairs:

image

(this makes use of a permute function defined in the Common.fs source file in the solution).

 

Then we need a function to sum a pair of number with the last element in the adjacent pair – e.g. a0 + a1 + b1:

image

 

For each permutation, we need to check:

  1. if a0 is the smallest amongst the head elements
  2. if the sums of the groups of 3 – i.e. a0 + a1 + b1, b0 + b1 + c1, etc. – are the same

image

This predicate function allows us to find arrangements that meet our criteria. All that’s left is to turn the result groups of 15 numbers into 16/17-digit strings and find the maximum (see full solution below).

 

Here’s the full solution:

image

 

This solution took 26s to execute on my machine.