Version 4B, notes updated 2026-07-14
Contestants on the UK Channel 4 TV game show Countdown have thirty seconds in each Numbers Round to work out how to calculate a three-digit random target (or get as close as possible). They may apply addition, subtraction, multiplication and division to some or all of the six numbers (each used at most once) from a partially random selection. This document describes a computer program that finds the best solutions to any given Numbers Round game. It is the author’s personal endeavour and is in no way affiliated.
The program reads the list of numbers and the target. It aims to find and display in human-friendly fashion all distinct ways of calculating the closest result.
It is one of many similar programs to be found online today. It started as a 1988 Christmas break project written in C, running under Xenix on a 12 MHz Intel 80286 processor. It was a challenge then to write code that would, within the time limit, find any exact solution if there was one. Version 1 did this but it took some minutes to conclude without a result when there was no exact solution. Now migrated to Linux, the same core solver runs much more quickly on a modern processor. Updates have improved the output’s completeness and, from a much more interesting angle, improved its human-friendliness through observation of how contestants solve Numbers Round problems and explain their solutions.
The program’s essential steps and its categorization of solutions (covered later in more detail) are embodied in a search procedure and a display procedure. They are:
find all of the closest candidate solutions by brute force search;
discard candidate solutions containing ineffectiveness;
keep only the candidate solution with the more human-friendly presentation when duplication occurs of one already found;
keep but mark candidate solutions containing over-complexity; and
present in human-friendly order the solutions that remain.
The baseline C code reads the problem to solve from its command line and prints the solutions. There is also a version (not yet provided) which adds a text-graphic user interface to the baseline code. This version also has extra code and a dictionary, to additionally provide suggested solutions for the Letters Round.
The program finds single number solutions from the list without arithmetic. This accommodates a target of 100 when 100 is present in the list. It has happened.
However, at the program’s core is a brute force search procedure. It takes a list of numbers, initially the original list, selects all unordered pairs and tries all allowed arithmetic operations on each pair (the appropriate way round). For each result it updates a record of how the calculation reached that result.
Whenever an operation produces an interesting result – one close enough to the target – a display procedure gets called. If it is a good candidate solution it gets stored. By default “interesting” starts at 10 away from the target. This distance reduces whenever a better result is found until only exact results are interesting. Less interesting candidate solutions found thus far get discarded.
If a result is equal to the target, onward search is terminated and the search procedure returns. Otherwise, either it calls itself recursively with a shorter numbers list where the selected pair of numbers has been replaced by the result, or it returns when the shorter list comprises just the result.
The final list of stored solutions is printed when the search is complete.
This search covers all possible calculations and will always find every solution. With six numbers and four arithmetic operators, the search procedure could in principle check up to 3,516,060 calculated results (in addition to the six checks on the numbers in the list).
The search procedure records its current calculation using a binary expression tree. The tree grows and shrinks with the recursive search. Each tree node contains two operands, pointers to where each originated (either the list or another tree node) and an operator.
The result currently reached by the search may be calculated from the tree’s root node. The full calculation at that point may be seen by following the pointers from calculated results back to the numbers from the list.
To produce just distinct solutions the search procedure discards calculations containing some types of ineffectiveness. The display procedure removes other types and detects duplication, keeping just the candidate solution with the most human-friendly presentation. It keeps but marks candidate solutions containing over-complexity so the user can decide if these are distinct or not.
These are the categorizations above in more detail:
Ineffectiveness: This arises when an operation makes no progress towards the target. The first type is a division that does not produce an integer result; the second is subtracting two identical numbers; the third is multiplying or dividing a number by one (which might alternatively be categorized as over-complexity). Whenever one of these happens, onward search is terminated, which significantly trims the number of results examined. A fourth type, handled by the display procedure, is an unconnected sub-calculation – some arithmetic that does not get used in obtaining the final result. It gets omitted when processing and storing a candidate solution.
Duplication: A brute force search generates many duplicate, arithmetically indistinguishable candidate solutions. A candidate solution is a duplicate if its fingerprint (its key) is the same as that of a stored one. Details of how the key is generated are given later. When a duplicate is found, just the candidate solution whose presentation scores better for human-friendliness is kept.
Over-complexity: This arises when an operation, or a connected sequence of operations, generates a result that is the same an input into its calculation. Obvious examples are 4x1=4, 10-5=5 and 9/3=3. These, in effect, simply discard a number. Less obvious sequences include 8–6=2 then 2x3=6 which in effect just discards 8 and 3; and 4x4=16 then 16/8=2 which effectively discards a 4. There are also much more obscure sequences. There is at least one simple sequence, 3+3=6 then 6+3=9, which effectively discards a 3 (i.e. 3+3+3=9 vs 3x3=9) but is not marked because the result is not the same as one of its inputs.
There is always a simpler corresponding solution when over-complexity arises. Does that always make it a duplicate? Solvers typically omit solutions involving 4x1=4 and similar, treating it as duplication even though it may really be over-complexity. So, simple over-complexity can be seen as duplication. However this view may be increasingly open to question for more obscure cases.
When the search procedure calls the display procedure with an interesting result, the display procedure generates the candidate solution’s key to check if it duplicates a candidate solution already found.
To generate the key, the binary (i.e. 2-way) expression tree on the stack gets printed to a string as though collapsed into an n-way expression tree. It is done by (i) merging connected sequences of add and subtract operations; (ii) merging connected sequences of multiply and divide operations; and (iii) printing each merged sub-calculation with its operations arranged so that the operands are in numerical order. Operands that are original list numbers get printed with a “tag” character to distinguish them from operands that have been calculated.
This procedure results in a set of separate sub-calculations in the string, which finally get reordered in a consistent way.
This string – the key – is a canonical way of presenting the solution that is not designed for human-friendliness but does seem to be sufficient to identify a set of duplicate solutions all sharing the same key.
When generating a key there is an important question about whether availability of choice for an operand in a calculation can lead to duplication or over-complexity. There are three cases, where the choice is: (i) between two identical numbers from the list; or (ii) between a number from the list and a partial calculation with a result that is the same; or (iii) between two partial calculations that give the same result.
Through using a tag as above, the key generation code answers “duplicate” in case (i) and “distinct” in case (ii). This is based on treating a number from the list as a symbol and a partial calculation as an expression. Identical symbols are equivalent but an expression is never equivalent to a symbol. In case (iii) the code currently always answers “duplicate”. Testing shows that using this answer very occasionally erroneously discards a distinct solution. If the answer were alternatively to always be “distinct”, the erroneous discard would not happen. Instead, some duplicate solutions would not get discarded. The code covering case (iii) needs an update because the right answer depends on specific partial calculations.
After developing this approach I found the same principle (with slightly different implementation) illustrated nicely here by Ben North. The same approach seems to be used by Alan Davies here. There is also some alignment with the approach explained here by Graeme Cole as used in his solver. If I were to look at some of the many other solvers I am sure I would find more similarities.
The key generation code is also used to generate a solution’s presentation. It operates as described above except for some differences described below to make the presentation human-friendly.
Connected sequences of operations involving just addition and subtraction are merged by default into a single sub-calculation. This is done the same way as in key generation. Contestants commonly seem to do this, and the ordering of operations and operands in a sub-calculation used in key generation is consistent with how contestants typically explain a solution.
Merging as above can also be done for sequences containing just multiplication and division. This is much less commonly done by contestants so by default this particular merge is not done. This is a difference from key generation. When a sequence of multiplications and/or divisions arises, whether they get merged or not, there is a question needing further study about whether there is any common pattern to the order in which contestants typically explain the sequence.
For solution presentation, the tag characters that identify operands from the list are not shown. Also, the key’s sub-calculation reordering is not done. This is another difference from key generation. Instead of reordering, human-friendliness is maximised by selecting from each complete set of duplicate solutions the one whose original sub-calculation order has the lowest heuristic distance score. A low distance score arises when as many sub-calculations as possible are placed immediately after the sub-calculations that make the results they use. This typically preserves a good “narrative” order – such as a contestant might use to explain a solution. The distance score code also has an element that encourages a sub-calculation display order that approaches the target as quickly as possible without going too far above, in a way contestants often seem to do.
After the search is complete, distinct solutions are scored for complexity according to a heuristic user score. Solutions with the lowest complexity are printed first. Contestants often find one of the simpler solutions. There is also an observable tendency to favour particular forms in the final sub-calculation. Some elements of this are taken into account but this needs further study.
Install a Linux operating system with gcc. Compile from a command line with gcc ‑O3 ‑o numbers-tty numbers-tty.c.
To solve example 1 below run “./numbers-tty 10 4 1 7 7 75 =778” from a command line. A problem’s numbers and target may appear in any order with the target identified by a preceding “=”.
There are game policy switches in the code for turning on or off validity checks for the numbers list and for setting its acceptable length. The range of acceptable target values can be specified. The minimum must be 1 or more. A maximum of 0 is interpreted as unlimited. The maximum distance between result and target that makes a solution interesting can be set.
There are constants for enabling or disabling solution sub-calculation merging (both types) and for tuning the heuristic scores. Over-complex solutions may be shown or excluded and the maximum complexity level at which they get marked can be set. Quite a few solution display options can be changed.
To test speed, run “time numbers-tty <problem X> >/dev/null” and record user time. On 2024-07-07 with an Intel Celeron processor N5015:
<problem A>: 1 1 2 2 3 4 =959 takes 8-14 ms (this tests searching only – there are no close enough solutions to display);
<problem B>: 3 2 5 8 4 9 =120 takes 35-47 ms (this tests searching plus processing to display 261 distinct solutions);
<problem C>: 1 2 3 4 5 6 =7 takes 132-142 ms (this tests searching plus processing to display 1015 distinct solutions – it needs the default minimum target reduced and the maximum solution store size increased).
Reproduced below is a selection of problems and solutions from a bigger suite of examples used for testing the solver (hence discontinuous numbering). If “*” is tagged onto to the solution number it contains over-complexity. Note that ordering and presentation depend on the tuning of the heuristic scoring. This might have changed since the examples were created.
Make
778 from 10 4 1 7 7 75
[1] 75x10=750 7x4=28
750+28=778
[2] 75+1=76 76x10=760 760+7+7+4=778
[3]
75x10=750 4+1=5 7x5=35 750+35-7=778
[4] 75x10=750 4-1=3
7x3=21 750+21+7=778
[5]* 75x10=750 7+1-4=4 7x4=28
750+28=778
Make
849 from 100 3 2 7 10 10
[1] 10-2=8 100x8=800
10-3=7 7x7=49 800+49=849
[2] 100+10-3=107 10-2=8 107x8=856
856-7=849
[3] 100+7=107 10-2=8 107x8=856 856+3-10=849
Make
669 from 50 100 75 25 7 9
[1] 75x9=675
100+50=150 150/25=6 675-6=669
[2] 100x7=700 50x9=450
450/75=6 700-25-6=669
Make
643 from 25 100 9 9 1 1
[1] 25x9=225
225+100=325 1+1=2 325x2=650 650-9=641
[2] 100-1=99
99x9=891 9+1=10 25x10=250 891-250=641
Make
477 from 8 4 7 4 3 50
[1] 50x8=400 4+4+3=11
11x7=77 400+77=477
[2] 50+3=53 8+4+4-7=9 53x9=477
[3]
50+7-4=53 8+4-3=9 53x9=477
[4] 50+3=53 8/4=2 7+2=9
53x9=477
[5] 50+3=53 4/4=1 8+1=9 53x9=477
[6]
50-7=43 8+3=11 43x11=473 473+4=477
[7] 50+3=53 4x4=16
16-7=9 53x9=477
[8] 50x8=400 7+4=11 4+3=7 11x7=77
400+77=477
[9] 4+4=8 50x8=400 8+3=11 11x7=77
400+77=477
[10]* 50+3=53 8/4=2 7+4-2=9 53x9=477
[11]
50-8-4=38 38x4=152 152+7=159 159x3=477
[12]* 50+3=53 8/4=2
4/2=2 7+2=9 53x9=477
[13] 4+4=8 8x8=64 64-3=61 61x7=427
427+50=477
[14]* 50+3=53 8-4=4 4x4=16 16-7=9 53x9=477
[15]
50+3=53 7x4=28 28+8=36 36/4=9 53x9=477
Make
701 from 25 9 10 2 3 8
[1] 10x3=30 30-2=28
28x25=700 700+9-8=701
[2] 25x9=225 225x3=675 8x2=16
675+16+10=701
[3] 9x8=72 72x10=720 3x2=6 720+6-25=701
[4]
25x3=75 75+2=77 77x9=693 693+8=701
[5] 10x9=90 90-2=88
88x8=704 704-3=701
[6] 25x9=225 225+8=233 233x3=699
699+2=701
[7] 25+10+9=44 44x8=352 352x2=704 704-3=701
[8]
10x2=20 20+9=29 29x25=725 8x3=24 725-24=701
[9]
25x10=250 250-9-8=233 233x3=699 699+2=701
[10] 8/2=4
25x3=75 75-4=71 71x10=710 710-9=701
[11] 25x3=75 8/2=4
75+4=79 79x9=711 711-10=701
[12] 25-2=23 23x9=207 207x3=621
10x8=80 621+80=701
[13] 25x9=225 225+10=235 235x3=705
8/2=4 705-4=701
Make
718 from 3 5 3 7 10 75
[1] 75x10=750 7x5=35
750+3-35=718
[2] 75-3=72 72x10=720 720+5-7=718
[3]
75-3=72 72x10=720 720+3-5=718
[4] 75+3-7=71 71x10=710
710+5+3=718
[5] 75-5=70 70x10=700 7x3=21 700+21-3=718
[6]
75x3=225 225x3=675 10x5=50 675+50-7=718
[7] 75x10=750
5+3=8 7-3=4 8x4=32 750-32=718
[8] 75-3=72 72x10=720
7+3=10 10/5=2 720-2=718
[9] 75-3=72 7+3=10 72x10=720
10/5=2 720-2=718
Make
556 from 10 4 1 7 7 75
[1] 75+4-1=78 78x7=546
546+10=556
[2] 75+4=79 79x7=553 553+10-7=556
[3]
75+7-4=78 78x7=546 546+10=556
[4] 7+1=8 10x8=80 80x7=560
560-4=556
[5] 75x7=525 4-1=3 7x3=21 525+21+10=556
[6]
75x7=525 7-4=3 10x3=30 525+30+1=556
[7] 10x7=70
75+70+1-7=139 139x4=556
[8] 75+10+7=92 7-1=6 92x6=552
552+4=556
[9] 75-1=74 74x7=518 7x4=28 518+28+10=556
[10]
7+7=14 14x10=140 140-1=139 139x4=556
Make
794 from 25 100 2 3 2 6
[1] 3x2=6 6+2=8
100x8=800 800-6=794
[2] 6+2=8 100x8=800 3x2=6
800-6=794
[3] 100-2-2=96 96/3=32 32x25=800 800-6=794
[4]
100+2=102 6+2=8 102x8=816 816+3-25=794
[5] 100+2=102
102/3=34 34-2=32 32x25=800 800-6=794
[6] 2x2=4 100-4=96
96/3=32 32x25=800 800-6=794
[7] 2/2=1 25-1=24 24/3=8
100x8=800 800-6=794
[8] 6-2=4 100x4=400 400-3=397
397x2=794
[9] 6x2=12 25x12=300 300+100-3=397 397x2=794
[10]
6x2=12 25x12=300 300-2=298 298x3=894 894-100=794
Make
732 from 4 2 9 5 7 7
[1] 9x7=63 63-2=61 7+5=12
61x12=732
[2]* 9x7=63 63+2-4=61 7+5=12 61x12=732
[3]
9x5=45 45+7=52 52x2=104 104x7=728 728+4=732
[4] 4+2=6
9x6=54 54+7=61 7+5=12 61x12=732
[5]* 9x7=63 4/2=2 63-2=61
7+5=12 61x12=732
Graeme Cole’s numbers game solver (https://graemecole.uk/countdown/), when asked to find all solutions (2025-06), shows a few differences compared to my examples. The differences all seem to be related to showing some solutions that I categorize as distinct but over-complex, but not showing others I categorize the same way.
These
are Quantum Tombola’s
solutions to example 16 above (the mobile page, 2025-06, with
Selection 4 2 9 5 7 7 and Target 732):
732
(9 × 7 − 2)
× (7 + 5)
(9 × 5 + 7) × 7 × 2 + 4
(9 × (4 + 2) + 7) ×
(7 + 5)
(9 × 7 + 2 − 4) × (7 + 5)
This omits the baseline code’s distinct but over-complex solution [5]. However the output includes the baseline code’s distinct but over-complex solution [2] (the fourth solution above). It is not clear whether this difference arises from Quantum Tombola’s different definition of distinctiveness as set out here; or from a design decision; or from something else.
Broadly considering problem B (make 120 from 3 2 5 8 4 9), Quantum Tombola gives 214 distinct solutions. The baseline code gives 261 (or 191, omitting over-complex solutions).
Incidentally, when problem B appeared on air Rachel Riley declared there to be 214 solutions. On another occasion Rachel confirmed what everyone knows, that no computer is used in the studio, but added that the control room uses a computer to check whether a numbers round problem can be solved or not. It looks likely that the control room uses Quantum Tombola – it was written by a Countdown series champion.
Ben North’s solver (https://bennorth.github.io/countdown-numbers-solver/) gives the same five distinct solutions to example 16 as the baseline code (including the over-complex ones). That is after removing duplications caused by a duplicated number in the list (known and mentioned on his web site).
This solver seems to reproduce my other examples too (2025-06). It bases its definition of distinctiveness on “Counting dendrograms: A survey” by Fionn Murtagh, published in “Discrete Applied Mathematics 7 (1984)”. The code I arrived at seems to do the same but I have seen a difference. For problem B, Ben North’s solver gives 264 solutions (2026-06). The baseline code, including over-complex solutions, gives 261. I have not identified the specific differences but I suspect the baseline code discards three distinct solutions for the reason set out earlier.
Alan Davies’ solver (https://alandavies.org/blog/2024/04/11/on-the-countdown-numbers-game) gives these solutions for example16 (2026-07):
target
= 732, numbers = [2, 4, 5, 7, 7, 9]
732 = ((((2 + 4) * 9) + 7) *
(5 + 7))
732 = (((7 * 9) - (4 - 2)) * (5 + 7))
732 = (((7 *
9) - (4 / 2)) * (5 + 7))
732 = (((2 * 7) * ((5 * 9) + 7)) +
4)
732 = (((2 + (7 * 9)) - 4) * (5 + 7))
732 = (((7 * 9) -
2) * (5 + 7))
All of my solutions are there (including the over-complex ones). This solver adds one more: its fifth solution. However that seems to duplicate its second.
Furthermore, the solver gives 353 solutions to problem B which is many more than other solvers.
This solver takes the attractive approach of using an existing symbolic mathematics library called SymPy which exposes functionality to parse expression strings into a standard expression tree form. This is used create a canonical representation for each solution expression that is used as the key to remove duplicates. That seems to be the right approach, just as adopted by others. However the implementation seems to produce a slightly different outcome. A look into the SymPy code might shed some light on the differences.
The cases above have not been investigated fully but study might shed more light on the differences between how solvers treat the issues of distinctiveness, over-complexity and the boundary between them.
Over-complexity can range from obvious to obscure. The baseline code by design does not even generate candidate solutions containing steps like 4x1=4. Online solvers seem typically to do this, maybe primarily to trim the search tree. An obvious over-complexity like 4-2=2 may be worth marking or even worth omitting because it clearly just discards the 4. However non-obvious over-complexities exist such as (9x4)- (8x3)=12. Using the same numbers (3, 4, 8, 9) the 12 may be made from 4x3 which avoids discarding 9 and 8; or from 9+3 which avoids discarding 4 and 8. I suspect some might consider obvious examples as duplication but perhaps not more obscure ones. Hence keeping an over-complex solution, marking it and letting the user decide seems to be appropriate.
For distinctiveness, for now I view Ben North’s approach as a reference but others are very similar. Its implementation – as he writes – needs work to deal with duplicated numbers in the list. I believe I know, as described above, where my implementation falls short of this reference. Right now I think the baseline code gets close enough to the reference to be considered good. Nevertheless, if time permits I may look at it again.
Graeme Cole wrote “It turns out that finding a good way of deciding that two expressions are equivalent is important when writing a numbers solver.” I agree. The small differences observed between good online solvers seem to arise from subtly different answers to the elements of the distinct solutions question.
John A. Phillips