Vietnamese math puzzle

Alex Bellos shared the following math puzzle that was given to Vietnamese eight-year-olds. The challenge is to place the digits from 1 to 9 into the grid.

Vietnamese math puzzle

Preliminary remarks

  1. Although it wasn’t stated explicitly, I assume that each digit should be used only once.
  2. The colon signifies division.
  3. There are many correct answers. I assume that students were meant to use enlightened trial and error to find a solution, but were not expected to find all solutions.
  4. I am assuming the standard order of operations, but I am not sure if this was the original intention. Would an eight-year-old be expected to understand that multiplication and division have higher precedence than addition and subtraction?
  5. Frankly, this puzzle is kind of boring for grown-ups.

Python code

Searching for solutions by hand did not appeal to me, so I wrote a Python program to search for all solutions. I hope that this example is helpful for people who are learning Python. (Perhaps it will inspire someone to write a better program than this.)

from itertools import permutations
standard_order = True
tolerance = 1e-7
target = 66

if standard_order:
    expr = "? + 13 * ? / ? + ? + 12 * ? - ? - 11 + ? * ? / ? - 10"
else:
    expr = "(((? + 13) * ? / ? + ? + 12) * ? - ? - 11 + ?) * ? / ? - 10"
expr = expr.replace("?", "%d")

for inputs in permutations(range(1,10)):
    expression = expr % inputs
    result = eval(expression)
    if abs(result - target) < tolerance:
        print ("%s = %s" % (expression, target))

Discussion

The itertools package provides many useful tools for iteration, including the permutation function, which generates all permutations of a list. Our script iterates through all 362880 permutations of the set of integers from 1 to 9.

Each permutation is substituted into the expression and evaluated. The program uses the eval function, which allows Python to run Python code within itself. This can be very dangerous, so use this function with care.

The division operations can lead to round-off error, so we only check that the result is very close to the target value. The variable tolerance sets the required precision.

Output

There are 136 solutions.

