This blog has moved to Medium

Subscribe via email

## Programming – The art of giving percise instructions

I have a friend that likes riddling me riddles, kind of like a sphinx.
The last riddle, out of a computer science course, asked to solve programatically the following problem:

You are in a lattice, at some position (x,y), with x>=0 & y>=0. How many different routes do you have for reaching the origin (0,0), where in each step you either go one unit down, or go one unit left?

I found three solutions, each better in time complexity than the previous one:

## Solution 1 – Just translate the question

As the title of this post claims, a great deal of the magic of programming is knowing how to translate problems from human language to machine language. The first algorithm should be straight forward – just do as a human would:

```public long CountWays1(int x, int y) { if (x == 0 || y ==0) { // you've reached the edge of the positive quadrant // only one way from here - either straight down or straight left return 1; }   // else, we're somewhere with x>=1 and y>=1. return // Either you go down now CountWays1(x, y-1) + // Or you go left CountWays1(x-1, y); }```

It’s easy to verify this solves the problem. No magic involved, just take the literal description of the problem and turn it into a language a computer will understand.

## Solution 2 – Add a little cache

A quick complexity analysis will reveal actually running this program on not too large inputs will never complete. The solution is hyper-exponential (grows faster than 2^(x+y)). This is because the same sub problem is computed over and over again. When calculating CountWays1(40,40), you will calculate CountWays1(20,20) many times over again (I’m ignoring integer overflow for the sake of discussion). This can be solved by adding a cache, or memory, to our solution:

```public long CountWays2(int x, int y) { long[,] solutions = new long[x,y]; return CountWays2Impl(x, y, solutions); }   private long CountWays2Impl(int x, int y, long[,] solutions) { if (x == 0 || y ==0) { // you've reached the edge of the positive quadrant // only one way from here - either straight down or straight left return 1; }   // !!! Change from CountWays1 !!! if (solutions[x,y] != 0) { // we already know the solution for (x,y), let's not recalculate it return solutions[x,y]; }   // else, we're somewhere with x>=1 and y>=1, and you don't know the answer yet long solution = // Either you go down now CountWays1(x, y-1, solutions) + // Or you go left CountWays1(x-1, y, solutions);   // !!! Change from CountWays1 !!! // We found the solution for (x,y), let's store the result in the array to avoid recalculations solutions[x,y] = solution; return solution; }```

In fact, this solution is so simple it can be “coded” in an Excel sheet. Just put 1 in the first row & column, put “=A2+B1” in the (2,2) cell, and copy this formula to all other cells.

The time and space complexity of this solution is O(x*y) – the size of our matrix, where filling every cell in it takes a constant amount of steps.

## Solution 3 – Remember Pascal

After playing with Excel and actually calculated some values, I remembered this problem is actually equivalent to Pascal’s Triangle An association every programmer should have for Pascal’s Triangle is combinatorics. If you think about the problem in terms of combinatorics instead computer science, you’ll see the solution is actually the total number of ways one can order a certain sequence of actions of length X+Y:

In X+Y moves, you will reach the origin (unless you step out of the positive quadrant). Every move is either Down or Left, so to get the number of ways you can calculate the total number of different orders of length X+Y, where every item is either Down or Left, and you have exactly X number of Lefts and Y number of Downs.

The number of ways to order a sequence of length X+Y is (X+Y)! Then, we divide by the number of ways to order the Left actions (X!), and by the number of ways to order the Down actions (Y!). This is because our initial number (X+Y)! counted the sequence “Left, Left, Down” twice.

So the most efficient way to answer the riddle is simply this:

```public long CountWays3(int x, int y) { return Factorial(x+y)/Factorial(x)/Factorial(y); }   private long Factorial(int x) { if (x <= 1) return 1;   return x*Factorial(x-1); }```

(Note this is Tail Recursion, so don’t give me arguments about inefficient recursion – this can be optimized into a loop by the compiler).

1. #### Eli:

Nice post!

2. #### Boaz:

Why “return 1”?
I believe it should be:
if (x == 0) return y;
if (y == 0) return x;

3. #### ripper234:

Thanks Eli.

Boaz, you are incorrect. If x == 0, this means you have no more Left moves left. You have only one route to get to the origin, by going Down all the time. Remember, we are calculating the number of routes to get to the origin, not the length of the route.

4. #### Boaz:

Yes, I’m stupid.
Though the fact that if x = 0 and y = 0 you return 1 confused me.

5. #### ripper234:

You’re not alone in this confusion. There is one route from the origin to itself – the empty route.

It’s basically a matter of definition, but my definition allows us to have no special cases in the 3rd solution.

6. #### A Quantum Immortal » Blog Archive » Never rely on tail recursion elimination:

[…] previously posted a recursive implementation of Factorial, and asked the readers “not to bother saying it’s inefficient because of recursion, […]

7. #### Friend:

This is not the most efficient way, because you don’t need to calculate (X+Y)! at all. Note that the biggest number will be at most (2X)!/(X!)^{2}, (by setting X=Y). So there is no need to calculate the number (X+Y)! to its bitter end. Probably the most efficient way to calculate this asymptotically, is by using Stirling’s formula. Note that by the nature of this approximation, it is evident that the complexity is way beyond exponential. It is in fact something like X^{X}.