Count Ways to Do [something]

Published: Jun 1, 2017

“Count how may ways to do …” is one type of algorithm questions. For example, count ways to climb up stairs, count ways to make sum by given coins, or count ways to reach from top left to bottom right corner.

For this type of problem, recursion and dynamic programming are often used. The recursion is an instinctive and understandable solution. However, sometime, its performance goes really worse, say, exponential. While the dynamic programming solution stays on O(n^2) time complexity.

The count ways problems have a similar idea with bits of variations. Not to confuse those, I’m going to write a memo about three problems: climbimg stairs, coin change, and unique paths.

Problem Description - Climbing Stairs

“Given two integers, m (number of steps) and n (number of stairs), find how many ways to climbing up the stair” is a typical problem of this sort. The steps can be any number between 1 to m.

For example, m = 3 and n = 5 are given, possible ways are 13 in total.


                                         0

                       /                 |        \
                     /                   |          \
                   /                     |            \
                  1                      2             3
            /     |     \          /     |   \         |  \
          /       |       \      /       |     \       |   \
         1        2        3    1        2     [3]     1   [2]
      /  | \      | \      |    | \      |             |
    /    |   \    |   \    |    |   \    |             |
   1     2   [3]  1   [2] [1]   1   [2] [1]           [1]
  / \    |        |             |
 1  [2] [1]      [1]           [1]
 |
[1]

[1, 1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 2, 1], [1, 1, 3], [1, 2, 1, 1], [1, 2, 2], [1, 3, 1]
[2, 1, 1, 1], [2, 1, 2], [2, 2, 1], [2, 3],
[3, 1, 1], [3, 2]

As in the above, 2 -> 3 and 3 -> 2 are different. The order of steps taken matters for this problem.

The idea to solve climbing stairs

Since I took a dynamic programming approach, I used an auxiliary array to save states. The auxiliary array has a length of n + 1 to have an initialization parameter. The index 0 and 1 of the array (memo) is initialized with the value 1. Then, the array will be filled out one by one while increasing number of stairs one by one. Below describes how the auxiliary array (memo[i]) will be changed in case of m = 3 and n = 4.



stairs 0   1            2                   3                          4

       0   0            0                   0                          0
           |          /   \            /    |   \                /     |     \
           1         1     2          1     2    3              1      2      3
                     |              /   \   |                 / | \    | \    |
                     1             1     2  1               /   |  \   |  \   |
                                   |                       1    2   3  1   2  1
                                   1                     /   \  |      |
                                                        1     2 1      1
                                                        |
                                                        1

memo(state)
[1, 1, 0, 0, 0]   [1, 1, 2, 0, 0]   [1, 1, 2, 4, 0]          [1, 1, 2, 4, 7]

Java code for climbing stairs

Here’s the code to count ways to climbing stairs.

The result is:

13
7
4

Problem Description - Coin Change

“Given coins (or denominators) and sum, find how many ways to make the sum” is a common problem description. Mostly, infinite number of each coins are available to make the sum. Sometime, there’s a limitation of available coins. However, basic idea is the same.

For example, coins = [1, 2, 3] and sum = 5 are given, possible ways of making sum of 5 will be: [1, 1, 1, 1, 1], [1, 1, 1, 2], [1, 2, 2], [1, 1, 3], [2, 3] So, the answer is 5.

There’s a very similar, but different coin change problem. Another coin change problem asks minimum number of coins to make a given sum. This is not what I’m going to write here. This memo is about how many combinations are there.

The idea for coin change

Like climbing stairs, I took a dynamic programming approach, I used an auxiliary table to save states. The auxiliary table (memo) has a size [number of coins + 1] x [sum + 1]. Plus one is to have initialization parameters. Table’s first column means sum = 0 and initialized with 1. This is because sum = 0 can be created by an empty set. Table’s first row (except 0) means no coins. The first row form 1 to sum + 1 will be initialized with 0. Then fill the rest of the table. The value, memo[i][j] shows a number of patters to make sum j using coins[0] to coiins[i - 1].

If coins = [1, 2, 3] and sum = 5 are given, the final auxiliary table, memo will be:

               sum
         0  1  2  3  4  5
        ------------------
coins 0| 1  0  0  0  0  0
      1| 1  1  1  1  1  1
      2| 1  1  2  2  3  3
      3| 1  1  2  3  4  5

Java code for coin change

Below is the code to count ways to making a sum.

The result is:

5
4
5

Problem Description - Unique Paths

Given two integers, m and n, which expresses m rows and n columns, find how many unique paths are there from top left to bottom right. Available directions are right and down only.

For example, m = 3 and n = 4 are given, three of all possible paths are like this:


+---+---+---+---+     +---+---+---+---+     +---+---+---+---+
| x-----------┐ |     | x-------┐ |   |     | x |   |   |   |
+---+---+---+-|-+     +---+---+-|-+---+     +-|-+---+---+---+
|   |   |   | | |     |   |   | | |   |     | └-----------┐ |
+---+---+---+-|-+     +---+---+-|-+---+     +---+---+---+-|-+
|   |   |   | x |     |   |   | └---x |     |   |   |   | x |
+---+---+---+- -+     +---+---+---+---+     +---+---+---+---+

The idea for unique paths

Like other two problems, I chose the dynamic programming to find the answer, I used auxiliary table to save states. The auxiliary table (memo) has a size [m] x [n]. In this case, plus one doesn’t need since the first row and column can be initialized without any previous state. Movements are only right and down. Given that, the first row are all 1 since from left is the only one way. The first columns are all 1 as well since from above is the only one way. Then, fill the rest of the table by expanding the path one by one, right or down.

At each table[i][j], the values on above table[i - 1][j] and left table[i][j - 1] will be added up. This way, ways to come index i, j will be calculated. When traversal reaches to the bottom right index, table[m - 1][n - 1], the answer is there.

Java code for unique paths

Below is the code to count ways to reach from top left to bottom right.

The result is:

6
10

Resources

Latest Posts

Vite + Vue + Bun on Rails

Vue.js is one of frontend frameworks gaining popularity among rapidly emerging JavaScript technologies. The combination of Vue.js and Rails is becoming more popular as well, however, Vue.js development on Rails is not so straightforward. The reason would be that Vue.js relies on Vite for a development environment such as HMR (Hot Module Replacement) and bundling.

Bun + React on Rails

In the frontend world, new technologies keep emerging rapidly these years. Still, React is a well-established and very popular frontend framework, Vue.js, Svelte, Astro and more frameworks are gaining popularity. Not just the frameworks, tools for a transpiler/bundler or sort are also under a rapid development. In JavaScript domain, esbuild, rollup, vite and some more are out.

Ruby on Rails Secrets Management

A web application needs various kinds of values, params and etc which should not be revealed to the public, say GitHub repo. For example, API keys, tokens, passwords, and endpoints, all those should be kept secret. Another important factor is that such secrets should be shared among the team members. Additionally, all those secrets should come along with the deployment.