1 + 13 * 2 / 6 + 4 + 12 * 7 - 8 - 11 + 3 * 5 / 9 - 10 = 66
1 + 13 * 2 / 6 + 4 + 12 * 7 - 8 - 11 + 5 * 3 / 9 - 10 = 66
1 + 13 * 3 / 2 + 4 + 12 * 5 - 8 - 11 + 7 * 9 / 6 - 10 = 66
1 + 13 * 3 / 2 + 4 + 12 * 5 - 8 - 11 + 9 * 7 / 6 - 10 = 66
1 + 13 * 3 / 2 + 9 + 12 * 5 - 6 - 11 + 4 * 7 / 8 - 10 = 66
1 + 13 * 3 / 2 + 9 + 12 * 5 - 6 - 11 + 7 * 4 / 8 - 10 = 66
1 + 13 * 3 / 4 + 7 + 12 * 6 - 5 - 11 + 2 * 9 / 8 - 10 = 66
1 + 13 * 3 / 4 + 7 + 12 * 6 - 5 - 11 + 9 * 2 / 8 - 10 = 66
1 + 13 * 3 / 6 + 2 + 12 * 7 - 9 - 11 + 4 * 5 / 8 - 10 = 66
1 + 13 * 3 / 6 + 2 + 12 * 7 - 9 - 11 + 5 * 4 / 8 - 10 = 66
1 + 13 * 3 / 9 + 4 + 12 * 7 - 8 - 11 + 2 * 5 / 6 - 10 = 66
1 + 13 * 3 / 9 + 4 + 12 * 7 - 8 - 11 + 5 * 2 / 6 - 10 = 66
1 + 13 * 4 / 8 + 2 + 12 * 7 - 9 - 11 + 3 * 5 / 6 - 10 = 66
1 + 13 * 4 / 8 + 2 + 12 * 7 - 9 - 11 + 5 * 3 / 6 - 10 = 66
1 + 13 * 5 / 2 + 3 + 12 * 4 - 8 - 11 + 7 * 9 / 6 - 10 = 66
1 + 13 * 5 / 2 + 3 + 12 * 4 - 8 - 11 + 9 * 7 / 6 - 10 = 66
1 + 13 * 5 / 2 + 8 + 12 * 4 - 7 - 11 + 3 * 9 / 6 - 10 = 66
1 + 13 * 5 / 2 + 8 + 12 * 4 - 7 - 11 + 9 * 3 / 6 - 10 = 66
1 + 13 * 5 / 3 + 9 + 12 * 4 - 2 - 11 + 7 * 8 / 6 - 10 = 66
1 + 13 * 5 / 3 + 9 + 12 * 4 - 2 - 11 + 8 * 7 / 6 - 10 = 66
1 + 13 * 8 / 3 + 7 + 12 * 4 - 5 - 11 + 2 * 6 / 9 - 10 = 66
1 + 13 * 8 / 3 + 7 + 12 * 4 - 5 - 11 + 6 * 2 / 9 - 10 = 66
1 + 13 * 9 / 6 + 4 + 12 * 5 - 8 - 11 + 3 * 7 / 2 - 10 = 66
1 + 13 * 9 / 6 + 4 + 12 * 5 - 8 - 11 + 7 * 3 / 2 - 10 = 66
1 + 13 * 9 / 6 + 7 + 12 * 5 - 2 - 11 + 3 * 4 / 8 - 10 = 66
1 + 13 * 9 / 6 + 7 + 12 * 5 - 2 - 11 + 4 * 3 / 8 - 10 = 66
2 + 13 * 1 / 4 + 3 + 12 * 7 - 9 - 11 + 5 * 6 / 8 - 10 = 66
2 + 13 * 1 / 4 + 3 + 12 * 7 - 9 - 11 + 6 * 5 / 8 - 10 = 66
2 + 13 * 3 / 6 + 1 + 12 * 7 - 9 - 11 + 4 * 5 / 8 - 10 = 66
2 + 13 * 3 / 6 + 1 + 12 * 7 - 9 - 11 + 5 * 4 / 8 - 10 = 66
2 + 13 * 4 / 8 + 1 + 12 * 7 - 9 - 11 + 3 * 5 / 6 - 10 = 66
2 + 13 * 4 / 8 + 1 + 12 * 7 - 9 - 11 + 5 * 3 / 6 - 10 = 66
2 + 13 * 6 / 9 + 8 + 12 * 5 - 1 - 11 + 4 * 7 / 3 - 10 = 66
2 + 13 * 6 / 9 + 8 + 12 * 5 - 1 - 11 + 7 * 4 / 3 - 10 = 66
2 + 13 * 8 / 6 + 9 + 12 * 4 - 1 - 11 + 5 * 7 / 3 - 10 = 66
2 + 13 * 8 / 6 + 9 + 12 * 4 - 1 - 11 + 7 * 5 / 3 - 10 = 66
2 + 13 * 9 / 6 + 3 + 12 * 5 - 1 - 11 + 4 * 7 / 8 - 10 = 66
2 + 13 * 9 / 6 + 3 + 12 * 5 - 1 - 11 + 7 * 4 / 8 - 10 = 66
3 + 13 * 1 / 4 + 2 + 12 * 7 - 9 - 11 + 5 * 6 / 8 - 10 = 66
3 + 13 * 1 / 4 + 2 + 12 * 7 - 9 - 11 + 6 * 5 / 8 - 10 = 66
3 + 13 * 2 / 1 + 5 + 12 * 4 - 7 - 11 + 8 * 9 / 6 - 10 = 66
3 + 13 * 2 / 1 + 5 + 12 * 4 - 7 - 11 + 9 * 8 / 6 - 10 = 66
3 + 13 * 2 / 4 + 8 + 12 * 5 - 1 - 11 + 7 * 9 / 6 - 10 = 66
3 + 13 * 2 / 4 + 8 + 12 * 5 - 1 - 11 + 9 * 7 / 6 - 10 = 66
3 + 13 * 2 / 8 + 6 + 12 * 5 - 1 - 11 + 7 * 9 / 4 - 10 = 66
3 + 13 * 2 / 8 + 6 + 12 * 5 - 1 - 11 + 9 * 7 / 4 - 10 = 66
3 + 13 * 5 / 2 + 1 + 12 * 4 - 8 - 11 + 7 * 9 / 6 - 10 = 66
3 + 13 * 5 / 2 + 1 + 12 * 4 - 8 - 11 + 9 * 7 / 6 - 10 = 66
3 + 13 * 6 / 4 + 9 + 12 * 5 - 8 - 11 + 1 * 7 / 2 - 10 = 66
3 + 13 * 6 / 4 + 9 + 12 * 5 - 8 - 11 + 7 * 1 / 2 - 10 = 66
3 + 13 * 9 / 2 + 8 + 12 * 1 - 5 - 11 + 6 * 7 / 4 - 10 = 66
3 + 13 * 9 / 2 + 8 + 12 * 1 - 5 - 11 + 7 * 6 / 4 - 10 = 66
3 + 13 * 9 / 6 + 2 + 12 * 5 - 1 - 11 + 4 * 7 / 8 - 10 = 66
3 + 13 * 9 / 6 + 2 + 12 * 5 - 1 - 11 + 7 * 4 / 8 - 10 = 66
4 + 13 * 2 / 6 + 1 + 12 * 7 - 8 - 11 + 3 * 5 / 9 - 10 = 66
4 + 13 * 2 / 6 + 1 + 12 * 7 - 8 - 11 + 5 * 3 / 9 - 10 = 66
4 + 13 * 3 / 2 + 1 + 12 * 5 - 8 - 11 + 7 * 9 / 6 - 10 = 66
4 + 13 * 3 / 2 + 1 + 12 * 5 - 8 - 11 + 9 * 7 / 6 - 10 = 66
4 + 13 * 3 / 9 + 1 + 12 * 7 - 8 - 11 + 2 * 5 / 6 - 10 = 66
4 + 13 * 3 / 9 + 1 + 12 * 7 - 8 - 11 + 5 * 2 / 6 - 10 = 66
4 + 13 * 9 / 6 + 1 + 12 * 5 - 8 - 11 + 3 * 7 / 2 - 10 = 66
4 + 13 * 9 / 6 + 1 + 12 * 5 - 8 - 11 + 7 * 3 / 2 - 10 = 66
5 + 13 * 1 / 2 + 9 + 12 * 6 - 7 - 11 + 3 * 4 / 8 - 10 = 66
5 + 13 * 1 / 2 + 9 + 12 * 6 - 7 - 11 + 4 * 3 / 8 - 10 = 66
5 + 13 * 2 / 1 + 3 + 12 * 4 - 7 - 11 + 8 * 9 / 6 - 10 = 66
5 + 13 * 2 / 1 + 3 + 12 * 4 - 7 - 11 + 9 * 8 / 6 - 10 = 66
5 + 13 * 3 / 1 + 7 + 12 * 2 - 6 - 11 + 8 * 9 / 4 - 10 = 66
5 + 13 * 3 / 1 + 7 + 12 * 2 - 6 - 11 + 9 * 8 / 4 - 10 = 66
5 + 13 * 4 / 1 + 9 + 12 * 2 - 7 - 11 + 3 * 8 / 6 - 10 = 66
5 + 13 * 4 / 1 + 9 + 12 * 2 - 7 - 11 + 8 * 3 / 6 - 10 = 66
5 + 13 * 4 / 8 + 9 + 12 * 6 - 7 - 11 + 1 * 3 / 2 - 10 = 66
5 + 13 * 4 / 8 + 9 + 12 * 6 - 7 - 11 + 3 * 1 / 2 - 10 = 66
5 + 13 * 7 / 2 + 8 + 12 * 3 - 9 - 11 + 1 * 6 / 4 - 10 = 66
5 + 13 * 7 / 2 + 8 + 12 * 3 - 9 - 11 + 6 * 1 / 4 - 10 = 66
5 + 13 * 9 / 3 + 6 + 12 * 2 - 1 - 11 + 7 * 8 / 4 - 10 = 66
5 + 13 * 9 / 3 + 6 + 12 * 2 - 1 - 11 + 8 * 7 / 4 - 10 = 66
6 + 13 * 2 / 8 + 3 + 12 * 5 - 1 - 11 + 7 * 9 / 4 - 10 = 66
6 + 13 * 2 / 8 + 3 + 12 * 5 - 1 - 11 + 9 * 7 / 4 - 10 = 66
6 + 13 * 3 / 1 + 9 + 12 * 2 - 5 - 11 + 7 * 8 / 4 - 10 = 66
6 + 13 * 3 / 1 + 9 + 12 * 2 - 5 - 11 + 8 * 7 / 4 - 10 = 66
6 + 13 * 9 / 3 + 5 + 12 * 2 - 1 - 11 + 7 * 8 / 4 - 10 = 66
6 + 13 * 9 / 3 + 5 + 12 * 2 - 1 - 11 + 8 * 7 / 4 - 10 = 66
7 + 13 * 1 / 4 + 9 + 12 * 6 - 5 - 11 + 2 * 3 / 8 - 10 = 66
7 + 13 * 1 / 4 + 9 + 12 * 6 - 5 - 11 + 3 * 2 / 8 - 10 = 66
7 + 13 * 2 / 8 + 9 + 12 * 6 - 5 - 11 + 1 * 3 / 4 - 10 = 66
7 + 13 * 2 / 8 + 9 + 12 * 6 - 5 - 11 + 3 * 1 / 4 - 10 = 66
7 + 13 * 3 / 1 + 5 + 12 * 2 - 6 - 11 + 8 * 9 / 4 - 10 = 66
7 + 13 * 3 / 1 + 5 + 12 * 2 - 6 - 11 + 9 * 8 / 4 - 10 = 66
7 + 13 * 3 / 2 + 8 + 12 * 5 - 9 - 11 + 1 * 6 / 4 - 10 = 66
7 + 13 * 3 / 2 + 8 + 12 * 5 - 9 - 11 + 6 * 1 / 4 - 10 = 66
7 + 13 * 3 / 4 + 1 + 12 * 6 - 5 - 11 + 2 * 9 / 8 - 10 = 66
7 + 13 * 3 / 4 + 1 + 12 * 6 - 5 - 11 + 9 * 2 / 8 - 10 = 66
7 + 13 * 5 / 2 + 8 + 12 * 4 - 9 - 11 + 1 * 3 / 6 - 10 = 66
7 + 13 * 5 / 2 + 8 + 12 * 4 - 9 - 11 + 3 * 1 / 6 - 10 = 66
7 + 13 * 6 / 4 + 8 + 12 * 5 - 9 - 11 + 1 * 3 / 2 - 10 = 66
7 + 13 * 6 / 4 + 8 + 12 * 5 - 9 - 11 + 3 * 1 / 2 - 10 = 66
7 + 13 * 8 / 3 + 1 + 12 * 4 - 5 - 11 + 2 * 6 / 9 - 10 = 66
7 + 13 * 8 / 3 + 1 + 12 * 4 - 5 - 11 + 6 * 2 / 9 - 10 = 66
7 + 13 * 9 / 6 + 1 + 12 * 5 - 2 - 11 + 3 * 4 / 8 - 10 = 66
7 + 13 * 9 / 6 + 1 + 12 * 5 - 2 - 11 + 4 * 3 / 8 - 10 = 66
8 + 13 * 2 / 4 + 3 + 12 * 5 - 1 - 11 + 7 * 9 / 6 - 10 = 66
8 + 13 * 2 / 4 + 3 + 12 * 5 - 1 - 11 + 9 * 7 / 6 - 10 = 66
8 + 13 * 3 / 2 + 7 + 12 * 5 - 9 - 11 + 1 * 6 / 4 - 10 = 66
8 + 13 * 3 / 2 + 7 + 12 * 5 - 9 - 11 + 6 * 1 / 4 - 10 = 66
8 + 13 * 5 / 2 + 1 + 12 * 4 - 7 - 11 + 3 * 9 / 6 - 10 = 66
8 + 13 * 5 / 2 + 1 + 12 * 4 - 7 - 11 + 9 * 3 / 6 - 10 = 66
8 + 13 * 5 / 2 + 7 + 12 * 4 - 9 - 11 + 1 * 3 / 6 - 10 = 66
8 + 13 * 5 / 2 + 7 + 12 * 4 - 9 - 11 + 3 * 1 / 6 - 10 = 66
8 + 13 * 6 / 4 + 7 + 12 * 5 - 9 - 11 + 1 * 3 / 2 - 10 = 66
8 + 13 * 6 / 4 + 7 + 12 * 5 - 9 - 11 + 3 * 1 / 2 - 10 = 66
8 + 13 * 6 / 9 + 2 + 12 * 5 - 1 - 11 + 4 * 7 / 3 - 10 = 66
8 + 13 * 6 / 9 + 2 + 12 * 5 - 1 - 11 + 7 * 4 / 3 - 10 = 66
8 + 13 * 7 / 2 + 5 + 12 * 3 - 9 - 11 + 1 * 6 / 4 - 10 = 66
8 + 13 * 7 / 2 + 5 + 12 * 3 - 9 - 11 + 6 * 1 / 4 - 10 = 66
8 + 13 * 9 / 2 + 3 + 12 * 1 - 5 - 11 + 6 * 7 / 4 - 10 = 66
8 + 13 * 9 / 2 + 3 + 12 * 1 - 5 - 11 + 7 * 6 / 4 - 10 = 66
9 + 13 * 1 / 2 + 5 + 12 * 6 - 7 - 11 + 3 * 4 / 8 - 10 = 66
9 + 13 * 1 / 2 + 5 + 12 * 6 - 7 - 11 + 4 * 3 / 8 - 10 = 66
9 + 13 * 1 / 4 + 7 + 12 * 6 - 5 - 11 + 2 * 3 / 8 - 10 = 66
9 + 13 * 1 / 4 + 7 + 12 * 6 - 5 - 11 + 3 * 2 / 8 - 10 = 66
9 + 13 * 2 / 8 + 7 + 12 * 6 - 5 - 11 + 1 * 3 / 4 - 10 = 66
9 + 13 * 2 / 8 + 7 + 12 * 6 - 5 - 11 + 3 * 1 / 4 - 10 = 66
9 + 13 * 3 / 1 + 6 + 12 * 2 - 5 - 11 + 7 * 8 / 4 - 10 = 66
9 + 13 * 3 / 1 + 6 + 12 * 2 - 5 - 11 + 8 * 7 / 4 - 10 = 66
9 + 13 * 3 / 2 + 1 + 12 * 5 - 6 - 11 + 4 * 7 / 8 - 10 = 66
9 + 13 * 3 / 2 + 1 + 12 * 5 - 6 - 11 + 7 * 4 / 8 - 10 = 66
9 + 13 * 4 / 1 + 5 + 12 * 2 - 7 - 11 + 3 * 8 / 6 - 10 = 66
9 + 13 * 4 / 1 + 5 + 12 * 2 - 7 - 11 + 8 * 3 / 6 - 10 = 66
9 + 13 * 4 / 8 + 5 + 12 * 6 - 7 - 11 + 1 * 3 / 2 - 10 = 66
9 + 13 * 4 / 8 + 5 + 12 * 6 - 7 - 11 + 3 * 1 / 2 - 10 = 66
9 + 13 * 5 / 3 + 1 + 12 * 4 - 2 - 11 + 7 * 8 / 6 - 10 = 66
9 + 13 * 5 / 3 + 1 + 12 * 4 - 2 - 11 + 8 * 7 / 6 - 10 = 66
9 + 13 * 6 / 4 + 3 + 12 * 5 - 8 - 11 + 1 * 7 / 2 - 10 = 66
9 + 13 * 6 / 4 + 3 + 12 * 5 - 8 - 11 + 7 * 1 / 2 - 10 = 66
9 + 13 * 8 / 6 + 2 + 12 * 4 - 1 - 11 + 5 * 7 / 3 - 10 = 66
9 + 13 * 8 / 6 + 2 + 12 * 4 - 1 - 11 + 7 * 5 / 3 - 10 = 66

