Sunday, September 16, 2012

Combinatorial explosion and Project Euler 393

Last week I found a nice video about algorithmic combinatorial explosion. It was very interesting but it's a bit sad to see the big sister throwing away her life in order to teach her little students the perils of trying to compute combinatorial problems that grow very fast. The problem they studied didn't seem to be hard, just enumerate all the ways you can go from one corner to the opposite corner in a rectangular grid without passing the same node twice. Deceptively simple, for a 1x1 grid there are only 2 ways, for a 2x2 grid there are 12 ways:


However things get ugly as long as we start increasing the size, for 5x5 grids she needs to use a computer to find the 1'262,816 ways. For grids greater that 6x6, she uses a supercomputer to get the answer without waiting so much and even with that the 8x8 grid takes 4.5 hours to be solved. The 9x9 grid takes 6.5 years to be solved. When she goes to compute the 10x10 grid she must be prepared to await almost 250,000 years to obtain the answer! Enumerating this way the solutions for the 11x11 grid would take the double of the estimated age of the universe. Here is the video:


Coincidentally, last week Project Euler problem 393 was somehow similar:

An nxn grid of squares contains n2 ants, one ant per square.
All ants decide to move simultaneously to an adjacent square (usually 4 possibilities, except for ants on the edge of the grid or at the corners). We define f(n) to be the number of ways this can happen without any ants ending on the same square and without any two ants crossing the same edge between two squares.

You are given that f(4) = 88. Find f(10). 

So I decided to give it a shot enumerating the solutions for small numbers. consider for simplicity the case where (n = 2), the initial grid would be:

where we have enumerated the ants from 1 to 4 in their starting cell. The ants can move only one cell to any adjacent cell. If we obviate the condition of two ants never crossing the same edge, the number of different positions we can obtain are the ones shown below:

If we take in account the constraint condition we'll need to discard the positions shown below because to obtain these positions two ants have needed to cross the same edge:

So the problem for the case (n = 2) has only two final positions:
In order to enumerate the solutions for (n = 4) I implemented a DFS algorithm. Here is the code in Killa (my programming language):

I tried to minimize the memory usage and the condition of two ants not crossing the same edge imposed some additional conditionals that need more explaining but I hope you can follow the code. The answer for (n = 4) is 88, I used the HTML5 canvas element to make a collage with all the different possible final positions. The original board position is:
in which every ant is represented with a different cell color and the different ways that they can move are arranged in the 8x11 matrix below:
Observe that the JavaScript code is almost identical to the code in Killa, if we ignore the drawing routines:

Be aware that this problem is also a combinatorial problem and it suffers from the same problem in the video, so the DFS algorithm is not going to end for (n = 10). In C++, the above algorithm took some time to calculate even the modest 6x6 board, I got bored awaiting the answer for the 8x8 board and killed the process. So how can we solve this for (n = 10)? Take in account that the problem doesn't require from you to generate all the different positions (like we did here for showing them graphically), we need only the final number of positions and that could be done using (spoiler): dynamic programming.

So have fun coding, and check your heart.

2 comments:

  1. I've been stuck on this one for a while now, and am fascinated by your solution - could you post an explanation, or maybe add some comments to the gist? ( https://gist.github.com/3768893/d9d8f32bb64a8b81e5fd89f06db355c9bbbffebe )

    ReplyDelete
    Replies
    1. Hi, actually the solution here is not going to solve the problem at all, because it's going to run forever. The solution requires dynamic programming and I'm thinking in a tutorial later. More on DP that in the problem itself.

      Delete