This is a number puzzle I saw in a newspaper when I was a kid. I do not know the official name of the game, but its objective is to find the smallest expression that produces a value using a single digit.
It is easier to understand this by looking at a problem.
Say we are asked to find combinations to produce the number 75.
A simple case is using the digit 3.
- The number
75is divisible by3, so we would think3 * 25, except we cannot use25. - So now we need to look for an expression that evaluates to
25. A good option could be27 - 2. - The first term is simply
3 ^ 3. - Now we need an expression that produces
2in terms of3. We could use3 - 1. - And use
3 / 3to produce that1.
So a valid combination would be
Continuing with this example, we can try another digit, say 6.
Notice that 6 * 12 is 72, which gets us closer to the target.
So we can think of 75 as 72 + 3.
The first term (72) is easy (6 * 12), but getting the second term, 3, requires some creative thinking.
Eventually we end up with something like
The cost of a solution is determined by the number of times the digit is used.
So the solution 6.
Likewise, the solution using the digit 6 has a cost of 7.
We can improve both solutions.
One solution for the digit 3 would be 81 - 6, and has a cost of 4.
And an improvement on the solution with the digit 6 could be 6.
The operations allowed also impact the difficulty of the game.
Allowing only additions is the most limiting case.
It makes for an easy game without much challenge.
For example, using the digit 5, we can only do:
10 = 5 + 5
15 = 5 + 5 + 5
20 = 5 + 5 + 5 + 5
25 = 5 + 5 + 5 + 5 + 5
...
Expanding the operations to allow multiplication results in less costly solutions:
25 = 5 + 5 + 5 + 5 + 5
25 = 5 * 5
And adding subtraction and division can both generate smaller solutions, and generate numbers we couldn't produce before.
20 = 5 * 5 - 5
4 = 5 - 5/5
Many magazine versions of the game stop there.
But online puzzles tend to include more operations.
I have seen some with exponentiation (^), factorial (!) and square root (sqrt).
And some even allow concatenation of the digit (example use 3 to produce 33, with a cost of 2).
Using those operations results in some expressions that look like mathematical versions of Rube Goldberg machines.
For the solver, I am using monotonic operations at first. That allows us to have a directed acyclic graph (DAG).
To get past the first limitation, I will also allow the use of the number 1 at cost of 2 (the result of 5/5).
The script onedigit is a CLI to the solver.
The syntax is:
onedigit [OPTIONS]
Options:
--digit <number> digit to use for expressions
--max_value <number> largest number to report
--max_steps <number> number of iterations
--max_cost <number> largest cost for an expression to be used
--full show combinations in terms of the digit, otherwise use expanded values
--input_filename <filename> import a JSON file that describes the model
--output_filename <filename> name of a JSON used for the output, an automatic name would be used otherwise.
--help this informationThe simplest use is to get combinations using the digit 3.
onedigit --digit 3To see the default values:
onedigit --helpThe JSON format is helpful as we can use jq to run queries on the output.
For example, to generate all combinations with the digit 7 up to 100, with a cost less than '3'.
onedigit --digit 7 --max_value 100 --output_filename foo.json
cat foo.json | jq '.[] | select(.cost <= 3) | {"value":.value, "expression":.expr_full}'This project also includes an alternative CLI implementation (cli2.py) that uses Python's argparse instead of the fire library. This provides a more traditional command-line interface experience.
Note that the argument names in cli2 use dashes instead of underscores (e.g., --max-value instead of --max_value).
The script onedigit2 provides the same functionality as onedigit but with standard argparse-style arguments:
usage: onedigit [-h] [--max-value MAX_VALUE] [--max-cost MAX_COST]
[--max-steps MAX_STEPS] [--full] [--input-filename INPUT_FILENAME]
[--output-filename OUTPUT_FILENAME] digit
Calculate number combinations with a single digit.
positional arguments:
digit The digit to use to generate combinations (1-9)
options:
-h, --help show this help message and exit
--max-value MAX_VALUE
Largest value for a combination to be shown in the output (default: 9999)
--max-cost MAX_COST Maximum cost a combination can have for it to be remembered (default: 2)
--max-steps MAX_STEPS
Maximum number of generative rounds (default: 5)
--full Display combinations using full expressions instead of simple values
--input-filename INPUT_FILENAME
JSON file used to preload the model
--output-filename OUTPUT_FILENAME
JSON file used to store the model upon completionThe simplest use is to get combinations using the digit 3:
./onedigit2 3Show combinations for digit 7 up to 100 with full expressions:
./onedigit2 7 --max-value 100 --fullLoad model from file and save results:
./onedigit2 3 --input-filename input.json --output-filename results.jsonBoth CLI implementations (onedigit and onedigit2) produce identical results and support the same functionality.
木