Russian peasant multiplication and loop invariants

Russian peasant multiplication is a method for multiplying numbers based on repeated doubling and halving. Nobody is quite sure why it is called “Russian peasant multiplication,” but the name has stuck.

Here is the procedure:

  1. Write each number at the head of a column.
  2. Double the number in the first column.
  3. Halve the number in the second column. (If odd, ignore the remainder.)
  4. Repeat (2) and (3) until the number in the second column is 1.
  5. Cross out the rows with an even number in the second column.
  6. Add up the remaining numbers in the first column.

Example: Multiply 43 by 18.

First number Second number
 43   18 
\(86\) \(9\)
 172   4 
 344   2 
\(688\) \(1\)

\(\implies 43 \times 18 = 86 + 688 = 774.\)

Here is a Python implementation of the algorithm.

def multiply(m, n):
    p = 0
    while n > 0:
        if n % 2: # n is odd
            p = p + m
        m = m * 2
        n = n // 2
    return p

Correctness

Let’s see what happens to the values of \(m\), \(n\), and \(p\) in one iteration. If \(n\) is even, then \(m\) is doubled and \(n\) is halved, so the product \(mn\) is unchanged. If \(n\) is odd, then \(m\) is doubled and \(n\) is replaced with \((n-1)/2\), so the value of the product \(mn\) is decreased.
\[(2m) \cdot (n-1)/2 = mn – m\]
To compensate for this decrease, the value of \(p\) is increased by \(m\) when \(n\) is odd. Consequently, the value of \(Q = mn + p\) does not change – it is a loop invariant. At the beginning of the loop we have \(Q = mn\) (since \(p=0\)) and at the end we have \(Q = p\) (since \(n=0\)). Therefore, the product \(mn\) is equal to the final value of \(p\).

