Combinatoric selections
Problem 053: Combinatoric selections
There are exactly ten ways of selecting three from five, 12345
:
123, 124, 125, 134, 135, 145, 234, 235, 245, and 345
In combinatorics, we use the notation, C(5,3) = 10
.
In general, C(n,r) = n! / (r!(n?r)!)
,where r <= n, n! = n * (n?1) * ... * 3 * 2 * 1
, and 0! = 1
.
It is not until n = 23
, that a value exceeds one-million: C(23,10) = 1144066
.
How many, not necessarily distinct, values of C(n,r)
, for 1 <= n <= 100
, are greater than one-million?
Solution:
####################################################
>190p191p020p130pv >40g9+30g2%!g40g8+30g2%!g+vv!g!%2g03+9g04`**<
>30g:"d"-!#v_1+:30p2/40p>30g40g2*>-| >:40g9+30g2%p"}}@"^
^ >#<$ #<20g.@ >40g8+30g2%!g2* ^>$40g9+1-30g2%!g!v
>30g40g2* -^v _v#!_v#!_v#!<
^ _^#!p04:-1g04<p%2g03+9g040p02++1!!-*2g04g03g02$< $< $<
Explanation:
That is the kind of problem I really like. There is a lot of depth in the solution and a proper algorithm solves this in no time.
The algorithm idea here is that we use the properties of Pascal's Triangle.
As you probably know this triangle starts at its top with a 1
. Then every cell is calculated as the sum of the two cells above it.
cell[y][x] = cell[y-1][x-1] + cell[y-1][x]
When we calculate C(n, r)
this is nothing more than the number at row n and column r.
So what we do is build Pascal's Triangle up to a height of one-hundred and look at the cells with values greater than one million.
The obvious problem is that the numbers grow extremely big, and sooner or later (probably sooner) the numbers will overflow. So what we do is use a little trick:
As soon as a cell is over 1 000 000
we mark her (= put a zero in that cell). When we create a new cell out of its two parents we check if one of the parents has the mark (= is zero) and then the children gets also marked. Because if one of the parents (or both) is over one million then all of its children will also be over one million.
Another "trick" is that we don't need to remember the whole triangle, only the current and the last row are important. So we have two "arrays" each representing a row and after every cycle the two change roles. The lastRow becomes the currentRow and vice versa. In Befunge we can do this simply by placing the two arrays on top of each other and accessing them with y = row%2
or y = (row+1)%2
.
There is one last optimisation I have done and this one will allow us to fit our program in the 80x25 Befunge-93 size restrictions.
The triangle is symmetric and we can only look at one half of it. But we have to be aware of the fact that now every found number counts two times (one for each side) except it's exactly in the middle. ALso if we want to calculate the value of the middle cell we can't take the value of the right parent (this one does not exist / was never calculated), but because of it's symmetric properties we can just take the left one two times.
for(int row = 2; row <= NMAX; row++)
for(int col = (row/2); col > 0; col--)
{
if (2*col == row)
matrix[row % 2][col] = matrix[(row + 1) % 2][col - 1] * 2;
else
matrix[row % 2][col] = matrix[(row + 1) % 2][col] + matrix[(row + 1) % 2][col - 1];
if (matrix[row % 2][col] > 1000000 || matrix[(row + 1) % 2][col] == 0 || matrix[(row + 1) % 2][col - 1] == 0)
{
count += (2 * col == row) ? 1 : 2;
matrix[row % 2][col] = 0;
}
}
Tip:
Run this code with BefunExec on the 50kHz speed setting - it looks quite cool in action.
Interpreter steps: | 372 790 |
Execution time (BefunExec): | 125ms (2.98 MHz) |
Program size: | 80 x 7 (fully conform befunge-93) |
Solution: | 4075 |
Solved at: | 2015-01-22 |