A decimal variation

The Russian Peasant method is associated with binary (base two) arithmetic, since it involves multiplying and dividing by two. Is there a decimal (base ten) version? Consider the following:

def multiply_decimal(m, n):
    p = 0
    while n > 0:
        r = n % 10
        p = p + m * r
        m = m * 10
        n = n // 10
    return p

Let’s analyze this algorithm. Each time through the loop, the values of \(m\), \(n\), and \(p\) are replaced with new values \(m’\), \(n’\), and \(p’\). The following relations hold between the new values and the old values.
\[
\begin{align*}
m’ &= 10m\\
n &= 10n’ + r\\
p’ &= p + mr
\end{align*}
\]

As before, the quantity \(Q = mn+p\) is a loop invariant, since
\(
mn + p = m(10n’ + r) + p = (10m)n’ + (p + mr) = m’n’ + p’.
\)
Therefore, the function returns the product \(mn\).

Example: Multiply 123 by 456.

\(m\) \(n\) \(mr\)
\(123\) \(456\) \(123 \times 6 = 738\)
\(1230\) \(45\) \(1230 \times 5 = 6150\)
\(12300\) \(4\) \(12300 \times 4 = 49200\)

\(\implies 123 \times 456 = 738 + 6150 + 49200 = 56088.\)

Note that this is essentially the standard algorithm for multiplying numbers.

  123
× 456
—————
  738
 6150
49200
—————
56088

Fast exponentiation

We have seen that we multiply numbers via repeated doubling. In a similar way, we can raise a number to a power via repeated squaring. Here is a Python implementation:

def power(m, n):
    p = 1
    while n > 0:
       if n % 2: # n is odd
            p = p * m
        m = m * m
        n = n // 2
    return p

The loop invariant \(Q = m^n p\) can be used to prove that the function correctly returns the value of \(m^n\).