From 198aaae45444f4daac61825d1e8840a7d650f22b Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Sat, 19 May 2018 17:08:13 -0700 Subject: [PATCH] Add files via upload --- ipynb/Countdown.ipynb | 3451 ++++++++++++++--------------------------- 1 file changed, 1125 insertions(+), 2326 deletions(-) diff --git a/ipynb/Countdown.ipynb b/ipynb/Countdown.ipynb index bfb69cf..c653f90 100644 --- a/ipynb/Countdown.ipynb +++ b/ipynb/Countdown.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
Peter Norvig
5 January 2016
" + "Peter Norvig
5 January 2016
revised 18 May 2018
" ] }, { @@ -17,22 +17,22 @@ } }, "source": [ - "# Countdown to 2016\n", + "# Four 4s, Five 5s, and Countdown to 2016\n", "\n", - "In 2016 Alex Bellos [posed](http://www.theguardian.com/science/2016/jan/04/can-you-solve-it-complete-the-equation-10-9-8-7-6-5-4-3-2-1-2016) this New Year's puzzle:\n", + "On January 1, 2016 Alex Bellos [posed](http://www.theguardian.com/science/2016/jan/04/can-you-solve-it-complete-the-equation-10-9-8-7-6-5-4-3-2-1-2016) this New Year's puzzle:\n", "\n", "\n", "> Fill in the blanks so that this equation makes arithmetical sense:\n", "\n", - "> `10 _ 9 _ 8 _ 7 _ 6 _ 5 _ 4 _ 3 _ 2 _ 1 = 2016`\n", + "> 10 ␣ 9 ␣ 8 ␣ 7 ␣ 6 ␣ 5 ␣ 4 ␣ 3 ␣ 2 ␣ 1 = 2016\n", "\n", "> You are allowed to use *only* the four basic arithmetical operations: +, -, ×, ÷. But brackets (parentheses) can be used wherever needed. So, for example, the solution could begin\n", "\n", - "> `(10 - 9) / (8` ...\n", + "> `(10 + 9) * (8` ...\n", "\n", "> or\n", "\n", - "> `10 + (9 - 8)` ...\n", + "> `10 + (9 * 8)` ...\n", "\n", "Let's see if we can solve this puzzle, and some of the related ones from Alex's [first](http://www.theguardian.com/science/2016/jan/04/can-you-solve-it-complete-the-equation-10-9-8-7-6-5-4-3-2-1-2016) and [second](http://www.theguardian.com/science/2016/jan/04/did-you-solve-it-complete-the-equation-10-9-8-7-6-5-4-3-2-1-2016) post. We'll start with a simpler version of the puzzle." ] @@ -47,9 +47,9 @@ } }, "source": [ - "# Four Operators, No Brackets\n", + "# Four Operations, No Brackets\n", "\n", - "Suppose for the moment we are not allowed to use brackets. Then there are nine blanks, each of which can be filled by one of four operators, so the total number of possible expressions is:" + "Suppose for the moment we are not allowed to use brackets. Then there are nine blanks, each of which can be filled by one of four operators, so there are 94 = 262,144 possibilities, few enough that we can enumerate them all, using `itertools.product` to get sequences of operators, and then `str.format` to plug them into blanks, and then `eval` to evaluate the string:" ] }, { @@ -66,7 +66,7 @@ { "data": { "text/plain": [ - "262144" + "('+', '+', '+', '+', '+', '+', '+', '+', '+')" ] }, "execution_count": 1, @@ -75,37 +75,22 @@ } ], "source": [ - "4 ** 9" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "The function `itertools.product` can enumerate all the ways of filling 9 blanks with one of the four operators:" + "import itertools\n", + "from functools import lru_cache # Used later\n", + "\n", + "ops = next(itertools.product(('+', '-', '*', '/'), repeat=9))\n", + "ops" ] }, { "cell_type": "code", "execution_count": 2, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "262144" + "'10+9+8+7+6+5+4+3+2+1'" ] }, "execution_count": 2, @@ -114,41 +99,18 @@ } ], "source": [ - "import itertools\n", - "\n", - "operators = ('+', '-', '*', '/')\n", - " \n", - "len(set(itertools.product(operators, repeat=9)))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "So we can fill in the equation with each possible sequence of operations, and evaluate each string to see if it equals 2016. But we need to catch errors such as dividing by zero, so we'll define a wrapper function, `evaluate`, to do the `eval`:" + "'10{}9{}8{}7{}6{}5{}4{}3{}2{}1'.format(*ops)" ] }, { "cell_type": "code", "execution_count": 3, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[]" + "55" ] }, "execution_count": 3, @@ -156,6 +118,28 @@ "output_type": "execute_result" } ], + "source": [ + "eval(_)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We need to catch errors such as dividing by zero, so I'll define a wrapper function, `evaluate`, to do that, and I'll define `countdown_no_brackets` to put the pieces together:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "button": false, + "new_sheet": false, + "run_control": { + "read_only": false + } + }, + "outputs": [], "source": [ "def evaluate(exp):\n", " \"eval exp, or return None if there is an arithmetic error.\"\n", @@ -164,13 +148,31 @@ " except ArithmeticError:\n", " return None\n", "\n", - "def solve_no_brackets(operators, target=2016):\n", + "def countdown_no_brackets(target, operators=('+', '-', '*', '/')):\n", " \"All solutions to the countdown puzzle (with no brackets).\"\n", " exps = ('10{}9{}8{}7{}6{}5{}4{}3{}2{}1'.format(*ops)\n", " for ops in itertools.product(operators, repeat=9))\n", - " return [exp for exp in exps if evaluate(exp) == target]\n", - "\n", - "solve_no_brackets(operators)" + " return [exp for exp in exps if evaluate(exp) == target]" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "countdown_no_brackets(2016)" ] }, { @@ -183,12 +185,12 @@ } }, "source": [ - "Too bad; we did all that work and didn't find a solution. What years *can* we find solutions for? Let's modify `solve_no_brackets` to take a collection of target years rather than a single one, and return a dict of the form `{year: 'expression'}` for each expression that evaluates to one of the target years:" + "Too bad; we did all that work and didn't find a solution. What years *can* we find solutions for? I'll modify `countdown_no_brackets` to take a collection of target years rather than a single one, and return a dict of the form `{year: 'expression'}` for each expression that evaluates to one of the target years. I'll also generalize it to allow any format string, not just the countdown string." ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": { "button": false, "new_sheet": false, @@ -211,19 +213,20 @@ " 2019: '10*9*8*7/6/5*4*3+2+1'}" ] }, - "execution_count": 4, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def solve_no_brackets(operators, targets):\n", + "def countdown_no_brackets(targets, operators=('+', '-', '*', '/'), \n", + " fmt='10{}9{}8{}7{}6{}5{}4{}3{}2{}1'):\n", " \"All solutions to the countdown puzzle (with no brackets).\"\n", - " exps = ('10{}9{}8{}7{}6{}5{}4{}3{}2{}1'.format(*ops)\n", - " for ops in itertools.product(operators, repeat=9))\n", + " exps = (fmt.format(*ops)\n", + " for ops in itertools.product(operators, repeat=fmt.count('{}')))\n", " return {int(evaluate(exp)): exp for exp in exps if evaluate(exp) in targets}\n", "\n", - "solve_no_brackets(operators, range(1900, 2100))" + "countdown_no_brackets(range(1900, 2100))" ] }, { @@ -249,25 +252,27 @@ } }, "source": [ - "# Four Operators, With Brackets\n", + "# Four Operations, With Brackets\n", "\n", - "Now let's return to the original puzzle, with the brackets. How many ways are there of bracketing an expression with 9 binary operators? I happen to remember that this is given by the [Catalan numbers](https://en.wikipedia.org/wiki/Catalan_number), and we can [look it up](http://www.wolframalpha.com/input/?i=9th+catalan+number) to find that there are 4862 diffferent bracketing. If we enumerated and evaluated all of them, it would take about 4862 times longer than doing a single `solve_no_brackets`, which took about 6 seconds, so the estimated time would be about 8 hours. I'm impatient, so I'd like a faster approach. \n", + "What is the problem I want to solve? I want to know if I can create an expression whose value is 2016. But to get there I'll solve a more general problem: given a sequence of numbers, like `(10, 9, 8)`, what expressions can I make with them?\n", + "I'll define `expressions(numbers)` to return a dict of `{value: expression}` \n", + "for all expressions (strings) whose numeric value is `value`, and can be made from `numbers`, for example:\n", "\n", - "I'll use the idea of [dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming): break the problem down into simpler subparts, and compute an answer for each subpart, and store intermediate results in a table so we don't need to re-compute them when we need them again.\n", + " expressions((10,)) ⇒ {10: '10'}\n", + " expressions((9, 8)) ⇒ {1: '(9-8)', 1.125: '(9/8)', 17: '(9+8)', 72: '(9*8)'}\n", "\n", - "How do we break the problem into parts? In general, any expression must consist of an operator with two operands (which might in turn be complex subexpressions). For example, a complete countdown expression might be of the form\n", + "I'll use the idea of [dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming): break the problem down into simpler subparts, compute an answer for each subpart, and remember intermediate results so we don't need to re-compute them later. How do we break the problem into parts? `expressions((10, 9, 8))` should consist of all the ways of splitting `(10, 9, 8)` into two parts, finding all the expressions that can be made with each part, and combining pairs of expressions with any of the four operators:\n", "\n", - " (10 ... 8) + (7 ... 1)\n", - " \n", - "where `(10 ... 8)` means some expression that starts with 10 and ends with 8. Of course we need not use `'+'` as the operator, and we need not split after the 8; we could use any of the four operators and split anywhere. Let's start by defining `c10` as the tuple of integers forming the countdown from 10 to 1, and the function `splits` to split a tuple in all ways: " + " expressions((10, 9, 8)) ⇒ {11: '(10+(9-8))', 27: '(10+(9+8))', 720: '(10*(9*8))', ...}\n", + "\n", + "First the function `splits`:" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": { "button": false, - "collapsed": true, "new_sheet": false, "run_control": { "read_only": false @@ -277,15 +282,15 @@ "source": [ "c10 = (10, 9, 8, 7, 6, 5, 4, 3, 2, 1)\n", "\n", - "def splits(items):\n", - " \"Split sequence of items into two non-empty parts, in all ways.\"\n", - " return [(items[:i], items[i:]) \n", - " for i in range(1, len(items))]" + "def splits(sequence):\n", + " \"Split sequence into two non-empty parts, in all ways.\"\n", + " return [(sequence[:i], sequence[i:]) \n", + " for i in range(1, len(sequence))]" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": { "button": false, "new_sheet": false, @@ -308,7 +313,7 @@ " ((10, 9, 8, 7, 6, 5, 4, 3, 2), (1,))]" ] }, - "execution_count": 6, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -327,70 +332,34 @@ } }, "source": [ - "Now what I would like to do is build up a table that says, for every subsequence of the numbers, what are the expressions we can make with those numbers, and what do they evaluate to? We'll call the table `EXPS`. For example, with the subsequence `(10, 9, 8)`, we would have:\n", - "\n", - " EXPS[(10, 9, 8)] = {\n", - " 27: '((10+9)+8)',\n", - " 8: '((10-9)*8)', \n", - " -7: '(10-(9+8))', \n", - " ...}\n", - " \n", - "We'll do the same for every other subsequence, for example:\n", - "\n", - " EXPS[(7, 6, 5, 4, 3, 2, 1)] = {\n", - " 1: '((((7/((6/5)-4))+3)*2)*1)',\n", - " 2: '((((7/((6/5)-4))+3)*2)+1)',\n", - " 3.5: '((((7/((6/5)-4))+3)+2)+1)',\n", - " 4: '((7-((6/5)*((4/3)+2)))+1)',\n", - " ...}\n", - " \n", - "Once we have the tables for these two subsequences, we can put them together to get the table for the complete `countdown(10)` by considering all ways of taking a value from the first table, then one of the four operators, then a value from the second table. For example, taking the first entry from each table, and the operator `'+'`, we would have:\n", - "\n", - " EXPS[(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)] = { \n", - " 28: '(((10+9)+8)+((((7/((6/5)-4))+3)*2)*1))',\n", - " ...}\n", - "\n", - "\n", - "I can implement `EXPS` as a defaultdict of dicts, and define `expressions(numbers)` to fill in `EXPS` entries for `numbers` and all sub-sequences of `numbers`. Within `expressions`, note that `Lnums` and `Rnums` are sequences of numbers, such as `(10, 9, 8)` and `(7, 6)`. `L` and `R` are numeric values, such as `27` and `1`. And `Lexp` and `Rexp` are strings, such as `\"((10+9)+8)\"` and `\"(7-6)\"`. Rather than catching division-by-zero errors, we just avoid the division when the denominator is 0." + "Now the function `expressions`:" ] }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "button": false, - "collapsed": true, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, + "execution_count": 9, + "metadata": {}, "outputs": [], "source": [ - "from collections import defaultdict, Counter\n", - "\n", - "EXPS = defaultdict(dict) # e.g., EXPS[(10, 9, 8)][27] == '((10+9)+8)'\n", - "\n", - "def expressions(numbers):\n", - " \"Fill EXPS table for numbers, and all sub-sequences of numbers. Return EXPS[numbers]\"\n", - " if numbers in EXPS: # Already did the work\n", - " pass\n", - " elif len(numbers) == 1: # Only one way to make an expression out of a single number\n", - " expr(numbers, numbers[0], str(numbers[0]))\n", - " else: # Split in all ways; fill tables for left and right; combine tables in all ways\n", + "@lru_cache()\n", + "def expressions(numbers: tuple) -> dict:\n", + " \"Return {value: expr} for all expressions that can be made from numbers.\"\n", + " if len(numbers) == 1: \n", + " return {numbers[0]: str(numbers[0])}\n", + " else: \n", + " result = {}\n", " for (Lnums, Rnums) in splits(numbers):\n", - " for (L, R) in itertools.product(expressions(Lnums), expressions(Rnums)):\n", - " Lexp, Rexp = '(' + EXPS[Lnums][L], EXPS[Rnums][R] + ')'\n", - " expr(numbers, L * R, Lexp + '*' + Rexp)\n", - " expr(numbers, L - R, Lexp + '-' + Rexp)\n", - " expr(numbers, L + R, Lexp + '+' + Rexp)\n", + " for (L, R) in pairs(expressions(Lnums), expressions(Rnums)):\n", + " Lexp = '(' + expressions(Lnums)[L]\n", + " Rexp = expressions(Rnums)[R] + ')'\n", " if R != 0: \n", - " expr(numbers, L / R, Lexp + '/' + Rexp)\n", - " return EXPS[numbers]\n", - "\n", - "def expr(numbers, value, exp): \n", - " \"Record exp as an expression with the given value, covering the sequence of numbers.\"\n", - " EXPS[numbers][value] = exp" + " result[L / R] = Lexp + '/' + Rexp\n", + " result[L * R] = Lexp + '*' + Rexp\n", + " result[L - R] = Lexp + '-' + Rexp\n", + " result[L + R] = Lexp + '+' + Rexp\n", + " return result\n", + " \n", + "def pairs(left, right): return ((L, R) for L in left for R in right)" ] }, { @@ -403,12 +372,73 @@ } }, "source": [ + "For example, at one point in a call to `expressions((10, 9, 8))` we would have:\n", + "\n", + " Lnums, Rnums = (10), (9, 8)\n", + " L, R = 10, 1\n", + " Lexp, Rexp = '(10', '(9-8))'\n", + "\n", + "This would lead to us adding the following four entries to `result`:\n", + "\n", + " result[10] = '(10*(9-8))'\n", + " result[9] = '(10-(9-8))'\n", + " result[11] = '(10+(9-8))'\n", + " result[10] = '(10/(9-8))'\n", + " \n", + "The decorator `@lru_cache` takes care of storing the intermediate results. Rather than catching errors, we just avoid division by 0.\n", + "\n", "Let's give it a try:" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{10: '10'}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expressions((10,))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "button": false, + "new_sheet": false, + "run_control": { + "read_only": false + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: '(9-8)', 1.125: '(9/8)', 17: '(9+8)', 72: '(9*8)'}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expressions((9, 8))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, "metadata": { "button": false, "new_sheet": false, @@ -432,7 +462,7 @@ " 8.88888888888889: '((10/9)*8)',\n", " 9: '((10-9)+8)',\n", " 9.11111111111111: '((10/9)+8)',\n", - " 10: '(10/(9-8))',\n", + " 10.0: '(10*(9-8))',\n", " 11: '((10+9)-8)',\n", " 11.125: '(10+(9/8))',\n", " 11.25: '((10*9)/8)',\n", @@ -444,7 +474,7 @@ " 720: '((10*9)*8)'}" ] }, - "execution_count": 8, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -453,32 +483,6 @@ "expressions((10, 9, 8))" ] }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'((10+9)+8)'" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "EXPS[(10, 9, 8)][27]" - ] - }, { "cell_type": "markdown", "metadata": { @@ -496,7 +500,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 13, "metadata": { "button": false, "new_sheet": false, @@ -509,8 +513,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 29.5 s, sys: 698 ms, total: 30.2 s\n", - "Wall time: 30.2 s\n" + "CPU times: user 28.3 s, sys: 728 ms, total: 29 s\n", + "Wall time: 29.2 s\n" ] }, { @@ -519,7 +523,7 @@ "'(((((((10+((9*8)*7))-6)-5)*4)+3)+2)-1)'" ] }, - "execution_count": 10, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -538,14 +542,401 @@ } }, "source": [ - "We have an answer! And in a lot less than 8 hours, thanks to dynamic programming! \n", - "\n", - "Removing unnecessary brackets, this equation is equivalent to:" + "We have an answer! And in seconds, not hours, thanks to dynamic programming! Here are solutions for nearby years:" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{2010: '((((10*((9+((8+7)*6))+(5/4)))+3)*2)-1)',\n", + " 2011: '((((((10+9)*8)+7)*(6+(5*(4/3))))-2)-1)',\n", + " 2012: '((((((10+9)*8)+7)*(6+(5*(4/3))))-2)*1)',\n", + " 2013: '((((((10+9)*8)+7)*(6+(5*(4/3))))-2)+1)',\n", + " 2014: '((((10-(9*8))*(7-(6*((5+4)+3))))/2)-1)',\n", + " 2015: '((((((((10*9)+8)-7)*(6+5))+4)+3)*2)-1)',\n", + " 2016: '(((((((10+((9*8)*7))-6)-5)*4)+3)+2)-1)',\n", + " 2017: '(((((((10+((9*8)*7))-6)-5)*4)+3)+2)*1)',\n", + " 2018: '(((((((10+((9*8)*7))-6)-5)*4)+3)+2)+1)',\n", + " 2019: '(((((((((10*9)*8)*7)/6)/5)*4)*3)+2)+1)',\n", + " 2020: '((((10+((((9*8)*7)*(6-5))*4))-3)-2)-1)',\n", + " 2021: '((((10+((((9*8)*7)*(6-5))*4))-3)-2)*1)',\n", + " 2022: '((((10+((((9*8)*7)*(6-5))*4))-3)-2)+1)',\n", + " 2023: '(((((10*9)+8)*((7*6)-(5/(4+3))))/2)*1)',\n", + " 2024: '(((((10*9)+8)*((7*6)-(5/(4+3))))/2)+1)'}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{y: expressions(c10)[y] for y in range(2010, 2025)}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Four 4s\n", + "\n", + "Alex Bellos continues with a related puzzle:\n", + " \n", + "> The most famous “fill in the gaps in the equation” puzzle is known as the [four fours](https://en.wikipedia.org/wiki/Four_fours), because every equation is of the form\n", + "\n", + "> 4 ␣ 4 ␣ 4 ␣ 4 = X.\n", + "\n", + ">In the classic form of the puzzle you must find a solution for X = 0 to 9 using just addition, subtraction, multiplication and division.\n", + "\n", + "This puzzle is \"most famous\" because it goes back to a [1914 publication by W. Ball](https://archive.org/details/mathematicalrecr00ball). The \"0 to 9\" solution is easy:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: '(((4-4)-4)+4)',\n", + " 1: '(((4/4)-4)+4)',\n", + " 2: '((4/(4+4))*4)',\n", + " 3: '(((4+4)+4)/4)',\n", + " 4: '(((4-4)*4)+4)',\n", + " 5: '(((4*4)+4)/4)',\n", + " 6: '(((4+4)/4)+4)',\n", + " 7: '((4-(4/4))+4)',\n", + " 8: '(((4+4)+4)-4)',\n", + " 9: '(((4/4)+4)+4)'}" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{i: expressions((4, 4, 4, 4))[i] for i in range(10)}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# New Mathematical Operations\n", + "\n", + "Bellos then writes:\n", + " \n", + "> If you want to show off, you can introduce **new mathematical operations** such as powers, square roots, concatenation and decimals, ... or use the factorial symbol, `!`.\n", + "\n", + "Bellos is suggesting the following operations:\n", + "\n", + "- **Powers**: `(4 ^ 4)` is 4 to the fourth power, or 256.\n", + "- **Square roots:** `√4` is the square root of 4, or 2.\n", + "- **Concatenation:** `44` is forty-four.\n", + "- **Decimals:** `4.4` is four and four tenths.\n", + "- **Factorials** `4!` is 4 × 3 × 2 × 1, or 24.\n", + "\n", + "There are some complications to deal with:\n", + "\n", + "- **Irrationals**: `√2` is an irrational number; so we can't do exact rational arithmetic.\n", + "- **Imaginaries**: `√-1` is an imaginary number, but Python gives a `ValueError` for `sqrt(-1)`.\n", + "- **Overflow**: `(10. ^ (9. ^ 8.))`, as a `float`, gives an `OverflowError`.\n", + "- **Out of memory**: [`(10 ^ (9 ^ (8 * 7)))`](http://www.wolframalpha.com/input/?i=10+%5E+9+%5E+56), as an `int`, gives an `OutOfMemoryError` (even if your memory uses every atom on Earth).\n", + "- **Infinite expressions**: We could add an infinite number of square root and/or factorial signs to any expression: `√√√√√√(4!!!!!!!!)`...\n", + "- **Round-off error**: `(49*(1/49))` evaluates to `0.9999999999999999`, not `1`.\n", + "\n", + "We could try to manage this with *symbolic algebra*, perhaps using [SymPy](http://www.sympy.org/en/index.html), but that seems complicated, so instead I will:\n", + "- Arbitrarily limit expressions to two nested unary operations to avoid infinite expressions.\n", + "- Use floats for all computation (to avoid out of memory errors), and accept that there are some approximations. \n", + "\n", + "I'll define the function `do` to do an arithmetic computation, catch any errors, and try to correct round-off errors. The idea is that since my expressions start with integers, any result that is very close to an integer is probably actually that integer. So I'll correct `(49*(1/49))` to be `1.0`. Of course, an expression like `(1+(10^-99))` is also very close to `1.0`, but it should not be rounded off. Instead, I'll try to avoid such expressions by silently dropping any value that is outside the range of 10-10 to 1010 (except of course I will accept an exact 0)." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "from operator import add, sub, mul, truediv\n", + "from math import sqrt, factorial\n", + "from collections import defaultdict, Counter\n", + "\n", + "def do(op, *args): \n", + " \"Return op(*args), trying to correct for roundoff error, or `None` on Error or too big/small.\"\n", + " try:\n", + " val = op(*args)\n", + " return (val if val == 0 else\n", + " None if not (1e-10 < abs(val) < 1e10) else\n", + " round(val) if abs(val - round(val)) < 1e-12 else \n", + " val)\n", + " except (ArithmeticError, ValueError):\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "assert do(truediv, 12, 4) == 3 and do(truediv, 12, 0) == None\n", + "assert do(mul, 49, do(truediv, 1, 49)) == 1\n", + "assert do(pow, 10, -99) == None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I'll take this opportunity to refactor `expressions` to have these subfunctions:\n", + "- `digit_expressions(result, numbers)`: returns a dict like {0.44: '.44', 4.4: '4.4', 44.0: '44'}\n", + "- `add_unary_expressions(result)`: adds expressions like `√44` and `4!` and return `result`. \n", + "- `add_binary_expressions(result, numbers)`: adds expressions like `4+√44` and `4^4` and return `result`." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "button": false, + "new_sheet": false, + "run_control": { + "read_only": false + } + }, + "outputs": [], + "source": [ + "@lru_cache(1000)\n", + "def expressions(numbers):\n", + " \"Return {value: expr} for all expressions that can be made from numbers.\"\n", + " return add_unary_expressions(add_binary_expressions(digit_expressions(numbers), numbers))\n", + "\n", + "def digit_expressions(numbers) -> dict:\n", + " \"All ways of making numbers from these numbers, in order, and a decimal point.\"\n", + " D = ''.join(map(str, numbers))\n", + " exps = [(D[:i] + '.' + D[i:]).rstrip('.')\n", + " for i in range(len(D) + 1)\n", + " if not (D.startswith('0') and i > 1)]\n", + " return {float(exp): exp for exp in exps}\n", + " \n", + "def add_binary_expressions(result, numbers) -> dict:\n", + " \"Add binary expressions by splitting numbers and combining with an op.\"\n", + " for (Lnums, Rnums) in splits(numbers):\n", + " for (L, R) in pairs(expressions(Lnums), expressions(Rnums)):\n", + " Lexp = '(' + expressions(Lnums)[L]\n", + " Rexp = expressions(Rnums)[R] + ')'\n", + " assign(result, do(truediv, L, R), Lexp + '/' + Rexp)\n", + " assign(result, do(mul, L, R), Lexp + '*' + Rexp)\n", + " assign(result, do(add, L, R), Lexp + '+' + Rexp)\n", + " assign(result, do(sub, L, R), Lexp + '-' + Rexp)\n", + " if -10 <= R <= 10 and (L > 0 or int(R) == R):\n", + " assign(result, do(pow, L, R), Lexp + '^' + Rexp)\n", + " return result\n", + " \n", + "def add_unary_expressions(result, nesting_level=2) -> dict:\n", + " \"Add unary expressions: -v, √v and v!\"\n", + " for _ in range(nesting_level):\n", + " for v in tuple(result):\n", + " exp = result[v]\n", + " if -v not in result:\n", + " assign(result, -v, '-' + exp)\n", + " if 0 < v <= 100 and 120 * v == round(120 * v): \n", + " assign(result, sqrt(v), '√' + exp)\n", + " if 3 <= v <= 6 and v == int(v):\n", + " assign(result, factorial(v), exp + '!')\n", + " return result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The function `assign` will silently drop expressions whose value is `None`, and if two expressions have the same value, `assign` keeps the one with the lower \"weight\"—a measure of the characters in the expression. This shows simpler expressions in favor of complex ones: for four 4s, the entry for `0` will be `44-44`, not something like `-√((-√4*--4)*(-√4--√4))`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "def assign(result, value, exp): \n", + " \"Assign result[value] = exp, unless we already have a lighter exp, or value is too extreme.\"\n", + " if (value is not None \n", + " and (value not in result or weight(exp) < weight(result[value]))): \n", + " result[value] = exp\n", + " \n", + "PENALTIES = defaultdict(int, {'√':7, '!':5, '.':1, '^':3, '/':1, '-':1})\n", + " \n", + "def weight(exp) -> int: return len(exp) + sum(PENALTIES[c] for c in exp)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "button": false, + "new_sheet": false, + "run_control": { + "read_only": false + } + }, + "source": [ + "I'll define a function to create a table of consecutive integers (starting at 0) that can be made by a sequence of numbers:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "button": false, + "new_sheet": false, + "run_control": { + "read_only": false + } + }, + "outputs": [], + "source": [ + "def show(numbers, clear=True):\n", + " \"Print a table of expressions for all consecutive integers i from 0 up to the first unmakeable integer.\"\n", + " if clear: expressions.cache_clear() # To free up memory\n", + " print('{:,} entries for {}\\n'.format(len(expressions(numbers)), numbers))\n", + " for i in itertools.count(0): # All integers from 0\n", + " if i not in expressions(numbers):\n", + " return i\n", + " print('{:4} = {}'.format(i, unbracket(expressions(numbers)[i])))\n", + " \n", + "def unbracket(exp):\n", + " \"Strip outer parens from exp, if they are there\"\n", + " return (exp[1:-1] if exp.startswith('(') and exp.endswith(')') else exp)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "705,959 entries for (4, 4, 4, 4)\n", + "\n", + " 0 = 44-44\n", + " 1 = 44/44\n", + " 2 = 4*(4/(4+4))\n", + " 3 = (4+(4+4))/4\n", + " 4 = 4+(4*(4-4))\n", + " 5 = (4+(4*4))/4\n", + " 6 = 4+((4+4)/4)\n", + " 7 = (44/4)-4\n", + " 8 = 4+(4+(4-4))\n", + " 9 = 4+(4+(4/4))\n", + " 10 = 44/4.4\n", + " 11 = (4/.4)+(4/4)\n", + " 12 = (4+44)/4\n", + " 13 = 4!-(44/4)\n", + " 14 = (4+(.4*4))/.4\n", + " 15 = 4+(44/4)\n", + " 16 = .4*(44-4)\n", + " 17 = (4*4)+(4/4)\n", + " 18 = .4+(.4*44)\n", + " 19 = (4+(4-.4))/.4\n", + " 20 = 4*(4+(4/4))\n", + " 21 = (4+4.4)/.4\n", + " 22 = √4*(44/4)\n", + " 23 = ((4!*4)-4)/4\n", + " 24 = 4+(4+(4*4))\n", + " 25 = (4+(4!*4))/4\n", + " 26 = (4/.4)+(4*4)\n", + " 27 = 4!+(4-(4/4))\n", + " 28 = 44-(4*4)\n", + " 29 = 4+(4/(.4*.4))\n", + " 30 = (4+(4+4))/.4\n", + " 31 = 4!+((4!+4)/4)\n", + " 32 = (4*4)+(4*4)\n", + " 33 = 4!+((4-.4)/.4)\n", + " 34 = 44-(4/.4)\n", + " 35 = 4!+(44/4)\n", + " 36 = 44-(4+4)\n", + " 37 = ((.4+4!)/.4)-4!\n", + " 38 = 44-(4!/4)\n", + " 39 = ((4*4)-.4)/.4\n", + " 40 = 44-√(4*4)\n", + " 41 = (.4+(4*4))/.4\n", + " 42 = √4+(44-4)\n", + " 43 = 44-(4/4)\n", + " 44 = 4+(44-4)\n", + " 45 = (4/4)+44\n", + " 46 = 4+(44-√4)\n", + " 47 = 4!+(4!-(4/4))\n", + " 48 = 4*(4+(4+4))\n", + " 49 = (√4/.4)+44\n", + " 50 = (4+(4*4))/.4\n", + " 51 = (.4+(4!-4))/.4\n", + " 52 = 4+(4+44)\n", + " 53 = 4!+(4!+(√4/.4))\n", + " 54 = (4/.4)+44\n", + " 55 = 44/(.4+.4)\n", + " 56 = 4*(4+(4/.4))\n", + " 57 = ((.4+4!)/.4)-4\n", + " 58 = ((4^4)-4!)/4\n", + " 59 = (4!/.4)-(4/4)\n", + " 60 = (4*4)+44\n", + " 61 = (4/4)+(4!/.4)\n", + " 62 = (4*(4*4))-√4\n", + " 63 = ((4^4)-4)/4\n", + " 64 = (4+4)*(4+4)\n", + " 65 = (4+(4^4))/4\n", + " 66 = √4+(4*(4*4))\n", + " 67 = √4+((√4+4!)/.4)\n", + " 68 = 4+(4*(4*4))\n", + " 69 = (4!+(4-.4))/.4\n", + " 70 = (4!+(4^4))/4\n", + " 71 = (4!+4.4)/.4\n", + " 72 = 4!+(4+44)\n", + "CPU times: user 11.2 s, sys: 60.4 ms, total: 11.3 s\n", + "Wall time: 11.3 s\n" + ] + }, + { + "data": { + "text/plain": [ + "73" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time show((4, 4, 4, 4))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "button": false, + "new_sheet": false, + "run_control": { + "read_only": false + } + }, + "source": [ + "We can also solve the \"2016 with four fours\" puzzle:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, "metadata": { "button": false, "new_sheet": false, @@ -557,57 +948,486 @@ { "data": { "text/plain": [ - "2016" + "'(4!*(4!+(4!/.4)))'" ] }, - "execution_count": 11, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "(10 + 9 * 8 * 7 - 6 - 5) * 4 + 3 + 2 - 1" + "expressions((4, 4, 4, 4))[2016]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "button": false, + "new_sheet": false, + "run_control": { + "read_only": false + } + }, + "source": [ + "In a [separate video](https://www.youtube.com/embed/Noo4lN-vSvw), Alex Bellos shows how to form **every** integer from 0 to infinity using four 4s, if the square root and `log` functions are allowed. The solution comes from Paul Dirac (although [Dirac originally developed it](https://nebusresearch.wordpress.com/2014/04/18/how-dirac-made-every-number/) for the \"four 2s\" problem).\n", + "\n", + "Donald Knuth has [conjectured](https://www.tandfonline.com/doi/abs/10.1080/0025570X.1964.11975546) that with floor, square root and factorial, you can make any positive integer with just **one** 4.\n", + "\n", + "Below are some popular variants:\n", + "\n", + "# Four 2s" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "109,747 entries for (2, 2, 2, 2)\n", + "\n", + " 0 = 22-22\n", + " 1 = 22/22\n", + " 2 = 2+(2*(2-2))\n", + " 3 = (2+(2*2))/2\n", + " 4 = .2*(22-2)\n", + " 5 = 2+(2+(2/2))\n", + " 6 = 2*(2+(2/2))\n", + " 7 = 2+(2/(.2*2))\n", + " 8 = 2+(2+(2*2))\n", + " 9 = (22/2)-2\n", + " 10 = 22/2.2\n", + " 11 = (2/2)+(2/.2)\n", + " 12 = (2+22)/2\n", + " 13 = 2+(22/2)\n", + " 14 = 2+(2+(2/.2))\n", + " 15 = (2+(2/2))/.2\n", + " 16 = 2*(2*(2*2))\n", + " 17 = 22-(√.2^-2)\n", + " 18 = 22-(2*2)\n", + " 19 = (2+(2-.2))/.2\n", + " 20 = (2/.2)+(2/.2)\n", + " 21 = 22-(2/2)\n", + " 22 = 2+(22-2)\n", + " 23 = (2/2)+22\n", + " 24 = 2*(2+(2/.2))\n", + " 25 = 2/(.2*(.2*2))\n", + " 26 = 2+(2+22)\n", + " 27 = (√.2^-2)+22\n", + " 28 = 2+(2+(2*2)!)\n", + " 29 = 2+(2+(.2^-2))\n", + " 30 = (2+(2*2))/.2\n", + "CPU times: user 1.78 s, sys: 16.9 ms, total: 1.8 s\n", + "Wall time: 1.8 s\n" + ] + }, + { + "data": { + "text/plain": [ + "31" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time show((2, 2, 2, 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here are solutions for nearby years:" + "# Four 9s" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 24, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "539,849 entries for (9, 9, 9, 9)\n", + "\n", + " 0 = 99-99\n", + " 1 = 99/99\n", + " 2 = (99/9)-9\n", + " 3 = (9+(9+9))/9\n", + " 4 = 9-(9/(.9+.9))\n", + " 5 = √9+((9+9)/9)\n", + " 6 = ((9+(9+9))/9)!\n", + " 7 = 9-((9+9)/9)\n", + " 8 = ((9*9)-9)/9\n", + " 9 = 9+(9*(9-9))\n", + " 10 = 99/9.9\n", + " 11 = 9+((9+9)/9)\n", + " 12 = (9+99)/9\n", + " 13 = √9+(9+(9/9))\n", + " 14 = 9+(9/(.9+.9))\n", + " 15 = 9+((9+9)/√9)\n", + " 16 = 9+((9/.9)-√9)\n", + " 17 = 9+(9-(9/9))\n", + " 18 = 99-(9*9)\n", + " 19 = 9+(9+(9/9))\n", + " 20 = 9+(99/9)\n", + " 21 = (9+9.9)/.9\n", + " 22 = √9+(9+(9/.9))\n", + " 23 = √9+((9+9)/.9)\n", + " 24 = (99/√9)-9\n", + " 25 = √9!+(9+(9/.9))\n", + " 26 = (√9*9)-(9/9)\n", + " 27 = 9+(9+√(9*9))\n", + " 28 = 9+(9+(9/.9))\n", + " 29 = 9+((9+9)/.9)\n", + " 30 = (9+(9+9))/.9\n", + " 31 = (.9+(√9*9))/.9\n", + " 32 = (99-√9)/√9\n", + " 33 = √9*(99/9)\n", + " 34 = (√9+99)/√9\n", + " 35 = (√9!+99)/√9\n", + " 36 = 9+(9+(9+9))\n", + " 37 = (√9*9)+(9/.9)\n", + " 38 = √9!*(√9+(√9/.9))\n", + " 39 = 9+(√9*(9/.9))\n", + " 40 = (9+(√9*9))/.9\n", + " 41 = (.9+(√9!*√9!))/.9\n", + " 42 = 9+(99/√9)\n", + " 43 = √9+(√9!*(√9!/.9))\n", + " 44 = (√9!*9)-(9/.9)\n", + " 45 = 9*(9/(.9+.9))\n", + " 46 = (√9!*√9!)+(9/.9)\n", + " 47 = √9*(9+(√9!/.9))\n", + " 48 = √9!*(9-(9/9))\n", + " 49 = 9+(√9!*(√9!/.9))\n", + " 50 = ((√9!*9)-9)/.9\n", + " 51 = 9*(9-(√9/.9))\n", + " 52 = √9!*(9-(√9/9))\n", + " 53 = (√9!*9)-(9/9)\n", + " 54 = 9*((9+9)/√9)\n", + " 55 = 99/(.9+.9)\n", + " 56 = √9!*(9+(√9/9))\n", + " 57 = √9*(9+(9/.9))\n", + " 58 = √9!*(9+(√9!/9))\n", + " 59 = ((√9!*9)-.9)/.9\n", + " 60 = √9*((9+9)/.9)\n", + " 61 = (.9+(√9!*9))/.9\n", + "CPU times: user 7.42 s, sys: 40.3 ms, total: 7.46 s\n", + "Wall time: 7.47 s\n" + ] + }, { "data": { "text/plain": [ - "{2010: '((((10*((9+((8+7)*6))+(5/4)))+3)*2)-1)',\n", - " 2011: '((((((10+9)*8)+7)*(6+(5*(4/3))))-2)-1)',\n", - " 2012: '((((((10+9)*8)+7)*(6+(5*(4/3))))-2)/1)',\n", - " 2013: '((((((10+9)*8)+7)*(6+(5*(4/3))))-2)+1)',\n", - " 2014: '((((10-(9*8))*(7-(6*((5+4)+3))))/2)-1)',\n", - " 2015: '((((((((10*9)+8)-7)*(6+5))+4)+3)*2)-1)',\n", - " 2016: '(((((((10+((9*8)*7))-6)-5)*4)+3)+2)-1)',\n", - " 2017: '(((((((10+((9*8)*7))-6)-5)*4)+3)+2)/1)',\n", - " 2018: '(((((((10+((9*8)*7))-6)-5)*4)+3)+2)+1)',\n", - " 2019: '((((10+9)+((8+7)*(6+((5*4)*3))))*2)+1)',\n", - " 2020: '((((10+((((9*8)*7)/(6-5))*4))-3)-2)-1)',\n", - " 2021: '((((10+((((9*8)*7)/(6-5))*4))-3)-2)/1)',\n", - " 2022: '((((10+((((9*8)*7)/(6-5))*4))-3)-2)+1)',\n", - " 2023: '(((((10+(((9*(8+7))*6)*5))/4)-3)*2)-1)',\n", - " 2024: '(((((10+(((9*(8+7))*6)*5))/4)-3)*2)/1)'}" + "62" ] }, - "execution_count": 12, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "{y: expressions(c10)[y] for y in range(2010, 2025)}" + "%time show((9, 9, 9, 9))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Four 5s" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "button": false, + "new_sheet": false, + "run_control": { + "read_only": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "202,937 entries for (5, 5, 5, 5)\n", + "\n", + " 0 = 55-55\n", + " 1 = 55/55\n", + " 2 = (5/5)+(5/5)\n", + " 3 = (5+(5+5))/5\n", + " 4 = ((5*5)-5)/5\n", + " 5 = 5+(5*(5-5))\n", + " 6 = (55/5)-5\n", + " 7 = 5+((5+5)/5)\n", + " 8 = 5.5+(.5*5)\n", + " 9 = 5+(5-(5/5))\n", + " 10 = 55/5.5\n", + " 11 = 5.5+5.5\n", + " 12 = (5+55)/5\n", + " 13 = .5+(.5*(5*5))\n", + " 14 = 5+((5-.5)/.5)\n", + " 15 = (5*5)-(5+5)\n", + " 16 = 5+(55/5)\n", + " 17 = 5+(5!/(5+5))\n", + " 18 = (5-.5)/(.5*.5)\n", + " 19 = (5+(5-.5))/.5\n", + " 20 = 5+(5+(5+5))\n", + " 21 = (5+5.5)/.5\n", + " 22 = 55/(.5*5)\n", + " 23 = .5+(5*(5-.5))\n", + " 24 = (5*5)-(5/5)\n", + " 25 = .5*(55-5)\n", + " 26 = (5/5)+(5*5)\n", + " 27 = (.5*55)-.5\n", + " 28 = .5+(.5*55)\n", + " 29 = (5!+(5*5))/5\n", + " 30 = 55-(5*5)\n", + " 31 = 55-(5!/5)\n", + " 32 = ((5+5)/5)^5\n", + " 33 = .5*(5!*.55)\n", + " 34 = 5+(5+(5!/5))\n", + " 35 = 5+(5+(5*5))\n", + " 36 = (5!+(.5*5!))/5\n", + " 37 = (.5^-5)+√(5*5)\n", + " 38 = ((5!/5)-5)/.5\n", + "CPU times: user 3.33 s, sys: 17.6 ms, total: 3.35 s\n", + "Wall time: 3.35 s\n" + ] + }, + { + "data": { + "text/plain": [ + "39" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time show((5, 5, 5, 5))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Five 5s " + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7,624,387 entries for (5, 5, 5, 5, 5)\n", + "\n", + " 0 = 5*(55-55)\n", + " 1 = 5^(55-55)\n", + " 2 = 55/(.5*55)\n", + " 3 = .5*((55/5)-5)\n", + " 4 = 5-(55/55)\n", + " 5 = 5*(55/55)\n", + " 6 = 5+(55/55)\n", + " 7 = ((5+55)/5)-5\n", + " 8 = .5*(5+(55/5))\n", + " 9 = (55-(5+5))/5\n", + " 10 = (55/5)-(5/5)\n", + " 11 = 5*(55/(5*5))\n", + " 12 = (5/5)+(55/5)\n", + " 13 = (5+(5+55))/5\n", + " 14 = (5*5)-(55/5)\n", + " 15 = 5+(55/5.5)\n", + " 16 = 5+(5.5+5.5)\n", + " 17 = 5+((5+55)/5)\n", + " 18 = 5.5+(.5*(5*5))\n", + " 19 = (5*5)-(.5+5.5)\n", + " 20 = 55/(5*.55)\n", + " 21 = 5+(5+(55/5))\n", + " 22 = (55+55)/5\n", + " 23 = (5+(55/.5))/5\n", + " 24 = (5+55)/(.5*5)\n", + " 25 = 55-(5+(5*5))\n", + " 26 = 5*(5+(5/(5*5)))\n", + " 27 = 5+(55/(.5*5))\n", + " 28 = .5*(.5+55.5)\n", + " 29 = 5+((5*5)-(5/5))\n", + " 30 = 5*((55/5)-5)\n", + " 31 = .5+(5.5+(5*5))\n", + " 32 = (5+(55/5))/.5\n", + " 33 = .55*(5+55)\n", + " 34 = (5!+(55-5))/5\n", + " 35 = 5+(55-(5*5))\n", + " 36 = (5*5)+(55/5)\n", + " 37 = 5+(((5+5)/5)^5)\n", + " 38 = .5+(5*(5+(.5*5)))\n", + " 39 = ((5*5)-5.5)/.5\n", + " 40 = 55-(5+(5+5))\n", + " 41 = (5!*.55)-(5*5)\n", + " 42 = (5+5.5)/(.5*.5)\n", + " 43 = (.5^-5)+(55/5)\n", + " 44 = 55-(55/5)\n", + " 45 = (5*5!)-555\n", + " 46 = 55+((.5-5)/.5)\n", + " 47 = (5*(5+(5-.5)))-.5\n", + " 48 = .5+(5*(5+(5-.5)))\n", + " 49 = 55-(.5+5.5)\n", + " 50 = 55.5-5.5\n", + " 51 = .5+(55.5-5)\n", + " 52 = 55-(.5+(.5*5))\n", + " 53 = 55-((5+5)/5)\n", + " 54 = ((5*55)-5)/5\n", + " 55 = .5*(55+55)\n", + " 56 = (5+(5*55))/5\n", + " 57 = 55+((5+5)/5)\n", + " 58 = (.5*5)+55.5\n", + " 59 = 5+(55-(5/5))\n", + " 60 = 5*((5+55)/5)\n", + " 61 = 5.5+55.5\n", + " 62 = (.5*(5*(5*5)))-.5\n", + " 63 = .5+(.5*(5*(5*5)))\n", + " 64 = 55+((5-.5)/.5)\n", + " 65 = .5*(5+(5*(5*5)))\n", + " 66 = 55+(55/5)\n", + " 67 = 55+(5!/(5+5))\n", + " 68 = 5.5+(.5*(5+5!))\n", + " 69 = 5+(.5^(-.5-5.5))\n", + " 70 = 5+(5+(5+55))\n", + " 71 = 55+(.5/(.5^5))\n", + " 72 = (.5+5.5)!/(5+5)\n", + " 73 = (5*5)+(5!/(.5*5))\n", + " 74 = (5!/5)+(55-5)\n", + " 75 = 55+((5*5)-5)\n", + " 76 = 5+(5+(5!*.55))\n", + " 77 = 5+(5!*(.5+(.5/5)))\n", + " 78 = 55+((5!-5)/5)\n", + " 79 = (5!+(5*55))/5\n", + " 80 = 5*(5+(55/5))\n", + " 81 = 5+(.5*(5!+(.5^-5)))\n", + " 82 = (.5^-5)+(55-5)\n", + " 83 = (.5*5!)+((5!-5)/5)\n", + " 84 = 5+((5!/5)+55)\n", + " 85 = 5+(55+(5*5))\n", + " 86 = (55/.5)-(5!/5)\n", + " 87 = (555-5!)/5\n", + " 88 = 5*(.55/(.5^5))\n", + " 89 = (5*5)+((.5^-5)/.5)\n", + " 90 = (55-(5+5))/.5\n", + " 91 = (5*5)+(5!*.55)\n", + " 92 = 5+((.5^-5)+55)\n", + " 93 = .5+(5!-(.5*55))\n", + " 94 = 5!-((5/5)+(5*5))\n", + " 95 = (55/.55)-5\n", + " 96 = 5!+((5/5)-(5*5))\n", + " 97 = 5!+((.5^-5)-55)\n", + " 98 = 5!-(55/(.5*5))\n", + " 99 = (55-5.5)/.5\n", + " 100 = 5*(5+(5+(5+5)))\n", + " 101 = (55.5-5)/.5\n", + " 102 = (.5+(5*5))/(.5*.5)\n", + " 103 = 55+(5!/(.5*5))\n", + " 104 = ((55-.5)/.5)-5\n", + " 105 = 55+(55-5)\n", + " 106 = (555/5)-5\n", + " 107 = 5!-(.5+(.5*(5*5)))\n", + " 108 = (55-(5/5))/.5\n", + " 109 = (55/.5)-(5/5)\n", + " 110 = (555-5)/5\n", + " 111 = (5/5)+(55/.5)\n", + " 112 = (5+555)/5\n", + " 113 = .5+(5*(5*(5-.5)))\n", + " 114 = 5+((55-.5)/.5)\n", + " 115 = 5+(55+55)\n", + " 116 = 5+(555/5)\n", + " 117 = 5!-((5+(5+5))/5)\n", + " 118 = ((5*5!)-(5+5))/5\n", + " 119 = 5!-(55/55)\n", + " 120 = 5+(5+(55/.5))\n", + " 121 = (5+55.5)/.5\n", + " 122 = (5*((5*5)-.5))-.5\n", + " 123 = .5+(5*((5*5)-.5))\n", + " 124 = (5*(5*5))-(5/5)\n", + " 125 = .5*(5*(55-5))\n", + " 126 = (5/5)+(5*(5*5))\n", + " 127 = (5*(.5+(5*5)))-.5\n", + " 128 = .5+(5*(.5+(5*5)))\n", + " 129 = (5!-55.5)/.5\n", + " 130 = (5+(5+55))/.5\n", + " 131 = 5!+(5.5+5.5)\n", + " 132 = 5!+((5+55)/5)\n", + " 133 = (5+(5!*5.5))/5\n", + " 134 = (5!/5)+(55/.5)\n", + " 135 = .5*((5*55)-5)\n", + " 136 = 5+(5!+(55/5))\n", + " 137 = (.5*(5*55))-.5\n", + " 138 = .5+(.5*(5*55))\n", + " 139 = ((.5+5.5)!/5)-5\n", + " 140 = .5*(5+(5*55))\n", + " 141 = 5!+((5+5.5)/.5)\n", + " 142 = 5!+(55/(.5*5))\n", + " 143 = ((.5+5.5)!-5)/5\n", + " 144 = ((55/5)-5)!/5\n", + " 145 = (5*(5+(5*5)))-5\n", + " 146 = 5!+((5/5)+(5*5))\n", + " 147 = 5!+((.5*55)-.5)\n", + " 148 = .5+(5!+(.5*55))\n", + " 149 = (5!/5)+(5*(5*5))\n", + " 150 = 5*(55-(5*5))\n", + " 151 = 5!+(55-(5!/5))\n", + " 152 = 5!+(((5+5)/5)^5)\n", + " 153 = 5!+(.5*(5!*.55))\n", + " 154 = 5+(5+(5!+(5!/5)))\n", + " 155 = 5+(5*(5+(5*5)))\n", + " 156 = (5!+(5!*5.5))/5\n", + " 157 = (.5^-5)+(5*(5*5))\n", + " 158 = ((5!/5)+55)/.5\n", + " 159 = (5/(.5^5))-(5/5)\n", + " 160 = (55+(5*5))/.5\n", + " 161 = (5/5)+(5/(.5^5))\n", + " 162 = (5*(.5+(.5^-5)))-.5\n", + " 163 = .5+(5*(.5+(.5^-5)))\n", + " 164 = 5!+(.5*(5!-(.5^-5)))\n", + " 165 = 55+(55/.5)\n", + " 166 = 5!+((5!-5)/(.5*5))\n", + " 167 = 5!+(((5!/.5)-5)/5)\n", + " 168 = (5+(.5*.5))/(.5^5)\n", + " 169 = 5!+(((5*5)-.5)/.5)\n", + " 170 = 5+(5+(5/(.5^5)))\n", + " 171 = (5.5/(.5^5))-5\n", + "CPU times: user 2min 33s, sys: 747 ms, total: 2min 34s\n", + "Wall time: 2min 34s\n" + ] + }, + { + "data": { + "text/plain": [ + "172" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time show((5, 5, 5, 5, 5), False)" ] }, { @@ -624,30 +1444,24 @@ "\n", "Alex Bellos had another challenge: \n", "\n", - "> I was half hoping a computer scientist would let me know exactly how many solutions there are with only the four basic operations. Maybe someone will. \n", + "> I was half hoping a computer scientist would let me know exactly how many solutions [to the Countdown problem] there are with only the four basic operations. Maybe someone will. \n", "\n", "As it stands, my program can't answer that question, because I only keep one expression for each value. \n", "\n", - "Also, I'm not sure what it means to be a distinct solution. For example, are `((10+9)+8)` and `(10+(9+8))` different, or are they same, because they both are equivalent to `(10+9+8)`? Similarly, are `((3-2)-1)` and `(3-(2+1)` different, or the same because they both are equivalent to `(3 + -2 + -1)`? I think the notion of \"distinct solution\" is just inherently ambiguous, and each of these questions could reasonably be answered either way. My choice is to count each of these as distinct: every expression has exactly ten numbers, nine operators, and nine pairs of brackets, and if an expression differs in any character, it is different. But I won't argue with anyone who prefers a different definition of \"distinct solution.\"\n", + "Also, I'm not sure what it means to be a distinct solution. For example, are `((10+9)+8)` and `(10+(9+8))` different, or are they same, because they both are equivalent to `(10+9+8)`? Similarly, are `((3-2)-1)` and `(3-(2+1)` different, or the same because they both are equivalent to `(3 + -2 + -1)`? I think the notion of \"distinct solution\" is just inherently ambiguous. My choice is to count each of these as distinct: every expression has exactly ten numbers, nine operators, and nine pairs of brackets, and if an expression differs in any character, it is different. But I won't argue with anyone who prefers a different definition of \"distinct solution.\"\n", "\n", - "So how can I count expressions? One approach would be to go back to enumerating every equation (all 4862 × 49 = 1.2 bilion of them) and checking which ones equal 2016. That would take about 40 hours with my Python program, but I could get it under 40 minutes in a more efficient language.\n", + "So how can I count expressions? One approach would be to go back to enumerating every equation (all 4862 × 49 = 1.2 bilion of them) and checking which ones equal 2016. That would take about 40 hours with my Python program. Another approach is mimic `expressions`, but to make a table of counts, rather than expression strings. I want:\n", "\n", - "Another approach is to count subexpressions as the table is filled in. We won't enumerate all the expressions, just count them. I'll introduce a second table, `COUNTS`, such that\n", - "\n", - " COUNTS[(10, 9, 8)][27] == 2\n", + " counts[(10, 9, 8)][27] == 2\n", " \n", - "because there are 2 ways to make 27 with the numbers `(10, 9, 8)`, namely, `((10+9)+8)` and `(10+(9+8))`.\n", - "How do we compute the counts? By looking at every split and operator choice that can make the value, and summing up (over all of these) the product of the counts for the two sides. For example, there are 2 ways to make 27 with `(10 ... 8)`, and it turns out there are 3526 ways to make 1 with `(7 ... 1)`. So there are 2 × 3526 = 7052 ways to make 28 with this split by adding 27 and 1. \n", - "\n", - "I'll make `expr` maintain `COUNTS` as well as `EXPS`. And I'll define `clear` to clear out the cache of `COUNTS` and `EXPS`, so that we can fill the tables with our new, improved entries." + "because there are 2 ways to make 27 with the numbers `(10, 9, 8)`, namely, `((10+9)+8)` and `(10+(9+8))`. And in general, if there are `Lcount` ways to make `L` using `Lnums` and `Rcount` ways to make `R` using `Rnums` then there are `Lcount * Rcount` ways of making `L * R` using `Lnums` followed by `Rnums`. So we have:\n" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 27, "metadata": { "button": false, - "collapsed": true, "new_sheet": false, "run_control": { "read_only": false @@ -655,132 +1469,48 @@ }, "outputs": [], "source": [ - "COUNTS = defaultdict(Counter) # e.g., COUNTS[(10, 9, 8)][27] == 2\n", - "\n", - "def expressions(numbers):\n", - " \"Fill EXPS table for numbers, and all sub-sequences of numbers. Return EXPS[numbers]\"\n", - " if numbers in EXPS: # Already did the work\n", - " pass\n", - " elif len(numbers) == 1: # Only one way to make an expression out of a single number\n", - " expr(numbers, numbers[0], str(numbers[0]), 1)\n", - " else: # Split in all ways; fill tables for left and right; combine tables in all ways\n", + "@lru_cache()\n", + "def counts(numbers):\n", + " \"Return a dict of {value: count} for every value that can be made from numbers.\"\n", + " if len(numbers) == 1: # Only one way to make an expression out of a single number\n", + " return Counter(numbers)\n", + " else: \n", + " result = Counter()\n", " for (Lnums, Rnums) in splits(numbers):\n", - " for (L, R) in itertools.product(expressions(Lnums), expressions(Rnums)):\n", - " Lexp, Rexp = '(' + EXPS[Lnums][L], EXPS[Rnums][R] + ')'\n", - " count = COUNTS[Lnums][L] * COUNTS[Rnums][R]\n", - " expr(numbers, L * R, Lexp + '*' + Rexp, count)\n", - " expr(numbers, L - R, Lexp + '-' + Rexp, count)\n", - " expr(numbers, L + R, Lexp + '+' + Rexp, count)\n", - " if R != 0: \n", - " expr(numbers, L / R, Lexp + '/' + Rexp, count)\n", - " return EXPS[numbers]\n", - "\n", - "def expr(numbers, val, exp, count):\n", - " \"Fill EXPS[numbers][val] with exp, and increment COUNTS.\"\n", - " EXPS[numbers][val] = exp\n", - " COUNTS[numbers][val] += count\n", - " \n", - "def clear(): EXPS.clear(); COUNTS.clear()" + " for L in counts(Lnums):\n", + " for R in counts(Rnums):\n", + " count = counts(Lnums)[L] * counts(Rnums)[R]\n", + " result[L + R] += count\n", + " result[L - R] += count\n", + " result[L * R] += count\n", + " if R != 0:\n", + " result[L / R] += count\n", + " return result" ] }, { "cell_type": "code", - "execution_count": 14, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, + "execution_count": 28, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{-62: '(10-(9*8))',\n", - " -7: '((10-9)-8)',\n", - " -6.888888888888889: '((10/9)-8)',\n", - " 0.125: '((10-9)/8)',\n", - " 0.1388888888888889: '((10/9)/8)',\n", - " 0.5882352941176471: '(10/(9+8))',\n", - " 2.375: '((10+9)/8)',\n", - " 8: '((10-9)*8)',\n", - " 8.875: '(10-(9/8))',\n", - " 8.88888888888889: '((10/9)*8)',\n", - " 9: '((10-9)+8)',\n", - " 9.11111111111111: '((10/9)+8)',\n", - " 10: '(10/(9-8))',\n", - " 11: '((10+9)-8)',\n", - " 11.125: '(10+(9/8))',\n", - " 11.25: '((10*9)/8)',\n", - " 27: '((10+9)+8)',\n", - " 82: '((10*9)-8)',\n", - " 98: '((10*9)+8)',\n", - " 152: '((10+9)*8)',\n", - " 170: '(10*(9+8))',\n", - " 720: '((10*9)*8)'}" + "Counter({0: 1, 1.0: 1, 4: 2})" ] }, - "execution_count": 14, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "clear()\n", - "expressions((10, 9, 8))" + "counts((2, 2)) # corresponds to {0: '2-2', 1.0: '2/2', 4: '2+2' or '2*2'}" ] }, { "cell_type": "code", - "execution_count": 15, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Counter({-62: 1,\n", - " -7: 2,\n", - " -6.888888888888889: 1,\n", - " 0.125: 1,\n", - " 0.1388888888888889: 2,\n", - " 0.5882352941176471: 1,\n", - " 2.375: 1,\n", - " 8: 1,\n", - " 8.875: 1,\n", - " 8.88888888888889: 2,\n", - " 9: 2,\n", - " 9.11111111111111: 1,\n", - " 10: 2,\n", - " 11: 2,\n", - " 11.125: 1,\n", - " 11.25: 2,\n", - " 27: 2,\n", - " 82: 2,\n", - " 98: 1,\n", - " 152: 1,\n", - " 170: 1,\n", - " 720: 2})" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "COUNTS[(10, 9, 8)]" - ] - }, - { - "cell_type": "code", - "execution_count": 16, + "execution_count": 29, "metadata": { "button": false, "new_sheet": false, @@ -795,13 +1525,13 @@ "2" ] }, - "execution_count": 16, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "COUNTS[(10, 9, 8)][27]" + "counts((10, 9, 8))[27]" ] }, { @@ -814,63 +1544,12 @@ } }, "source": [ - "Looks good to me. Now let's repeat the computation, this time keeping track of `COUNTS`:" + "Looks good to me. Now let's see if we can answer the question." ] }, { "cell_type": "code", - "execution_count": 17, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 1min 4s, sys: 1.15 s, total: 1min 5s\n", - "Wall time: 1min 6s\n" - ] - }, - { - "data": { - "text/plain": [ - "'(((((((10+((9*8)*7))-6)-5)*4)+3)+2)-1)'" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "clear()\n", - "\n", - "%time expressions(c10)[2016]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "# The Answer (?)\n", - "\n", - "Now we can read off the answer:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, + "execution_count": 30, "metadata": { "button": false, "new_sheet": false, @@ -885,13 +1564,13 @@ "30066" ] }, - "execution_count": 18, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "COUNTS[c10][2016]" + "counts(c10)[2016]" ] }, { @@ -906,18 +1585,14 @@ "source": [ "This says there are 30,066 distinct expressions for 2016. \n", "\n", - "**But I don't believe it.**\n", + "**But we're forgetting about round-off error.**\n", "\n", - "Why not? Because floating point division can have round-off errors. \n", - "\n", - "# Dealing with Round-off Errors\n", - "\n", - "Consider this:" + "Let's find all the values that are very near to `2016`:" ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 31, "metadata": { "button": false, "new_sheet": false, @@ -929,94 +1604,28 @@ { "data": { "text/plain": [ - "2015.9999999999998" + "{2015.999999999997: 15,\n", + " 2015.999999999999: 10,\n", + " 2015.9999999999993: 14,\n", + " 2015.9999999999995: 1930,\n", + " 2015.9999999999998: 5868,\n", + " 2016.0: 30066,\n", + " 2016.0000000000002: 5792,\n", + " 2016.0000000000005: 510,\n", + " 2016.0000000000018: 264,\n", + " 2016.000000000002: 18,\n", + " 2016.0000000000023: 12}" ] }, - "execution_count": 19, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "2015 + 1/3 + 1/3 + 1/3" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "2015 + 1/3 + 1/3 + 1/3 == 2016" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "This means there might be perfectly good expressions that are counted under `2015.9999999999998` (or some similar number) when they should be counted for `2016`. Let's find all the values that are very near to `2016`:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{2015.999999999997,\n", - " 2015.999999999999,\n", - " 2015.9999999999993,\n", - " 2015.9999999999995,\n", - " 2015.9999999999998,\n", - " 2016,\n", - " 2016.0000000000002,\n", - " 2016.0000000000005,\n", - " 2016.0000000000018,\n", - " 2016.000000000002,\n", - " 2016.0000000000023}" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "epsilon = 10 ** -8\n", - "\n", - "{y for y in expressions(c10)\n", - " if abs(y - 2016) < epsilon}" + "{y: counts(c10)[y] \n", + " for y in counts(c10)\n", + " if abs(y - 2016) < 1e-10}" ] }, { @@ -1030,57 +1639,12 @@ }, "source": [ "I suspect that all of these actually should be exactly 2016. \n", - "\n", - "To be absolutely sure, I could re-do the calculations using exact rational arithmetic, as provided by the `fractions.Fraction` data type. From experience I know that would be an order of magnitude slower, so instead I'll just add up all the counts in the `COUNTS` table that are within epsilon of 2016:" + "To be absolutely sure, I could re-do the calculations using exact rational arithmetic, as provided by the `fractions.Fraction` data type. From experience I know that would be an order of magnitude slower, so instead I'll just add up the counts:" ] }, { "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{2015.999999999997: 15,\n", - " 2015.999999999999: 10,\n", - " 2015.9999999999993: 14,\n", - " 2015.9999999999995: 1930,\n", - " 2015.9999999999998: 5868,\n", - " 2016: 30066,\n", - " 2016.0000000000002: 5792,\n", - " 2016.0000000000005: 510,\n", - " 2016.0000000000018: 264,\n", - " 2016.000000000002: 18,\n", - " 2016.0000000000023: 12}" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "{y: COUNTS[c10][y] for y in expressions(c10)\n", - " if abs(y - 2016) < epsilon}" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "# The Answer (!)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -1089,7 +1653,7 @@ "44499" ] }, - "execution_count": 23, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -1111,17 +1675,6 @@ "I have more confidence in this answer, 44,499, than in 30,066, but I wouldn't accept it as definitive until it was independently verified and passed an extensive test suite. And of course, if you have a different definition of \"distinct solution,\" you will get a different answer." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "clear()" - ] - }, { "cell_type": "markdown", "metadata": { @@ -1132,1831 +1685,77 @@ } }, "source": [ - "# Exponentiation, and Four 4s\n", - "\n", - "Now let's turn to another of Alex's puzzles: making 2016 and other target values from a string of four or five `4`s, with exponentiation allowed. Exponentiation is tricky for five reasons:\n", - "\n", - "- **Division by zero**: `(0 ^ -1)` is the same as `(1 / 0)`, and gives a `ZeroDivisionError`.\n", - "- **Irrationals**: `(3 ^ (1 / 2))` is an irrational number; so we can't do exact rational arithmetic.\n", - "- **Imaginaries**: `(-1 ^ (1 / 2))` is an imaginary number, but Python gives a `ValueError`.\n", - "- **Overflow**: `(10. ^ (9. ^ 8.))`, as a `float`, gives a `OverflowError`.\n", - "- **Finite memory**: [`(10 ^ (9 ^ (8 * 7)))`](http://www.wolframalpha.com/input/?i=10+%5E+9+%5E+56), as an `int`, gives an `OutOfMemoryError` (even if your memory uses every atom on Earth).\n", - "\n", - "How do we deal with this? We can't do exact rational arithmetic. We could try to do exact *algebra*, perhaps using [SymPy](http://www.sympy.org/en/index.html), but that seems difficult\n", - "and computationally expensive, so instead I will abandon the goal of exact computation, and do everything in the domain of floats (reluctantly accepting that there will be some round-off errors). We'll coerce numbers to floats when we first put them in the table, and all subsequent operations will be with floats. I define a new function, `expr2`, to call `expr`, catching arithmetic errors. Since we are making some rather arbitrary decisions about what expressions are allowed (e.g. imaginary numbers are not), I'll give up on trying to maintain `COUNTS`." - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": { - "button": false, - "collapsed": true, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [], - "source": [ - "from operator import add, sub, mul, truediv\n", - "\n", - "def expressions(numbers):\n", - " \"Fill EXPS table for numbers, and all sub-sequences of numbers. Return EXPS[numbers]\"\n", - " if numbers in EXPS: # Already did the work\n", - " pass\n", - " elif len(numbers) == 1: # Only one way to make an expression out of a single number\n", - " expr(numbers, float(numbers[0]), str(numbers[0]))\n", - " else: # Split in all ways; fill tables for left and right; combine tables in all ways\n", - " for (Lnums, Rnums) in splits(numbers):\n", - " for (L, R) in itertools.product(expressions(Lnums), expressions(Rnums)):\n", - " Lexp, Rexp = '(' + EXPS[Lnums][L], EXPS[Rnums][R] + ')'\n", - " expr2(numbers, L, pow, R, Lexp + '^' + Rexp)\n", - " expr2(numbers, L, truediv, R, Lexp + '/' + Rexp)\n", - " expr2(numbers, L, mul, R, Lexp + '*' + Rexp)\n", - " expr2(numbers, L, add, R, Lexp + '+' + Rexp)\n", - " expr2(numbers, L, sub, R, Lexp + '-' + Rexp)\n", - " return EXPS[numbers]\n", - "\n", - "def expr2(numbers, L, op, R, exp): \n", - " \"Fill table entries for op(L, R), catching errors.\"\n", - " try:\n", - " expr(numbers, op(L, R), exp)\n", - " except (ArithmeticError, ValueError):\n", - " pass\n", - " \n", - "def expr(numbers, value, exp): EXPS[numbers][value] = exp" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "Now we can solve the \"2016 with five fours\" puzzle:" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'(((4^4)-4)*(4+4))'" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "clear()\n", - "\n", - "expressions((4, 4, 4, 4, 4))[2016]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "I'll define a function to create a table of makeable integers:" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "button": false, - "collapsed": true, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [], - "source": [ - "def makeable(numbers):\n", - " \"A table of {i: expression} for all integers i from 0 up to first unmakeable.\"\n", - " return {i: expressions(numbers)[i]\n", - " for i in range(unmakeable(numbers))}\n", - "\n", - "def unmakeable(numbers):\n", - " \"Smallest positive integer than can't be made by numbers.\"\n", - " return next(i for i in itertools.count(1) if i not in expressions(numbers))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "We'll use this to see if we can solve the \"0 to 9 with four fours\" puzzle:" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{0: '(((4+4)-4)-4)',\n", - " 1: '(((4+4)-4)/4)',\n", - " 2: '((4/(4+4))*4)',\n", - " 3: '(((4+4)+4)/4)',\n", - " 4: '(((4-4)*4)+4)',\n", - " 5: '(((4*4)+4)/4)',\n", - " 6: '(((4+4)/4)+4)',\n", - " 7: '((4-(4/4))+4)',\n", - " 8: '(((4+4)-4)+4)',\n", - " 9: '(((4/4)+4)+4)'}" - ] - }, - "execution_count": 65, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "makeable((4, 4, 4, 4))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "Yes: we can get 0 to 9 (but not 10).\n", - "\n", - "Now I'll see what integers we can make with five fives. Legend has it that you can get all the way up to 55:" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{0: '((((5-5)*5)-5)+5)',\n", - " 1: '(((5-(5*5))/5)+5)',\n", - " 2: '((((5+5)/5)-5)+5)',\n", - " 3: '((((5*5)-5)-5)/5)',\n", - " 4: '((((5-5)-5)/5)+5)',\n", - " 5: '((5/((5^5)^5))+5)',\n", - " 6: '((((5/5)+5)*5)/5)',\n", - " 7: '(((5/5)+(5/5))+5)',\n", - " 8: '((((5+5)+5)/5)+5)',\n", - " 9: '((((5+5)*5)-5)/5)',\n", - " 10: '((((5*5)-5)-5)-5)',\n", - " 11: '((((5+5)*5)+5)/5)',\n", - " 12: '((((5+5)/5)+5)+5)'}" - ] - }, - "execution_count": 67, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "clear()\n", - "\n", - "five5s = (5, 5, 5, 5, 5)\n", - "\n", - "makeable(five5s)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "We didn't get there. \n", - "\n", - "# More Operations\n", - "\n", - "With some research, I [see](http://www.infobarrel.com/Five_Fives_Problem_Recreational_Mathematics) that others who got up to 55 with five 5s used some or all of these three concepts:\n", - "\n", - "- **digit concatenation**: `55`\n", - "- **decimal point**: `.5`\n", - "- **unary operations**: `-5`, `5!`, and √ `5`\n", - "\n", - "\n", - "We'll refactor `expressions` to call the following three new subfunctions:\n", - "\n", - "- `digit_expressions`: For every subsequence of numbers, we'll smush the digits together, and then make a table entry for those resulting digits as an integer, and with a decimal point in each possible position.\n", - "- `binary_expressions`: The code that previously was the main body of `expressions`.\n", - "- `unary_expressions`: Apply the unary operators to every entry in the table. \n", - "\n", - "We'll still do all computation in the domain of floats." - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [], - "source": [ - "from math import sqrt, factorial\n", - "\n", - "def expressions(numbers):\n", - " \"Fill EXPS table for numbers, and all sub-sequences of numbers. Return EXPS[numbers]\"\n", - " if numbers not in EXPS: \n", - " digit_expressions(numbers)\n", - " binary_expressions(numbers)\n", - " unary_expressions(numbers)\n", - " return EXPS[numbers]\n", - "\n", - "def digit_expressions(numbers):\n", - " \"Fill EXPS with digits, with opyional minus sign and decimal point.\"\n", - " # digit_expressions(1, 2) => 12, .12, 1.2\n", - " exp = ''.join(str(n) for n in numbers)\n", - " for d in range(len(exp) + 1):\n", - " decimal = (exp[:d] + '.' + exp[d:]).rstrip('.')\n", - " if d < 2 or not decimal.startswith('0'): # '01' is illegal; '0', '0.1' ok\n", - " expr(numbers, float(decimal), decimal)\n", - " expr(numbers, -float(decimal), '-' + decimal)\n", - " \n", - "def binary_expressions(numbers):\n", - " \"Fill EXPS with all expressions formed by splitting numbers and combining with an op.\"\n", - " for (Lnums, Rnums) in splits(numbers):\n", - " for (L, R) in itertools.product(expressions(Lnums), expressions(Rnums)):\n", - " Lexp, Rexp = '(' + EXPS[Lnums][L], EXPS[Rnums][R] + ')'\n", - " expr2(numbers, L, truediv, R, Lexp + '/' + Rexp)\n", - " expr2(numbers, L, mul, R, Lexp + '*' + Rexp)\n", - " expr2(numbers, L, add, R, Lexp + '+' + Rexp)\n", - " expr2(numbers, L, sub, R, Lexp + '-' + Rexp)\n", - " if 1 <= R <= 10 and (L > 0 or int(R) == R):\n", - " expr2(numbers, L, pow, R, Lexp + '^' + Rexp)\n", - " \n", - "def unary_expressions(numbers):\n", - " \"Fill tables for -v, √v and v!\"\n", - " for v in tuple(EXPS[numbers]):\n", - " exp = EXPS[numbers][v]\n", - " expr(numbers, -v, '-(' + exp + ')')\n", - " if v > 0: \n", - " expr(numbers, sqrt(v), '√' + exp)\n", - " if 3 <= v <= 6 and v == int(v):\n", - " expr(numbers, factorial(v), exp + '!')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "Now that we have more variety in the types of expressions formed, I want to choose a \"good\" expression to represent each value. I'll modify `expr` so that when there are multiple expressions for a value, it chooses the one with the least \"weight,\" where I define the `weight` of a string as the sum of the weights of the characters, where the square root sign is the heaviest, and other characters are as listed." - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [], - "source": [ - "def expr(numbers, value, exp): \n", - " \"Fill EXPS[numbers][val] with exp, unless we already have a lighter exp.\"\n", - " if value not in EXPS[numbers] or weight(exp) <= weight(EXPS[numbers][value]):\n", - " EXPS[numbers][value] = exp\n", - " \n", - "WEIGHTS = {'√':3, '.':2, '^':1.3, '/':1.2, '-':1.1}\n", - " \n", - "def weight(exp, weights=WEIGHTS): \n", - " return sum(weights.get(c, 1) for c in exp)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "We'll try again:" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 6min 4s, sys: 2.81 s, total: 6min 7s\n", - "Wall time: 6min 7s\n" - ] - }, - { - "data": { - "text/plain": [ - "{0: '((55-55)*5)',\n", - " 1: '((55/55)^5)',\n", - " 2: '(55/(55*.5))',\n", - " 3: '√(5!-(555/5))',\n", - " 4: '(5-(55/55))',\n", - " 5: '((55-55)+5)',\n", - " 6: '((55/55)+5)',\n", - " 7: '(((55+5)/5)-5)',\n", - " 8: '(((5!-55)/5)-5)',\n", - " 9: '(5!-(555/5))',\n", - " 10: '(5!-(55+55))',\n", - " 11: '((.55*5!)-55)',\n", - " 12: '((5.5/55)*5!)',\n", - " 13: '(((55+5)+5)/5)',\n", - " 14: '((5*5)-(55/5))',\n", - " 15: '((55/5.5)+5)',\n", - " 16: '((55+(5*5))/5)',\n", - " 17: '(((55+5)/5)+5)',\n", - " 18: '(((5!-55)/5)+5)',\n", - " 19: '((5*5)-((5/5)+5))',\n", - " 20: '(55/(.55*5))',\n", - " 21: '(((55/5)+5)+5)',\n", - " 22: '((55+55)/5)',\n", - " 23: '(((55/.5)+5)/5)',\n", - " 24: '(5-(55/55))!',\n", - " 25: '(55-((5*5)+5))',\n", - " 26: '(55-((5!/5)+5))',\n", - " 27: '((55/(5*.5))+5)',\n", - " 28: '((55+(5/5))*.5)',\n", - " 29: '(((5*5)-(5/5))+5)',\n", - " 30: '(((55/5)-5)*5)',\n", - " 31: '(((55*5)-5!)/5)',\n", - " 32: '(((5-5!)/5)+55)',\n", - " 33: '((55+5)*.55)',\n", - " 34: '(((55+5!)-5)/5)',\n", - " 35: '((55-(5*5))+5)',\n", - " 36: '((55/5)+(5*5))',\n", - " 37: '(((5!-55)+5!)/5)',\n", - " 38: '(((.55*.5)*5!)+5)',\n", - " 39: '((((5!/5)+5)+5)+5)',\n", - " 40: '(55-((5+5)+5))',\n", - " 41: '(5!-(55+(5!/5)))',\n", - " 42: '((.55*5!)-(5!/5))',\n", - " 43: '(55-(5!/(5+5)))',\n", - " 44: '(55-(55/5))',\n", - " 45: '((5!*5)-555)',\n", - " 46: '(((55/.5)+5!)/5)',\n", - " 47: '(((5!-5)/5)+(5!/5))',\n", - " 48: '(5!/((5.5-5)*5))',\n", - " 49: '(55-((5/5)+5))',\n", - " 50: '(55.5-5.5)',\n", - " 51: '((55+(5/5))-5)',\n", - " 52: '(((5.5+5)*5)-.5)',\n", - " 53: '(55-((5+5)/5))',\n", - " 54: '(((55*5)-5)/5)',\n", - " 55: '((55+55)*.5)',\n", - " 56: '(((55*5)+5)/5)',\n", - " 57: '(((5+5)/5)+55)',\n", - " 58: '(55.5+(5*.5))',\n", - " 59: '((55-(5/5))+5)',\n", - " 60: '(√(55*55)+5)',\n", - " 61: '(55.5+5.5)',\n", - " 62: '((55-(5!/5))/.5)',\n", - " 63: '(((5!-5)*.5)+5.5)',\n", - " 64: '(5!-(55+(5/5)))',\n", - " 65: '((5-(55+5))+5!)',\n", - " 66: '((55/5)+55)',\n", - " 67: '((5!/(5+5))+55)',\n", - " 68: '(((5!+5)*.5)+5.5)',\n", - " 69: '((5!-5)*((.5/5)+.5))',\n", - " 70: '(((55+5)+5)+5)',\n", - " 71: '((55/5)+(5!*.5))',\n", - " 72: '(((5/5)+5)!/(5+5))',\n", - " 73: '(((5!+5!)/5)+(5*5))',\n", - " 74: '((55+(5!/5))-5)',\n", - " 75: '((55+(5*5))-5)',\n", - " 76: '(((.55*5!)+5)+5)',\n", - " 77: '((((5!+5!)+5!)/5)+5)',\n", - " 78: '(((5!-5)/5)+55)',\n", - " 79: '(((55*5)+5!)/5)',\n", - " 80: '(((55/5)+5)*5)',\n", - " 81: '(((5!/√(5-.5))+√.5)/√.5)',\n", - " 82: '(((5-(5!/5))/.5)+5!)',\n", - " 83: '(((5!-5)/5)+(5!*.5))',\n", - " 84: '((55+(5!/5))+5)',\n", - " 85: '((55+(5*5))+5)',\n", - " 86: '((55/.5)-(5!/5))',\n", - " 87: '((555-5!)/5)',\n", - " 88: '((.55/(.5^5))*5)',\n", - " 89: '(((5!/5)-55)+5!)',\n", - " 90: '(((5*5)-55)+5!)',\n", - " 91: '((.55*5!)+(5*5))',\n", - " 92: '(5!-((55*.5)+.5))',\n", - " 93: '((.5-(55*.5))+5!)',\n", - " 94: '(5!-((5/5)+(5*5)))',\n", - " 95: '(((55-5)/.5)-5)',\n", - " 96: '(((5/5)+5!)-(5*5))',\n", - " 97: '((((5!*5)+5)-5!)/5)',\n", - " 98: '(5!-(55/(5*.5)))',\n", - " 99: '((55-5.5)/.5)',\n", - " 100: '((55/.5)-(5+5))',\n", - " 101: '((55.5-5)/.5)',\n", - " 102: '((((5-5!)/5)+5)+5!)',\n", - " 103: '(((5!+5!)/5)+55)',\n", - " 104: '(5!-((55/5)+5))',\n", - " 105: '((55+55)-5)',\n", - " 106: '((555/5)-5)',\n", - " 107: '(((55-5!)/5)+5!)',\n", - " 108: '(5!-((55+5)/5))',\n", - " 109: '(((5!*5)-55)/5)',\n", - " 110: '((555-5)/5)',\n", - " 111: '(555/√(5*5))',\n", - " 112: '((555+5)/5)',\n", - " 113: '(5!-(((5+5)/5)+5))',\n", - " 114: '((5-(55/5))+5!)',\n", - " 115: '((55+55)+5)',\n", - " 116: '((555/5)+5)',\n", - " 117: '((((5+5)/5)-5)+5!)',\n", - " 118: '(5!-(5!/(55+5)))',\n", - " 119: '(5!-(55/55))',\n", - " 120: '((55-55)+5)!',\n", - " 121: '((55/55)+5!)',\n", - " 122: '((5!/(55+5))+5!)',\n", - " 123: '((((5+5)+5)/5)+5!)',\n", - " 124: '(((5*5)*5)-(5/5))',\n", - " 125: '(((55-5)*5)*.5)',\n", - " 126: '(((55/5)+5!)-5)',\n", - " 127: '(((5.5/5)*5!)-5)',\n", - " 128: '(((5*.5)+5.5)+5!)',\n", - " 129: '((5!-55.5)/.5)',\n", - " 130: '((55/5.5)+5!)',\n", - " 131: '(((5!*5)+55)/5)',\n", - " 132: '(((55+5)/5)+5!)',\n", - " 133: '(((5!-55)/5)+5!)',\n", - " 134: '((55/.5)+(5!/5))',\n", - " 135: '((555+5!)/5)',\n", - " 136: '(((55/5)+5!)+5)',\n", - " 137: '(((5.5/5)*5!)+5)',\n", - " 138: '(((55*5)*.5)+.5)',\n", - " 139: '((((5/5)+5)!/5)-5)',\n", - " 140: '(((55*5)+5)*.5)',\n", - " 141: '(((5.5+5)/.5)+5!)',\n", - " 142: '((55/(5*.5))+5!)',\n", - " 143: '((((5/5)+5)!-5)/5)',\n", - " 144: '(((55/5)-5)!/5)',\n", - " 145: '((((5*5)+5)*5)-5)',\n", - " 146: '(((5/5)+(5*5))+5!)',\n", - " 147: '(((55*.5)+5!)-.5)',\n", - " 148: '(((55*.5)+5!)+.5)',\n", - " 149: '(((5*5)*5)+(5!/5))',\n", - " 150: '((55-(5*5))*5)',\n", - " 151: '((55-(5!/5))+5!)',\n", - " 152: '((((5+5)/5)^5)+5!)',\n", - " 153: '(((.55*.5)*5!)+5!)',\n", - " 154: '((((5!/5)+5!)+5)+5)',\n", - " 155: '((55-(5!/5))*5)',\n", - " 156: '(((5.5*5!)+5!)/5)'}" - ] - }, - "execution_count": 70, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "clear()\n", - "\n", - "%time makeable(five5s)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "Wow! We almost tripled the 55 goal! (Although the increased number of expressions means it took 6 minutes.)\n", - "\n", - "I have to say, I would never have come up with the solution for 81 on my own. It works because (√(5 - .5) \\* √.5) = √(4.5 \\* 0.5) = √(9/4) = 3/2." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "# Even More Operations \n", - "\n", - "At the risk of making the computation take even longer, I'm going to add two more unary operations:\n", - "\n", - "- **Floor**: ⌊*x*⌋ is the largest integer less than or equal to *x* (in other words, rounding down).\n", - "- **Ceiling**: ⌈*x*⌉ is the smallest integer greater than or equal to *x* (in other words, rounding up).\n", - "\n", - "These operations are useful because they produce integers, and our targets are the integers (from 0 up to whatever). \n", - "\n", - "In addition, I'll allow two consecutive applications of unary operators, thus allowing expressions such as\n", - "\n", - "- ⌊√5⌋ = 2\n", - "- ⌈5.5⌉! = 720\n", - "\n", - "But still not allowing three consecutive applications, such as\n", - "\n", - "- ⌈√5⌉! = 6\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": { - "button": false, - "collapsed": true, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [], - "source": [ - "from math import floor, ceil\n", - "\n", - "floor_ceil_allowed = True # Should we allow floor and ceil?\n", - "\n", - "def unary_expressions(numbers):\n", - " \"Fill tables for -v, √v and v!, ⌊x⌋, and ⌈x⌉\"\n", - " for i in range(2):\n", - " for v in tuple(EXPS[numbers]):\n", - " exp = EXPS[numbers][v]\n", - " expr(numbers, -v, '-(' + exp + ')')\n", - " if 0 < v <= 100 and 4*v == round(4*v):\n", - " # Be stingier in allowing √ operator\n", - " expr(numbers, sqrt(v), '√' + exp)\n", - " if 3 <= v <= 6 and v == int(v):\n", - " expr(numbers, factorial(v), exp + '!')\n", - " if floor_ceil_allowed and v != int(v):\n", - " uexp = unbracket(exp)\n", - " expr(numbers, floor(v), '⌊' + uexp + '⌋')\n", - " expr(numbers, ceil(v), '⌈' + uexp + '⌉')\n", - " \n", - "def unbracket(exp):\n", - " \"Remove outer brackets from exp if they are there.\"\n", - " if exp.startswith('(') and exp.endswith(')'):\n", - " return exp[1:-1]\n", - " else:\n", - " return exp" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "Let's try (warning—it will take 20 or 30 minutes to run):" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 24min 17s, sys: 3.22 s, total: 24min 21s\n", - "Wall time: 24min 23s\n" - ] - }, - { - "data": { - "text/plain": [ - "{0: '⌊55/555⌋',\n", - " 1: '⌈55/555⌉',\n", - " 2: '⌊.5555*5⌋',\n", - " 3: '⌈.5555*5⌉',\n", - " 4: '⌊5-.5555⌋',\n", - " 5: '⌊5.5555⌋',\n", - " 6: '⌈5.5555⌉',\n", - " 7: '⌈5.55+.55⌉',\n", - " 8: '⌈5.555+√5⌉',\n", - " 9: '⌊55/5.55⌋',\n", - " 10: '⌊555/55⌋',\n", - " 11: '⌈555/55⌉',\n", - " 12: '⌈55.55/5⌉',\n", - " 13: '⌊(.555*5)*5⌋',\n", - " 14: '⌈(.555*5)*5⌉',\n", - " 15: '⌊(5.55+5)+5⌋',\n", - " 16: '⌈(5.55+5)+5⌉',\n", - " 17: '⌈(55.5/5)+5⌉',\n", - " 18: '⌊55/⌈5!/55⌉⌋',\n", - " 19: '⌈55/⌈5!/55⌉⌉',\n", - " 20: '(⌊555/5!⌋*5)',\n", - " 21: '⌊5!/5.555⌋',\n", - " 22: '⌈5!/5.555⌉',\n", - " 23: '⌈555/(5*5)⌉',\n", - " 24: '⌊5-.5555⌋!',\n", - " 25: '(⌊5.555⌋*5)',\n", - " 26: '⌈(55*55)/5!⌉',\n", - " 27: '⌊5.555*5⌋',\n", - " 28: '⌈5.555*5⌉',\n", - " 29: '(⌊555/5!⌋!+5)',\n", - " 30: '⌊.555*55⌋',\n", - " 31: '⌈.555*55⌉',\n", - " 32: '⌊(5.55*5)+5⌋',\n", - " 33: '⌈(5.55*5)+5⌉',\n", - " 34: '⌈5.55*⌈5.5⌉⌉',\n", - " 35: '⌊(55*.55)+5⌋',\n", - " 36: '⌈(55*.55)+5⌉',\n", - " 37: '⌊(55/√55)*5⌋',\n", - " 38: '⌈(55/√55)*5⌉',\n", - " 39: '⌊55.55*√.5⌋',\n", - " 40: '(55-((5+5)+5))',\n", - " 41: '⌊5.55*√55⌋',\n", - " 42: '⌈5.55*√55⌉',\n", - " 43: '⌈55-(√55+5)⌉',\n", - " 44: '(55-(55/5))',\n", - " 45: '((5!*5)-555)',\n", - " 46: '⌊5555/5!⌋',\n", - " 47: '⌈5555/5!⌉',\n", - " 48: '⌊55.5-√55⌋',\n", - " 49: '⌊55-5.55⌋',\n", - " 50: '⌈55-5.55⌉',\n", - " 51: '⌈55.55-5⌉',\n", - " 52: '⌊55-(5!/55)⌋',\n", - " 53: '⌈55-(5!/55)⌉',\n", - " 54: '⌊55-.555⌋',\n", - " 55: '⌊55.555⌋',\n", - " 56: '⌈55.555⌉',\n", - " 57: '⌈55.55+.5⌉',\n", - " 58: '⌈(5!/55)+55⌉',\n", - " 59: '⌊(55-.55)+5⌋',\n", - " 60: '⌊55.55+5⌋',\n", - " 61: '⌈55.55+5⌉',\n", - " 62: '⌊√55.5+55⌋',\n", - " 63: '⌈√55.5+55⌉',\n", - " 64: '⌊5!-55.55⌋',\n", - " 65: '⌈5!-55.55⌉',\n", - " 66: '⌊.5555*5!⌋',\n", - " 67: '⌈.5555*5!⌉',\n", - " 68: '⌈(√55+55)+5⌉',\n", - " 69: '⌊555/⌈√55⌉⌋',\n", - " 70: '⌈555/⌈√55⌉⌉',\n", - " 71: '⌊(.555*5!)+5⌋',\n", - " 72: '⌈(.555*5!)+5⌉',\n", - " 73: '⌊(5!/55)^5.5⌋',\n", - " 74: '⌊555/√55⌋',\n", - " 75: '⌈555/√55⌉',\n", - " 76: '⌊555/(5+√5)⌋',\n", - " 77: '⌈555/(5+√5)⌉',\n", - " 78: '⌈√⌊5!/55⌋*55⌉',\n", - " 79: '⌊555/⌊√55⌋⌋',\n", - " 80: '⌊55.5+(5*5)⌋',\n", - " 81: '⌈55.5+(5*5)⌉',\n", - " 82: '⌊(55*.5)+55⌋',\n", - " 83: '⌈(55*.5)+55⌉',\n", - " 84: '((55+(5!/5))+5)',\n", - " 85: '((55+(5*5))+5)',\n", - " 86: '⌊5!-(5^(5!/55))⌋',\n", - " 87: '((555-5!)/5)',\n", - " 88: '⌈√5^5.555⌉',\n", - " 89: '⌊5!-(55*.55)⌋',\n", - " 90: '⌊(55-5)/.55⌋',\n", - " 91: '⌈(55-5)/.55⌉',\n", - " 92: '⌊555/⌈5.5⌉⌋',\n", - " 93: '⌈555/⌈5.5⌉⌉',\n", - " 94: '⌊(55/.55)-5⌋',\n", - " 95: '⌈(55/.55)-5⌉',\n", - " 96: '⌊(55+5!)*.55⌋',\n", - " 97: '⌈(55+5!)*.55⌉',\n", - " 98: '⌊⌊55-.5⌋/.55⌋',\n", - " 99: '⌊55/.555⌋',\n", - " 100: '⌈55/.555⌉',\n", - " 101: '⌈555/5.5⌉',\n", - " 102: '⌈⌈55.5⌉/.55⌉',\n", - " 103: '⌈⌈55/.55⌉+√5⌉',\n", - " 104: '⌊(55/.55)+5⌋',\n", - " 105: '((55+55)-5)',\n", - " 106: '((555/5)-5)',\n", - " 107: '⌊(55+55)-√5⌋',\n", - " 108: '⌈(55+55)-√5⌉',\n", - " 109: '⌊(55+55)-.5⌋',\n", - " 110: '⌊55.5+55⌋',\n", - " 111: '⌈55.5+55⌉',\n", - " 112: '⌈555.5/5⌉',\n", - " 113: '⌈(55+55)+√5⌉',\n", - " 114: '⌊5!-5.555⌋',\n", - " 115: '((55+55)+5)',\n", - " 116: '((555/5)+5)',\n", - " 117: '⌊5!-(.555*5)⌋',\n", - " 118: '⌈5!-(.555*5)⌉',\n", - " 119: '⌊5!-.5555⌋',\n", - " 120: '⌊5.5555⌋!',\n", - " 121: '⌈.5555+5!⌉',\n", - " 122: '⌊(.555*5)+5!⌋',\n", - " 123: '⌊555/(5-.5)⌋',\n", - " 124: '⌊55.55*√5⌋',\n", - " 125: '⌊5.555+5!⌋',\n", - " 126: '⌈5.555+5!⌉',\n", - " 127: '⌊⌈5^5.5⌉/55⌋',\n", - " 128: '⌈⌈5^5.5⌉/55⌉',\n", - " 129: '⌊(5/.555)+5!⌋',\n", - " 130: '⌊(5.55+5)+5!⌋',\n", - " 131: '⌈(5.55+5)+5!⌉',\n", - " 132: '⌊(√55-5)*55⌋',\n", - " 133: '⌈(√55-5)*55⌉',\n", - " 134: '⌈(5.55/5)*5!⌉',\n", - " 135: '((555+5!)/5)',\n", - " 136: '(((55/5)+5!)+5)',\n", - " 137: '⌈⌈55.5+5⌉*√5⌉',\n", - " 138: '⌊(5.55*5)*5⌋',\n", - " 139: '⌈(5.55*5)*5⌉',\n", - " 140: '(⌈5.55*5⌉*5)',\n", - " 141: '⌊⌊55^√5⌋/55⌋',\n", - " 142: '⌈⌊55^√5⌋/55⌉',\n", - " 143: '⌈((55*5)*.5)+5⌉',\n", - " 144: '(⌈5.555⌉!/5)',\n", - " 145: '((⌊5.55⌋*5)+5!)',\n", - " 146: '⌊(5-√5.5)*55⌋',\n", - " 147: '⌊(5.55*5)+5!⌋',\n", - " 148: '⌈(5.55*5)+5!⌉',\n", - " 149: '⌈⌊55*.5⌋*5.5⌉',\n", - " 150: '(⌊55*.55⌋*5)',\n", - " 151: '⌊(55*.55)*5⌋',\n", - " 152: '⌈(55*.55)*5⌉',\n", - " 153: '⌊(.55+√5)*55⌋',\n", - " 154: '(⌈55*.5⌉*5.5)',\n", - " 155: '(⌈55*.55⌉*5)',\n", - " 156: '⌈(55+55)/√.5⌉',\n", - " 157: '⌊(555*.5)-5!⌋',\n", - " 158: '⌈(555*.5)-5!⌉',\n", - " 159: '⌈(55-5)*√(5+5)⌉',\n", - " 160: '(((55*5)-5!)+5)',\n", - " 161: '⌊(5*.5)^5.55⌋',\n", - " 162: '⌈(5*.5)^5.55⌉',\n", - " 163: '⌊(5!/.55)-55⌋',\n", - " 164: '⌊(.555*5)^5⌋',\n", - " 165: '(⌈5!/55⌉*55)',\n", - " 166: '⌊5.5^⌈5!/55⌉⌋',\n", - " 167: '⌈5.5^⌈5!/55⌉⌉',\n", - " 168: '⌈(55-√55)+5!⌉',\n", - " 169: '⌊(55-5.5)+5!⌋',\n", - " 170: '⌈(55-5.5)+5!⌉',\n", - " 171: '⌈(55.5+5!)-5⌉',\n", - " 172: '⌈5.555^⌈√5⌉⌉',\n", - " 173: '⌊√⌊5.5+5⌋*55⌋',\n", - " 174: '⌊(55-.55)+5!⌋',\n", - " 175: '⌊55.55+5!⌋',\n", - " 176: '⌈55.55+5!⌉',\n", - " 177: '⌊(55*√5)+55⌋',\n", - " 178: '⌈(55*√5)+55⌉',\n", - " 179: '⌈√(5.5+5)*55⌉',\n", - " 180: '⌊(55.5+5)+5!⌋',\n", - " 181: '⌈(55.5+5)+5!⌉',\n", - " 182: '⌊(55*5.5)-5!⌋',\n", - " 183: '⌈(55*5.5)-5!⌉',\n", - " 184: '⌊(5!-55.5)+5!⌋',\n", - " 185: '(555/⌈5*.5⌉)',\n", - " 186: '⌊(.555*5!)+5!⌋',\n", - " 187: '⌈(.555*5!)+5!⌉',\n", - " 188: '((⌈5*.5⌉^5)-55)',\n", - " 189: '⌊(55+5)*√(5+5)⌋',\n", - " 190: '(⌈√55.5*5⌉*5)',\n", - " 191: '⌈√⌊√55+5⌋*55⌉',\n", - " 192: '⌈⌈√55.5^5⌉/5!⌉',\n", - " 193: '⌊5!^(.55+.55)⌋',\n", - " 194: '⌊(555-5!)/√5⌋',\n", - " 195: '⌈(555-5!)/√5⌉',\n", - " 196: '⌊555/√⌈5+√5⌉⌋',\n", - " 197: '⌊((55*5)+5!)*.5⌋',\n", - " 198: '(⌊55/.55⌋/.5)',\n", - " 199: '⌊55/(.55*.5)⌋',\n", - " 200: '⌊555/(5-√5)⌋',\n", - " 201: '⌈555/(5-√5)⌉',\n", - " 202: '⌊555/(√5+.5)⌋',\n", - " 203: '⌊5!^(5.55/5)⌋',\n", - " 204: '⌈5!^(5.55/5)⌉',\n", - " 205: '(⌈√55*5.5⌉*5)',\n", - " 206: '⌈5.55*⌈5^√5⌉⌉',\n", - " 207: '⌊(5!-5)/.555⌋',\n", - " 208: '⌈(5!-5)/.555⌉',\n", - " 209: '(⌈√55*5⌉*5.5)',\n", - " 210: '((55*⌈5.5⌉)-5!)',\n", - " 211: '⌊(5!/.555)-5⌋',\n", - " 212: '⌈(5!/.555)-5⌉',\n", - " 213: '⌈(5!/.55)-5.5⌉',\n", - " 214: '⌊⌊5!-.5⌋/.555⌋',\n", - " 215: '((55*⌊5-.5⌋)-5)',\n", - " 216: '⌊5!/.5555⌋',\n", - " 217: '⌈5!/.5555⌉',\n", - " 218: '⌈(555-5!)*.5⌉',\n", - " 219: '(⌊55/.55⌋+5!)',\n", - " 220: '((55*5)-55)',\n", - " 221: '⌊(5!/.555)+5⌋',\n", - " 222: '(555/(5*.5))',\n", - " 223: '⌊⌈55*√5⌉/.55⌋',\n", - " 224: '⌈⌈55*√5⌉/.55⌉',\n", - " 225: '((55-(5+5))*5)',\n", - " 226: '⌈(5!+5)/.555⌉',\n", - " 227: '⌈555/√⌈5.5⌉⌉',\n", - " 228: '⌊(5.5+5!)/.55⌋',\n", - " 229: '⌊⌈5.5+5!⌉/.55⌋',\n", - " 230: '((55+55)+5!)',\n", - " 231: '((555/5)+5!)',\n", - " 232: '⌈(√55+5!)/.55⌉',\n", - " 233: '⌈⌈√55+5!⌉/.55⌉',\n", - " 234: '⌊(5!-5.55)+5!⌋',\n", - " 235: '(⌊55-√55⌋*5)',\n", - " 236: '⌊555/√5.5⌋',\n", - " 237: '⌈555/√5.5⌉',\n", - " 238: '⌈(55-√55)*5⌉',\n", - " 239: '⌊(5!-.555)+5!⌋',\n", - " 240: '(⌊5.555⌋!+5!)',\n", - " 241: '⌈(.555+5!)+5!⌉',\n", - " 242: '⌈(.555+5!)/.5⌉',\n", - " 243: '(⌈.555*5⌉^5)',\n", - " 244: '⌊(5-.55)*55⌋',\n", - " 245: '⌈(5-.55)*55⌉',\n", - " 246: '⌈(55+55)*√5⌉',\n", - " 247: '⌊(55-5.5)*5⌋',\n", - " 248: '⌈(55-5.5)*5⌉',\n", - " 249: '⌈555.5/√5⌉',\n", - " 250: '(⌈55-5.5⌉*5)',\n", - " 251: '⌈(555+5)/√5⌉',\n", - " 252: '⌊(55.5-5)*5⌋',\n", - " 253: '⌈(55.5-5)*5⌉',\n", - " 254: '⌈(555/√5)+5⌉',\n", - " 255: '(⌈55.5-5⌉*5)',\n", - " 256: '⌊((5!/55)*5!)-5⌋',\n", - " 257: '⌈((5!/55)*5!)-5⌉',\n", - " 258: '⌊⌈55.5/5⌉^√5⌋',\n", - " 259: '⌊(5!/55.5)*5!⌋',\n", - " 260: '⌈(5!/55.5)*5!⌉',\n", - " 261: '⌈(.55*5)^5.5⌉',\n", - " 262: '⌊((5.5+5)*5)*5⌋',\n", - " 263: '⌈((5.5+5)*5)*5⌉',\n", - " 264: '((55/(5*5))*5!)',\n", - " 265: '((55*5)-(5+5))',\n", - " 266: '⌊(55.5-√5)*5⌋',\n", - " 267: '⌊(55*5)-√55⌋',\n", - " 268: '⌈(55*5)-√55⌉',\n", - " 269: '⌊(55*5)-5.5⌋',\n", - " 270: '⌈(55*5)-5.5⌉',\n", - " 271: '⌈((55*5)+.5)-5⌉',\n", - " 272: '⌊(555*.5)-5⌋',\n", - " 273: '⌈(555*.5)-5⌉',\n", - " 274: '⌊(55*5)-.55⌋',\n", - " 275: '(⌊55.55⌋*5)',\n", - " 276: '⌈(55*5)+.55⌉',\n", - " 277: '⌊55.55*5⌋',\n", - " 278: '⌈55.55*5⌉',\n", - " 279: '⌈⌈555*.5⌉+.5⌉',\n", - " 280: '(⌈55.55⌉*5)',\n", - " 281: '⌈(55*5)+5.5⌉',\n", - " 282: '⌊(555*.5)+5⌋',\n", - " 283: '⌈(555*.5)+5⌉',\n", - " 284: '⌊((5^5)*5)/55⌋',\n", - " 285: '(((55*5)+5)+5)',\n", - " 286: '⌊555-(5!*√5)⌋',\n", - " 287: '⌊(√55*55)-5!⌋',\n", - " 288: '⌈(√55*55)-5!⌉',\n", - " 289: '⌊55^√⌊5!/55⌋⌋',\n", - " 290: '⌈55^√⌊5!/55⌋⌉',\n", - " 291: '⌊√⌈55*.5⌉*55⌋',\n", - " 292: '⌈√⌈55*.5⌉*55⌉',\n", - " 293: '⌊√⌈5.555⌉*5!⌋',\n", - " 294: '⌈√⌈5.555⌉*5!⌉',\n", - " 295: '(((55+5)*5)-5)',\n", - " 296: '⌈(55.5+5!)+5!⌉',\n", - " 297: '⌊(55*5.5)-5⌋',\n", - " 298: '⌈(55*5.5)-5⌉',\n", - " 299: '⌊(55-.5)*5.5⌋',\n", - " 300: '(⌊55.5+5⌋*5)',\n", - " 301: '⌊⌊55*5.5⌋-.5⌋',\n", - " 302: '⌊(55.5+5)*5⌋',\n", - " 303: '⌈(55.5+5)*5⌉',\n", - " 304: '⌈⌈55*5.5⌉+.5⌉',\n", - " 305: '⌊5.55*55⌋',\n", - " 306: '⌈5.55*55⌉',\n", - " 307: '⌊(55*5.5)+5⌋',\n", - " 308: '⌈(55*5.5)+5⌉',\n", - " 309: '⌊((5!-55)/5)^√5⌋',\n", - " 310: '(⌊√55+55⌋*5)',\n", - " 311: '⌈5.55*(√5^5)⌉',\n", - " 312: '⌊(√55+55)*5⌋',\n", - " 313: '⌈(√55+55)*5⌉',\n", - " 314: '⌊(55+√5)*5.5⌋',\n", - " 315: '(555-(5!+5!))',\n", - " 316: '⌊√⌊5.55+5⌋^5⌋',\n", - " 317: '⌈√⌊5.55+5⌋^5⌉',\n", - " 318: '⌊(55+5!)/.55⌋',\n", - " 319: '⌈(55+5!)/.55⌉',\n", - " 320: '(⌊5!-55.5⌋*5)',\n", - " 321: '⌈555/√⌈5*.5⌉⌉',\n", - " 322: '⌊(5!-55.5)*5⌋',\n", - " 323: '⌈(5!-55.5)*5⌉',\n", - " 324: '⌈55.5+(5!*√5)⌉',\n", - " 325: '(((55+5)+5)*5)',\n", - " 326: '⌈(5!/55)^√55⌉',\n", - " 327: '⌊(55+(5!*5))*.5⌋',\n", - " 328: '⌈(55+(5!*5))*.5⌉',\n", - " 329: '⌊(55*⌈5.5⌉)-.5⌋',\n", - " 330: '((55*5)+55)',\n", - " 331: '⌈(55+(5*√5))*5⌉',\n", - " 332: '⌊((5!*5)+5)*.55⌋',\n", - " 333: '⌊(.555*5)*5!⌋',\n", - " 334: '⌈(.555*5)*5!⌉',\n", - " 335: '(⌈.555*5!⌉*5)',\n", - " 336: '⌊(5!/.555)+5!⌋',\n", - " 337: '⌊(555+5!)*.5⌋',\n", - " 338: '⌈(555+5!)*.5⌉',\n", - " 339: '⌈(5-(5!/55))*5!⌉',\n", - " 340: '⌈√⌈√55*5⌉*55⌉',\n", - " 341: '⌊(5.5+√.5)*55⌋',\n", - " 342: '⌈(5.5+√.5)*55⌉',\n", - " 343: '⌊((5^5)*.55)/5⌋',\n", - " 344: '⌈((5^5)*.55)/5⌉',\n", - " 345: '(⌈5!/55⌉*(5!-5))',\n", - " 346: '⌊((55*5)-5!)*√5⌋',\n", - " 347: '⌊(5^5)/⌊5/.55⌋⌋',\n", - " 348: '⌈(5^5)/⌊5/.55⌋⌉',\n", - " 349: '⌊((5-.55)^5)/5⌋',\n", - " 350: '(((5-55)+5!)*5)',\n", - " 351: '⌊555/√(5*.5)⌋',\n", - " 352: '⌈555/√(5*.5)⌉',\n", - " 353: '⌈(√55-⌈.5⌉)*55⌉',\n", - " 354: '⌊(⌈5/55⌉+√5)^5⌋',\n", - " 355: '((⌈5!/55⌉*5!)-5)',\n", - " 356: '⌈(55/5)^√⌈5.5⌉⌉',\n", - " 357: '⌊(5!-55)*5.5⌋',\n", - " 358: '⌈(5!-55)*5.5⌉',\n", - " 359: '⌈⌈5!/55⌉*(5!-.5)⌉',\n", - " 360: '(⌈.555*5⌉*5!)',\n", - " 361: '⌊(55-5)*(5+√5)⌋',\n", - " 362: '⌈(55-5)*(5+√5)⌉',\n", - " 363: '⌊(5.5*.55)*5!⌋',\n", - " 364: '⌈(5.5*.55)*5!⌉',\n", - " 365: '((⌈5!/55⌉*5!)+5)',\n", - " 366: '⌈(⌈√5⌉^5.5)-55⌉',\n", - " 367: '⌊(55*(5-.5))+5!⌋',\n", - " 368: '⌊(555/√5)+5!⌋',\n", - " 369: '⌈(555/√5)+5!⌉',\n", - " 370: '⌊(55-5)*√55⌋',\n", - " 371: '⌈(55-5)*√55⌉',\n", - " 372: '⌈(√(5+5)^5)+55⌉',\n", - " 373: '⌈(5-√⌈5!/55⌉)^5⌉',\n", - " 374: '⌈√⌈5.5^√5⌉*55⌉',\n", - " 375: '(⌈5!/55⌉*(5!+5))',\n", - " 376: '⌊(5.55+5!)*⌈√5⌉⌋',\n", - " 377: '⌈(5.55+5!)*⌈√5⌉⌉',\n", - " 378: '(⌊55-.5⌋*⌊√55⌋)',\n", - " 379: '⌊√⌊5.55+5⌋*5!⌋',\n", - " 380: '⌊(√55-.5)*55⌋',\n", - " 381: '⌊55^(√55/5)⌋',\n", - " 382: '⌈55^(√55/5)⌉',\n", - " 383: '⌊((55-√5)*5)+5!⌋',\n", - " 384: '⌈((55-√5)*5)+5!⌉',\n", - " 385: '(⌊√55.5⌋*55)',\n", - " 386: '⌈⌊55-√5⌋*√55⌉',\n", - " 387: '⌊(555*√.5)-5⌋',\n", - " 388: '⌊√(55-5)*55⌋',\n", - " 389: '⌈√(55-5)*55⌉',\n", - " 390: '(((55*5)+5!)-5)',\n", - " 391: '⌊⌊55.5+5!⌋*√5⌋',\n", - " 392: '⌊555.5*√.5⌋',\n", - " 393: '⌈555.5*√.5⌉',\n", - " 394: '⌈⌈555.5⌉*√.5⌉',\n", - " 395: '((55+(5!/5))*5)',\n", - " 396: '⌊⌈5.55⌉!*.55⌋',\n", - " 397: '⌊(555*.5)+5!⌋',\n", - " 398: '⌈(555*.5)+5!⌉',\n", - " 399: '⌊.555*⌈5.5⌉!⌋',\n", - " 400: '((55+(5*5))*5)',\n", - " 401: '⌊55.5*(5+√5)⌋',\n", - " 402: '⌊(√55*55)-5⌋',\n", - " 403: '⌈(√55*55)-5⌉',\n", - " 404: '⌈(√5.5+5)*55⌉',\n", - " 405: '⌈(55-.5)*√55⌉',\n", - " 406: '⌊√(55-.5)*55⌋',\n", - " 407: '⌊(√55^5)/55⌋',\n", - " 408: '⌈(√55^5)/55⌉',\n", - " 409: '⌊√55.5*55⌋',\n", - " 410: '⌈√55.5*55⌉',\n", - " 411: '⌊55.5*√55⌋',\n", - " 412: '⌈55.5*√55⌉',\n", - " 413: '⌈(√55*55)+5⌉',\n", - " 414: '⌊(5!-(√55*5))*5⌋',\n", - " 415: '⌊⌈55.5⌉*√55⌋',\n", - " 416: '⌈⌈55.5⌉*√55⌉',\n", - " 417: '⌊(5^(.55*5))*5⌋',\n", - " 418: '⌈(5^(.55*5))*5⌉',\n", - " 419: '⌊(5^5)/√55.5⌋',\n", - " 420: '⌊⌈5!/55⌉^5.5⌋',\n", - " 421: '⌈⌈5!/55⌉^5.5⌉',\n", - " 422: '⌊(55*5.5)+5!⌋',\n", - " 423: '⌈(55*5.5)+5!⌉',\n", - " 424: '⌊(55+√5)*√55⌋',\n", - " 425: '⌊(5.5+√5)*55⌋',\n", - " 426: '⌊√(55+5)*55⌋',\n", - " 427: '⌈√(55+5)*55⌉',\n", - " 428: '⌈((5-√5)+5)*55⌉',\n", - " 429: '⌈⌈55*5.5⌉/√.5⌉',\n", - " 430: '(555-(5!+5))',\n", - " 431: '⌈⌈55+√5⌉*√55⌉',\n", - " 432: '⌊555-(5!+√5)⌋',\n", - " 433: '⌈555-(5!+√5)⌉',\n", - " 434: '⌊555-(5!+.5)⌋',\n", - " 435: '⌊555.5-5!⌋',\n", - " 436: '⌈555.5-5!⌉',\n", - " 437: '⌊(555+√5)-5!⌋',\n", - " 438: '⌈(555+√5)-5!⌉',\n", - " 439: '⌊(55*⌈√55⌉)-.5⌋',\n", - " 440: '((555-5!)+5)',\n", - " 441: '⌊(5^5)/√(55-5)⌋',\n", - " 442: '⌈(5^5)/√(55-5)⌉',\n", - " 443: '⌊√(5!-55)*55⌋',\n", - " 444: '⌊(55+5)*√55⌋',\n", - " 445: '⌈(55+5)*√55⌉',\n", - " 446: '⌊√(.55*5!)*55⌋',\n", - " 447: '⌊⌈√5⌉^5.555⌋',\n", - " 448: '⌈⌈√5⌉^5.555⌉',\n", - " 449: '⌈(√(5+5)+5)*55⌉',\n", - " 450: '((55*⌈5.5⌉)+5!)',\n", - " 451: '⌊(⌈5.55⌉-√5)*5!⌋',\n", - " 452: '⌊(⌈5.5⌉+√5)*55⌋',\n", - " 453: '⌈(⌈5.5⌉+√5)*55⌉',\n", - " 454: '⌊(⌈5!/√55⌉/5)^5⌋',\n", - " 455: '((5!-55)*⌊√55⌋)',\n", - " 456: '⌊⌊√5^5.5⌋*5.5⌋',\n", - " 457: '⌊(55+(5^√5))*5⌋',\n", - " 458: '⌈(55+(5^√5))*5⌉',\n", - " 459: '⌊555/(√.5+.5)⌋',\n", - " 460: '(⌊5!-(55*.5)⌋*5)',\n", - " 461: '⌈√⌊√5.5^5⌋*55⌉',\n", - " 462: '⌊(5!-(55*.5))*5⌋',\n", - " 463: '⌈(5!-(55*.5))*5⌉',\n", - " 464: '(⌈55+√5⌉*⌈√55⌉)',\n", - " 465: '(⌈5!-(55*.5)⌉*5)',\n", - " 466: '⌊5.55*⌊5!*√.5⌋⌋',\n", - " 467: '⌊(⌈√55⌉+.5)*55⌋',\n", - " 468: '((5-(5.5/5))*5!)',\n", - " 469: '⌊((√.5*5)+5)*55⌋',\n", - " 470: '⌊555-(5!*√.5)⌋',\n", - " 471: '⌈555-(5!*√.5)⌉',\n", - " 472: '⌈5.55*⌈5!*√.5⌉⌉',\n", - " 473: '⌈(5!*5)-(√55+5!)⌉',\n", - " 474: '⌊(5!*5)-(5.5+5!)⌋',\n", - " 475: '((⌊5-.55⌋*5!)-5)',\n", - " 476: '⌊5!/((.55^5)*5)⌋',\n", - " 477: '⌊(555+5!)*√.5⌋',\n", - " 478: '⌈(555+5!)*√.5⌉',\n", - " 479: '⌊(5!*5)-(.55+5!)⌋',\n", - " 480: '(⌊555/5!⌋*5!)',\n", - " 481: '⌈(.55-5!)+(5!*5)⌉',\n", - " 482: '⌊(5!-55)*√55⌋',\n", - " 483: '⌈(5!-55)*√55⌉',\n", - " 484: '⌈(5!*√5)/.555⌉',\n", - " 485: '((⌊5-.55⌋*5!)+5)',\n", - " 486: '⌈⌈5!/.555⌉*√5⌉',\n", - " 487: '⌊((5!*5)-5!)+√55⌋',\n", - " 488: '⌊⌊√55*5!⌋*.55⌋',\n", - " 489: '⌊(√55*.55)*5!⌋',\n", - " 490: '⌈(√55*.55)*5!⌉',\n", - " 491: '⌈5!*(5-(5/5.5))⌉',\n", - " 492: '⌊((55/5)+5)^√5⌋',\n", - " 493: '⌈((55/5)+5)^√5⌉',\n", - " 494: '⌊((55*√5)*5)-5!⌋',\n", - " 495: '(⌊55/.55⌋*5)',\n", - " 496: '⌊555/(√5*.5)⌋',\n", - " 497: '⌈555/(√5*.5)⌉',\n", - " 498: '(⌈555/√5⌉/.5)',\n", - " 499: '⌊(55/.55)*5⌋',\n", - " 500: '(555-55)',\n", - " 501: '⌊555-(5!/√5)⌋',\n", - " 502: '⌈555-(5!/√5)⌉',\n", - " 503: '⌊⌈5.5^5⌉/(5+5)⌋',\n", - " 504: '⌈⌈5.5^5⌉/(5+5)⌉',\n", - " 505: '((55*⌊√55⌋)+5!)',\n", - " 506: '((((5^5)+5)/5)-5!)',\n", - " 507: '⌊((5.5+5!)*5)-5!⌋',\n", - " 508: '⌈((5.5+5!)*5)-5!⌉',\n", - " 509: '⌊(√55^5.5)/5!⌋',\n", - " 510: '⌈(√55^5.5)/5!⌉',\n", - " 511: '⌊(5-.55)*(5!-5)⌋',\n", - " 512: '⌊(555*√.5)+5!⌋',\n", - " 513: '⌈(555*√.5)+5!⌉',\n", - " 514: '⌈(5!/55)^⌈√55⌉⌉',\n", - " 515: '(((55*5)+5!)+5!)',\n", - " 516: '⌈⌊√5⌋^(5/.555)⌉',\n", - " 517: '⌊⌈(5/.55)^5⌉/5!⌋',\n", - " 518: '⌊555-(5^√5)⌋',\n", - " 519: '⌈555-(5^√5)⌉',\n", - " 520: '(((5!-5)*5)-55)',\n", - " 521: '⌈(5^5)/⌈5.55⌉⌉',\n", - " 522: '⌊((5+5)-.5)*55⌋',\n", - " 523: '⌈((5+5)-.5)*55⌉',\n", - " 524: '⌈((5!+5!)/55)*5!⌉',\n", - " 525: '(((55/.5)-5)*5)',\n", - " 526: '⌈(⌈5!/55⌉+.5)^5⌉',\n", - " 527: '⌊(√55*55)+5!⌋',\n", - " 528: '⌈(√55*55)+5!⌉',\n", - " 529: '(((5-.55)*5!)-5)',\n", - " 530: '(555-(5*5))',\n", - " 531: '(555-(5!/5))',\n", - " 532: '⌊5^(5-(5.5/5))⌋',\n", - " 533: '⌊(5-.555)*5!⌋',\n", - " 534: '⌈(5-.555)*5!⌉',\n", - " 535: '((55+(5!*5))-5!)',\n", - " 536: '⌊√(5!-(5*5))*55⌋',\n", - " 537: '⌊(5-(√55/5))^5⌋',\n", - " 538: '⌈(5-(√55/5))^5⌉',\n", - " 539: '(((5-.55)*5!)+5)',\n", - " 540: '((5!*5)-(55+5))',\n", - " 541: '⌊((5^5)/√55)+5!⌋',\n", - " 542: '⌊((5!-.5)*5)-55⌋',\n", - " 543: '⌊555-(5*√5)⌋',\n", - " 544: '⌈555-(5*√5)⌉',\n", - " 545: '(555-(5+5))',\n", - " 546: '((5.55*5!)-5!)',\n", - " 547: '⌊555-√55⌋',\n", - " 548: '⌈555-√55⌉',\n", - " 549: '⌊555-5.5⌋',\n", - " 550: '⌊555.5-5⌋',\n", - " 551: '⌈555.5-5⌉',\n", - " 552: '⌊555-√5.5⌋',\n", - " 553: '⌈555-√5.5⌉',\n", - " 554: '⌊555-.55⌋',\n", - " 555: '⌊555.55⌋',\n", - " 556: '⌈555.55⌉',\n", - " 557: '⌊555+√5.5⌋',\n", - " 558: '⌈555+√5.5⌉',\n", - " 559: '⌊(555+5)-.5⌋',\n", - " 560: '⌊555.5+5⌋',\n", - " 561: '⌈555.5+5⌉',\n", - " 562: '⌊555+√55⌋',\n", - " 563: '⌈555+√55⌉',\n", - " 564: '⌈(5^5)/5.55⌉',\n", - " 565: '((555+5)+5)',\n", - " 566: '⌊555+(5*√5)⌋',\n", - " 567: '⌈555+(5*√5)⌉',\n", - " 568: '⌈((5^5)-5)/5.5⌉',\n", - " 569: '⌊((5^5)+5)/5.5⌋',\n", - " 570: '(⌊5!-5.55⌋*5)',\n", - " 571: '⌈(⌊5!-5.5⌋*5)+.5⌉',\n", - " 572: '⌊(5!-5.55)*5⌋',\n", - " 573: '⌈(5!-5.55)*5⌉',\n", - " 574: '⌈((5^5)/5.5)+5⌉',\n", - " 575: '(⌈5!-5.55⌉*5)',\n", - " 576: '((5!/5)^⌊5!/55⌋)',\n", - " 577: '⌊(5.5+5)*55⌋',\n", - " 578: '⌈(5.5+5)*55⌉',\n", - " 579: '(555+(5!/5))',\n", - " 580: '(555+(5*5))',\n", - " 581: '⌈((5!-5)*5)+5.5⌉',\n", - " 582: '⌊((5!-5)*5)+√55⌋',\n", - " 583: '⌈((5!-5)*5)+√55⌉',\n", - " 584: '⌈(5!-√(55/5))*5⌉',\n", - " 585: '(⌊5!-(5!/55)⌋*5)',\n", - " 586: '⌊(5!-(.55*5))*5⌋',\n", - " 587: '(555+(⌊√5⌋^5))',\n", - " 588: '⌈(5!*5)-(√55+5)⌉',\n", - " 589: '⌊5!*(5-(5/55))⌋',\n", - " 590: '⌈5!*(5-(5/55))⌉',\n", - " 591: '⌊555+(5^√5)⌋',\n", - " 592: '⌈555+(5^√5)⌉',\n", - " 593: '⌈(5!*5)-√55.5⌉',\n", - " 594: '⌊(5!*5)-5.55⌋',\n", - " 595: '⌈(5!*5)-5.55⌉',\n", - " 596: '⌈√(5+5)^5.55⌉',\n", - " 597: '⌊(5!-.555)*5⌋',\n", - " 598: '⌈(5!-.555)*5⌉',\n", - " 599: '⌊(5!*5)-.555⌋',\n", - " 600: '(⌊5.555⌋!*5)',\n", - " 601: '⌈.555+(5!*5)⌉',\n", - " 602: '⌊(.555+5!)*5⌋',\n", - " 603: '⌈(.555+5!)*5⌉',\n", - " 604: '(⌊55*5.5⌋/.5)',\n", - " 605: '((55*55)/5)',\n", - " 606: '⌈5.55+(5!*5)⌉',\n", - " 607: '⌊√55.5+(5!*5)⌋',\n", - " 608: '⌊555+(5!/√5)⌋',\n", - " 609: '⌈555+(5!/√5)⌉',\n", - " 610: '(555+55)',\n", - " 611: '⌈555+(√5^5)⌉',\n", - " 612: '⌊(√55+(5!*5))+5⌋',\n", - " 613: '⌊((.55*5)+5!)*5⌋',\n", - " 614: '(((5^5)-55)/5)',\n", - " 615: '(555+(5!*.5))',\n", - " 616: '(⌊5!-√55⌋*5.5)',\n", - " 617: '⌊((5^5)/5)-√55⌋',\n", - " 618: '⌈((5^5)/5)-√55⌉',\n", - " 619: '⌊⌊555*.5⌋*√5⌋',\n", - " 620: '⌊(55.5*√5)*5⌋',\n", - " 621: '⌈(55.5*√5)*5⌉',\n", - " 622: '⌈⌈555*.5⌉*√5⌉',\n", - " 623: '⌊((5^5)-5.5)/5⌋',\n", - " 624: '⌊((5^5)-.55)/5⌋',\n", - " 625: '(5^⌊555/5!⌋)',\n", - " 626: '⌈((5^5)/5)+.55⌉',\n", - " 627: '⌊(5.55+5!)*5⌋',\n", - " 628: '⌈(5.55+5!)*5⌉',\n", - " 629: '⌊(5!-5.5)*5.5⌋',\n", - " 630: '(⌈5.55+5!⌉*5)',\n", - " 631: '⌈((5^5)/5)+5.5⌉',\n", - " 632: '⌊⌈5!-5.5⌉*5.5⌋',\n", - " 633: '⌈⌈5!-5.5⌉*5.5⌉',\n", - " 634: '⌊√⌈5.55*5⌉*5!⌋',\n", - " 635: '⌈√⌈5.55*5⌉*5!⌉',\n", - " 636: '((55+(5^5))/5)',\n", - " 637: '⌊(√55.5+5!)*5⌋',\n", - " 638: '⌊5.55*(5!-5)⌋',\n", - " 639: '⌈5.55*(5!-5)⌉',\n", - " 640: '(⌊55*√5.5⌋*5)',\n", - " 641: '⌈(√(5!-55)+5!)*5⌉',\n", - " 642: '⌊((√55+5!)*5)+5⌋',\n", - " 643: '⌈((√55+5!)*5)+5⌉',\n", - " 644: '⌊(55*√5.5)*5⌋',\n", - " 645: '⌈(55*√5.5)*5⌉',\n", - " 646: '⌈((5/.55)+5!)*5⌉',\n", - " 647: '⌊(5!-√5.5)*5.5⌋',\n", - " 648: '((⌊55*.5⌋/5)*5!)',\n", - " 649: '⌊5.55*⌊5!-√5⌋⌋',\n", - " 650: '((55+(5!*5))-5)',\n", - " 651: '⌊(((5^5)*5)*5)/5!⌋',\n", - " 652: '⌊((5.5+5!)+5)*5⌋',\n", - " 653: '⌈((5.5+5!)+5)*5⌉',\n", - " 654: '⌊⌊5!-.55⌋*5.5⌋',\n", - " 655: '⌊55.5+(5!*5)⌋',\n", - " 656: '⌈55.5+(5!*5)⌉',\n", - " 657: '⌈(5!-.55)*5.5⌉',\n", - " 658: '⌈√⌊55*.55⌋*5!⌉',\n", - " 659: '⌊(5.5*5!)-.55⌋',\n", - " 660: '(⌊√55+5⌋*55)',\n", - " 661: '((5.55*5!)-5)',\n", - " 662: '⌊((√55+5)+5!)*5⌋',\n", - " 663: '⌊(.55+5!)*5.5⌋',\n", - " 664: '⌊⌈5.5⌉!-55.5⌋',\n", - " 665: '(⌈5.55⌉!-55)',\n", - " 666: '⌊5.555*5!⌋',\n", - " 667: '⌈5.555*5!⌉',\n", - " 668: '⌊5.55*(5!+.5)⌋',\n", - " 669: '⌈5.55*(5!+.5)⌉',\n", - " 670: '((555+5!)-5)',\n", - " 671: '((5.55*5!)+5)',\n", - " 672: '⌊(555-√5)+5!⌋',\n", - " 673: '⌈(555-√5)+5!⌉',\n", - " 674: '⌊(555+5!)-.5⌋',\n", - " 675: '⌊555.5+5!⌋',\n", - " 676: '⌈555.5+5!⌉',\n", - " 677: '⌊(555+√5)+5!⌋',\n", - " 678: '⌈(555+√5)+5!⌉',\n", - " 679: '⌈⌊5.5^5⌋/√55⌉',\n", - " 680: '((555+5!)+5)',\n", - " 681: '⌈((5/√55)+5)*5!⌉',\n", - " 682: '⌊(√55+5)*55⌋',\n", - " 683: '⌈(√55+5)*55⌉',\n", - " 684: '(⌊5!-5.5⌋*⌈5.5⌉)',\n", - " 685: '(⌊(55*5)*.5⌋*5)',\n", - " 686: '⌊555*(√5-⌈.5⌉)⌋',\n", - " 687: '⌊((55*5)*5)*.5⌋',\n", - " 688: '⌈((55*5)*5)*.5⌉',\n", - " 689: '⌈((5^5)/5.5)+5!⌉',\n", - " 690: '⌊(5.5+5!)*5.5⌋',\n", - " 691: '⌈(5.5+5!)*5.5⌉',\n", - " 692: '⌊((5!+5)*5.5)+5⌋',\n", - " 693: '⌊5.55*(5!+5)⌋',\n", - " 694: '⌈5.55*(5!+5)⌉',\n", - " 695: '(⌈5.55⌉!-(5*5))',\n", - " 696: '(⌈5.55⌉!-(5!/5))',\n", - " 697: '⌈((√55*.5)^5)-5⌉',\n", - " 698: '⌊⌈5^5.5⌉/(5+5)⌋',\n", - " 699: '⌈⌈5^5.5⌉/(5+5)⌉',\n", - " 700: '((⌈55*.5⌉*5)*5)',\n", - " 701: '⌈(√55+5!)*5.5⌉',\n", - " 702: '⌊(5^5)/(5-.55)⌋',\n", - " 703: '⌈(5^5)/(5-.55)⌉',\n", - " 704: '(⌈√55+5!⌉*5.5)',\n", - " 705: '⌈(5^(5.5/5))*5!⌉',\n", - " 706: '⌊⌈5.55⌉*(5!-√5)⌋',\n", - " 707: '⌈⌈5.55⌉*(5!-√5)⌉',\n", - " 708: '⌊⌈5.55⌉!-(5*√5)⌋',\n", - " 709: '(⌈5.5⌉!-(55/5))',\n", - " 710: '(⌈5.55⌉!-(5+5))',\n", - " 711: '⌈⌈5.5⌉!-(5/.55)⌉',\n", - " 712: '⌊⌈5.55⌉!-√55⌋',\n", - " 713: '⌈⌈5.55⌉!-√55⌉',\n", - " 714: '⌊⌈5.5⌉!-5.55⌋',\n", - " 715: '(⌈5.555⌉!-5)',\n", - " 716: '⌊(5!-.55)*⌈5.5⌉⌋',\n", - " 717: '⌊⌈5.555⌉!-√5⌋',\n", - " 718: '⌈⌈5.555⌉!-√5⌉',\n", - " 719: '⌊⌈5.5⌉!-.555⌋',\n", - " 720: '⌈5.5555⌉!',\n", - " 721: '⌈⌈5.55⌉!+.55⌉',\n", - " 722: '⌊⌈5.555⌉!+√5⌋',\n", - " 723: '⌈⌈5.555⌉!+√5⌉',\n", - " 724: '⌈5^((5/.55)-5)⌉',\n", - " 725: '(⌈5.555⌉!+5)',\n", - " 726: '⌈5.55+⌈5.5⌉!⌉',\n", - " 727: '⌊⌈5.55⌉!+√55⌋',\n", - " 728: '⌈⌈5.55⌉!+√55⌉',\n", - " 729: '(⌈5!/55⌉^⌈5.5⌉)',\n", - " 730: '((⌈5.55⌉!+5)+5)',\n", - " 731: '((55/5)+⌈5.5⌉!)',\n", - " 732: '(((5.5/5)+5)*5!)',\n", - " 733: '⌈(√55+⌈5.5⌉!)+5⌉',\n", - " 734: '⌊((55*√5)*5)+5!⌋',\n", - " 735: '(⌊(55*.5)+5!⌋*5)',\n", - " 736: '⌊√5.5^√(55+5)⌋',\n", - " 737: '⌊((55*.5)+5!)*5⌋',\n", - " 738: '⌈((55*.5)+5!)*5⌉',\n", - " 739: '⌈⌈(5!*5)*√5⌉*.55⌉',\n", - " 740: '(⌈(55*.5)+5!⌉*5)',\n", - " 741: '⌊(5!/5.5)+⌈5.5⌉!⌋',\n", - " 742: '⌈(5!/5.5)+⌈5.5⌉!⌉',\n", - " 743: '⌊⌈5!^√5⌉/(55+5)⌋',\n", - " 744: '(⌈5.55⌉!+(5!/5))',\n", - " 745: '(⌈5.55⌉!+(5*5))',\n", - " 746: '((((5^5)+5)/5)+5!)',\n", - " 747: '⌊((5.5+5!)*5)+5!⌋',\n", - " 748: '⌈((5.5+5!)*5)+5!⌉',\n", - " 749: '⌈(√55^(5*.5))*5⌉',\n", - " 750: '(⌈5.55⌉*(5!+5))',\n", - " 751: '⌊(5/.55)^⌈5*.5⌉⌋',\n", - " 752: '⌊((5!-5)*5.5)+5!⌋',\n", - " 753: '((5.5+5!)*⌈5.5⌉)',\n", - " 754: '⌊(√⌈55*.5⌉*5!)+5!⌋',\n", - " 755: '(((55+5!)*5)-5!)',\n", - " 756: '⌈(55/5)^(5-√5)⌉',\n", - " 757: '⌈(55*.5)^⌊5*.5⌋⌉',\n", - " 758: '⌈(√55*5)+⌈5.5⌉!⌉',\n", - " 759: '⌈√⌊√55*5.5⌋*5!⌉',\n", - " 760: '⌊((5-√5)*55)*5⌋',\n", - " 761: '⌈((5-√5)*55)*5⌉',\n", - " 762: '(⌊√55+5!⌋*⌈5.5⌉)',\n", - " 763: '⌈(5!^(.55+.5))*5⌉',\n", - " 764: '⌊(√55+5!)*⌈5.5⌉⌋',\n", - " 765: '(⌈(5-√5)*55⌉*5)',\n", - " 766: '⌊((5*5)-5.5)^√5⌋',\n", - " 767: '⌈((5*5)-5.5)^√5⌉',\n", - " 768: '⌊√⌈5.55⌉^√55⌋',\n", - " 769: '⌊55^√(.55*5)⌋',\n", - " 770: '((55+⌈5.5⌉!)-5)',\n", - " 771: '⌈(5.55*5)^⌊√5⌋⌉',\n", - " 772: '⌊((5*.5)^5.5)*5⌋',\n", - " 773: '⌊(√55.5*5!)-5!⌋',\n", - " 774: '⌈(√55.5*5!)-5!⌉',\n", - " 775: '(⌈5.55⌉!+55)',\n", - " 776: '⌈55.5+⌈5.5⌉!⌉',\n", - " 777: '⌊(555-5)/√.5⌋',\n", - " 778: '⌈(555-5)/√.5⌉',\n", - " 779: '⌊(555/√.5)-5⌋',\n", - " 780: '((55+⌈5.5⌉!)+5)',\n", - " 781: '⌊(5^5)/⌊5-.55⌋⌋',\n", - " 782: '⌈(5^5)/⌊5-.55⌋⌉',\n", - " 783: '⌊⌊555-.5⌋/√.5⌋',\n", - " 784: '⌊555*√⌊5*.5⌋⌋',\n", - " 785: '⌊555.5/√.5⌋',\n", - " 786: '((5.55*5!)+5!)',\n", - " 787: '⌈((.55*5)^5)*5⌉',\n", - " 788: '⌈(55+5!)*(5-.5)⌉',\n", - " 789: '⌊(555/√.5)+5⌋',\n", - " 790: '⌈(555/√.5)+5⌉',\n", - " 791: '⌊(555+5)/√.5⌋',\n", - " 792: '⌈(555+5)/√.5⌉',\n", - " 793: '⌈(5.5/5)*⌈5.5⌉!⌉',\n", - " 794: '(⌊(5+√5)*55⌋/.5)',\n", - " 795: '((555+5!)+5!)',\n", - " 796: '⌈55^(5/⌈5*.5⌉)⌉',\n", - " 797: '⌊((5*5)+5!)*5.5⌋',\n", - " 798: '(555+(⌈√5⌉^5))',\n", - " 799: '⌈⌈5!^√⌈5!/55⌉⌉/5⌉',\n", - " 800: '(((5+5)^5)/(5!+5))',\n", - " 801: '⌊(5!-5.5)*⌊√55⌋⌋',\n", - " 802: '⌈(5!-5.5)*⌊√55⌋⌉',\n", - " 803: '⌊(5!^√5)/55.5⌋',\n", - " 804: '⌈(5!^√5)/55.5⌉',\n", - " 805: '⌈√(55-(5+5))*5!⌉',\n", - " 806: '⌊(5-.5)^(5-.55)⌋',\n", - " 807: '⌊((5!+5)*5.5)+5!⌋',\n", - " 808: '⌈((5!+5)*5.5)+5!⌉',\n", - " 809: '⌊(√(55+5)*5!)-5!⌋',\n", - " 810: '⌊⌊(5!^√5)+5⌋/55⌋',\n", - " 811: '⌈⌊(5!^√5)+5⌋/55⌉',\n", - " 812: '⌈(5^√⌊5.5+5⌋)*5⌉',\n", - " 813: '⌊((5+√5)*5!)-55⌋',\n", - " 814: '(⌊√55*55⌋/.5)',\n", - " 815: '⌊(√55*55)/.5⌋',\n", - " 816: '⌈(√55*55)/.5⌉',\n", - " 817: '⌊5^((5/⌈5.5⌉)*5)⌋',\n", - " 818: '⌊(5!/.55)+(5!*5)⌋',\n", - " 819: '⌈(5!/.55)+(5!*5)⌉',\n", - " 820: '(((55*5)*⌈√5⌉)-5)',\n", - " 821: '⌊5!/(√.5^5.55)⌋',\n", - " 822: '⌊√⌊55-√55⌋*5!⌋',\n", - " 823: '⌊555+(5!*√5)⌋',\n", - " 824: '⌈555+(5!*√5)⌉',\n", - " 825: '(((5+5)+5)*55)',\n", - " 826: '⌊(5!*5)^(.55+.5)⌋',\n", - " 827: '⌊(5-.55)^(5-.5)⌋',\n", - " 828: '⌈(5-.55)^(5-.5)⌉',\n", - " 829: '⌈(((5+5)^5)/5!)-5⌉',\n", - " 830: '(⌊55.5*⌈√5⌉⌋*5)',\n", - " 831: '(⌊555*.5⌋*⌈√5⌉)',\n", - " 832: '⌊555*(⌈.5⌉+.5)⌋',\n", - " 833: '⌈555*(⌈.5⌉+.5)⌉',\n", - " 834: '⌊(√55*5!)-55⌋',\n", - " 835: '⌈(√55*5!)-55⌉',\n", - " 836: '⌊(5!-.55)*⌊√55⌋⌋',\n", - " 837: '⌈(5!-.55)*⌊√55⌋⌉',\n", - " 838: '⌊⌊5.5^5⌋/⌈5.5⌉⌋',\n", - " 839: '⌈⌊5.5^5⌋/⌈5.5⌉⌉',\n", - " 840: '(⌈5.555⌉!+5!)',\n", - " 841: '⌈(⌈5.5⌉!+.55)+5!⌉',\n", - " 842: '⌊(55*√5)+⌈5.5⌉!⌋',\n", - " 843: '⌊(√(55-5)*5!)-5⌋',\n", - " 844: '⌊√(55-5.5)*5!⌋',\n", - " 845: '((⌈5.55⌉!+5!)+5)',\n", - " 846: '⌈⌊5!-5.5⌋*√55⌉',\n", - " 847: '⌊(√55*(5!-5))-5⌋',\n", - " 848: '⌊√⌊55.5-5⌋*5!⌋',\n", - " 849: '⌈√⌊55.5-5⌋*5!⌉',\n", - " 850: '(((55+5!)-5)*5)',\n", - " 851: '⌈5!^((5/5.5)+.5)⌉',\n", - " 852: '⌊⌈5!-5.5⌉*√55⌋',\n", - " 853: '⌈⌈5!-5.5⌉*√55⌉',\n", - " 854: '⌈(√(55-5)*5!)+5⌉',\n", - " 855: '⌈(5.55^⌈√5⌉)*5⌉',\n", - " 856: '⌊√55.5*(5!-5)⌋',\n", - " 857: '⌈√55.5*(5!-5)⌉',\n", - " 858: '⌈(√55*(5!-5))+5⌉',\n", - " 859: '⌊55^(√5-.55)⌋',\n", - " 860: '⌈55^(√5-.55)⌉',\n", - " 861: '⌊((5!/55)+5)*5!⌋',\n", - " 862: '⌈((5!/55)+5)*5!⌉',\n", - " 863: '⌈5^(⌊5!/5.5⌋/5)⌉',\n", - " 864: '⌊(√55*5!)-(5*5)⌋',\n", - " 865: '(⌊55*√(5+5)⌋*5)',\n", - " 866: '⌈(√55*5!)-(5!/5)⌉',\n", - " 867: '⌊((√5-.5)^5)*55⌋',\n", - " 868: '⌊(√⌊5.55⌋+5)*5!⌋',\n", - " 869: '⌊(55*√(5+5))*5⌋',\n", - " 870: '((555-5!)/.5)',\n", - " 871: '⌊5!^√⌊.555*5⌋⌋',\n", - " 872: '⌊((55+5!)-.5)*5⌋',\n", - " 873: '⌈((55+5!)-.5)*5⌉',\n", - " 874: '⌊((55+5!)*5)-.5⌋',\n", - " 875: '(⌊55.5+5!⌋*5)',\n", - " 876: '⌈((55+5!)*5)+.5⌉',\n", - " 877: '⌊(55.5+5!)*5⌋',\n", - " 878: '⌈(55.5+5!)*5⌉',\n", - " 879: '⌊(√55*5!)-(5+5)⌋',\n", - " 880: '(⌈55.5+5!⌉*5)',\n", - " 881: '⌊√⌊55-.55⌋*5!⌋',\n", - " 882: '⌊⌊5!-.55⌋*√55⌋',\n", - " 883: '⌈⌊5!-.55⌋*√55⌉',\n", - " 884: '⌊(√55*5!)-5.5⌋',\n", - " 885: '⌊(5!-.55)*√55⌋',\n", - " 886: '⌈(5!-.55)*√55⌉',\n", - " 887: '⌈(⌈5.5^5⌉/5)-5!⌉',\n", - " 888: '⌊⌊√55*5!⌋-.55⌋',\n", - " 889: '⌊(55/√55)*5!⌋',\n", - " 890: '⌈(55/√55)*5!⌉',\n", - " 891: '⌈⌈√55*5!⌉+.55⌉',\n", - " 892: '⌈(5!^√5)/(55-5)⌉',\n", - " 893: '⌊√55.5*⌊5.5⌋!⌋',\n", - " 894: '⌊(.55+5!)*√55⌋',\n", - " 895: '⌊(√55*5!)+5.5⌋',\n", - " 896: '⌈(√55*5!)+5.5⌉',\n", - " 897: '⌊√⌈55.55⌉*5!⌋',\n", - " 898: '⌈√⌈55.55⌉*5!⌉',\n", - " 899: '⌈(√55.5*5!)+5⌉',\n", - " 900: '(((55+5)+5!)*5)',\n", - " 901: '⌊√55.5*⌈5!+.5⌉⌋',\n", - " 902: '⌊(5-(5.5/5))^5⌋',\n", - " 903: '⌈(5-(5.5/5))^5⌉',\n", - " 904: '⌊⌊55*√5⌋*√55⌋',\n", - " 905: '⌈⌊55*√5⌋*√55⌉',\n", - " 906: '(⌊55*5.5⌋*⌈√5⌉)',\n", - " 907: '⌊(55*5.5)*⌈√5⌉⌋',\n", - " 908: '⌈(55*5.5)*⌈√5⌉⌉',\n", - " 909: '(⌈55*5.5⌉*⌈√5⌉)',\n", - " 910: '⌊⌊√55*55⌋*√5⌋',\n", - " 911: '⌈⌊√55*55⌋*√5⌉',\n", - " 912: '⌊⌈55*√5⌉*√55⌋',\n", - " 913: '⌈⌈55*√5⌉*√55⌉',\n", - " 914: '⌊⌊5.5^5⌋/5.5⌋',\n", - " 915: '⌊5.5^⌊5-.55⌋⌋',\n", - " 916: '⌈5.5^⌊5-.55⌋⌉',\n", - " 917: '⌈√55.5*⌈5!+√5⌉⌉',\n", - " 918: '⌊(√5.5+5)*(5!+5)⌋',\n", - " 919: '⌊(55+5)^(5/⌈√5⌉)⌋',\n", - " 920: '⌊(5.5^⌊5-.5⌋)+5⌋',\n", - " 921: '⌊(5*.5)^√55.5⌋',\n", - " 922: '⌊(√55*(5!+5))-5⌋',\n", - " 923: '⌊((5+√5)*5!)+55⌋',\n", - " 924: '⌈((5+√5)*5!)+55⌉',\n", - " 925: '(((5!-55)+5!)*5)',\n", - " 926: '⌈⌊5^√(55*.5)⌋/5⌉',\n", - " 927: '⌊((√55*5)*5)*5⌋',\n", - " 928: '⌈((√55*5)*5)*5⌉',\n", - " 929: '⌊√⌊55.5+5⌋*5!⌋',\n", - " 930: '⌊(5.5+5!)*√55⌋',\n", - " 931: '⌈(5.5+5!)*√55⌉',\n", - " 932: '⌈√55.5*(5!+5)⌉',\n", - " 933: '⌊√(55.5+5)*5!⌋',\n", - " 934: '⌈√(55.5+5)*5!⌉',\n", - " 935: '(⌈5!/√55⌉*55)',\n", - " 936: '⌈√⌈55.5⌉*(5!+5)⌉',\n", - " 937: '⌊√⌈55.5+5⌉*5!⌋',\n", - " 938: '⌈√⌈55.5+5⌉*5!⌉',\n", - " 939: '⌊5.5/(.5^√55)⌋',\n", - " 940: '⌈5.5/(.5^√55)⌉',\n", - " 941: '⌊⌊√55+5!⌋*√55⌋',\n", - " 942: '⌊⌈5^5.5⌉/√55⌋',\n", - " 943: '⌈⌈5^5.5⌉/√55⌉',\n", - " 944: '⌊(√55*5!)+55⌋',\n", - " 945: '⌈(√55*5!)+55⌉',\n", - " 946: '⌈√((5.5+5)+5)^5⌉',\n", - " 947: '⌊((5+5)^√5)*5.5⌋',\n", - " 948: '⌊5.55^⌊5-.5⌋⌋',\n", - " 949: '⌈5.55^⌊5-.5⌋⌉',\n", - " 950: '((⌈√55*5⌉*5)*5)',\n", - " 951: '⌊⌈(5+5)^√5⌉*5.5⌋',\n", - " 952: '⌊√⌈√55+55⌉*5!⌋',\n", - " 953: '⌈√⌈√55+55⌉*5!⌉',\n", - " 954: '⌊(555+5!)/√.5⌋',\n", - " 955: '⌊(√55+.55)*5!⌋',\n", - " 956: '⌈(√55+.55)*5!⌉',\n", - " 957: '⌊(5^5)/(5.5-√5)⌋',\n", - " 958: '⌈(5^5)/(5.5-√5)⌉',\n", - " 959: '⌊(⌈√55⌉*5!)-.55⌋',\n", - " 960: '(⌈55/√55⌉*5!)',\n", - " 961: '⌊555*√⌈5*.5⌉⌋',\n", - " 962: '⌊(55+5!)*5.5⌋',\n", - " 963: '⌈(55+5!)*5.5⌉',\n", - " 964: '⌈555*(√5-.5)⌉',\n", - " 965: '⌊(5!/5.55)^√5⌋',\n", - " 966: '⌈(5!/5.55)^√5⌉',\n", - " 967: '⌊√((55+5)+5)*5!⌋',\n", - " 968: '⌊(5!^(5.5/5))*5⌋',\n", - " 969: '((⌊5-.5⌋^5)-55)',\n", - " 970: '(⌈5!^(5.5/5)⌉*5)',\n", - " 971: '⌈((5/.55)*5!)-5!⌉',\n", - " 972: '⌊(555-5!)*√5⌋',\n", - " 973: '⌈(555-5!)*√5⌉',\n", - " 974: '⌊√⌊.555*5!⌋*5!⌋',\n", - " 975: '⌈√⌊.555*5!⌋*5!⌉',\n", - " 976: '(⌊55*√5⌋*⌈√55⌉)',\n", - " 977: '⌈((5*.5)^5)*(5+5)⌉',\n", - " 978: '⌊⌈(5*√5)+5!⌉*√55⌋',\n", - " 979: '⌊(√⌊5.5+5⌋+5)*5!⌋',\n", - " 980: '(⌈(5*.5)^5⌉*(5+5))',\n", - " 981: '⌊((5-.5)/.55)*5!⌋',\n", - " 982: '⌊((5.5^5)-5!)/5⌋',\n", - " 983: '⌈((5.5^5)-5!)/5⌉',\n", - " 984: '⌈(55*⌈√55⌉)*√5⌉',\n", - " 985: '⌊⌈5!/.55⌉*(5-.5)⌋',\n", - " 986: '⌈⌈5!/.55⌉*(5-.5)⌉',\n", - " 987: '⌊√⌈5*√5⌉^5.55⌋',\n", - " 988: '⌊(⌈5.55⌉+√5)*5!⌋',\n", - " 989: '⌊5^(5!/⌈55*.5⌉)⌋',\n", - " 990: '((555/.5)-5!)',\n", - " 991: '⌊((5.5-√5)+5)*5!⌋',\n", - " 992: '⌈((5.5-√5)+5)*5!⌉',\n", - " 993: '⌊((5^5)/√(5+5))+5⌋',\n", - " 994: '⌊(√(.55*5)*5!)*5⌋',\n", - " 995: '(((55+5!)*5)+5!)',\n", - " 996: '⌊√⌈(5!+5)*.55⌉*5!⌋',\n", - " 997: '⌊(√(55/5)+5)*5!⌋',\n", - " 998: '⌈(√(55/5)+5)*5!⌉',\n", - " 999: '⌈⌈5^5.5⌉/⌊√55⌋⌉',\n", - " ...}" - ] - }, - "execution_count": 73, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "clear()\n", - "\n", - "%time makeable(five5s)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "The output got truncated by Jupyter Notebook after 1000 entries. Let's see what the first unmakeable integer actually is:" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "23308" - ] - }, - "execution_count": 74, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "unmakeable(five5s)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "button": false, - "new_sheet": false, - "run_control": { - "read_only": false - } - }, - "source": [ - "I think we've conquered the five 5s problem!\n", - "\n", "\n", "# Countdown to 2018\n", "\n", - "On January 1 2018, [Michael Littman](http://cs.brown.edu/~mlittman/) posted this:\n", + "One more thing: On January 1 2018, [Michael Littman](http://cs.brown.edu/~mlittman/) posted this:\n", "\n", - "> `2+0+1x8, 2+0-1+8, (2+0-1)x8, |2-0-1-8|, -2-0+1x8, -(2+0+1-8), sqrt(|2+0-18|), 2+0+1^8, 20-18, 2^(0x18), 2x0x1x8`... Happy New Year!\n", + "> 2+0+1×8, 2+0-1+8, (2+0-1)×8, |2-0-1-8|, -2-0+1×8, -(2+0+1-8), sqrt(|2+0-18|), 2+0+1^8, 20-18, 2^(0×18), 2×0×1×8... Happy New Year!\n", "\n", - "Can we replicate that countdown, using the four digits of the year, in order, with operators and parens to evaluate each of the numbers from 10 to 1? I'm assuming Michael is disallowing floor and ceil, but allowing our other operators." + "Can we replicate that countdown?" ] }, { "cell_type": "code", - "execution_count": 75, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'(20*.1)+8, (2.0-1)+8, (2.0-1)*8, (-2.0+1)+8, √(20*1.8), (-2.0-1)+8, √(-2.0+18), 2.0+(1^8), 20-18, (2.0-1)^8, (2*0)*18'" - ] - }, - "execution_count": 75, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "clear()\n", - "floor_ceil_allowed = False\n", - "\n", - "def littman_countdown(year):\n", - " \"Return a Littman countdown for the year.\"\n", - " return ', '.join(littman(year, i) for i in range(10, -1, -1))\n", - "\n", - "def littman(year, i):\n", - " \"Return a string that makes i with the digits of year.\"\n", - " digits = tuple(map(int, str(year)))\n", - " return unbracket(expressions(digits).get(i, '???'))\n", - "\n", - "littman_countdown(2018)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Similar results, with some alternatives for some numbers.\n", - "\n", - "Let's look at the decade:" - ] - }, - { - "cell_type": "code", - "execution_count": 76, + "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "20/(1+1), 20-11, (-.20+1)/.1, (2.0+1)!+1, √(20-11)!, (2.0+1)!-1, (2.0+1)+1, √(20-11), 2+(0*11), (2.0-1)*1, (2*0)*11 ... Happy New Year 2011!\n", - "-2.0+12, (2.0+1)^2, 20-12, 2.0+(1/.2), (2.0+1)*2, (2.0+1)+2, (20*1)*.2, (2.0-1)+2, 2+(0*12), (2.0+1)-2, (2*0)*12 ... Happy New Year 2012!\n", - "20/(-1+3), (2.0+1)*3, 2.0*(1+3), 20-13, (20*1)*.3, 20/(1+3), (2.0-1)+3, (2.0-1)*3, 2+(0*13), (2.0+1)/3, (2*0)*13 ... Happy New Year 2013!\n", - "2.0*(1+4), √((2.0+1)^4), (20*1)*.4, (2.0+1)+4, 20-14, (20*1)/4, 20/(1+4), (-20-1)+4!, 2+(0*14), .20*(1+4), (2*0)*14 ... Happy New Year 2014!\n", - "(20*1)*.5, √(201-5!), (2.0+1)+5, (20*.1)+5, (20*.15)!, 20-15, (20*1)/5, 20*.15, 2+(0*15), (.20*1)*5, (2*0)*15 ... Happy New Year 2015!\n", - "2.0*(-1+6), (2.0+1)+6, (20*.1)+6, (2.0-1)+6, √(20+16), 20/√16, 20-16, 2.0+(1^6), 2+(0*16), (2.0-1)^6, (2*0)*16 ... Happy New Year 2016!\n", - "(2.0+1)+7, (20*.1)+7, (2.0-1)+7, (2.0-1)*7, (20-17)!, (-2.0*1)+7, (-2.0-1)+7, 20-17, 2+(0*17), (2.0-1)^7, (2*0)*17 ... Happy New Year 2017!\n", - "(20*.1)+8, (2.0-1)+8, (2.0-1)*8, (-2.0+1)+8, √(20*1.8), (-2.0-1)+8, √(-2.0+18), 2.0+(1^8), 20-18, (2.0-1)^8, (2*0)*18 ... Happy New Year 2018!\n", - "20-(1+9), (2.0-1)*9, (-2.0+1)+9, (-2.0*1)+9, (-2.0-1)+9, 20/(1+√9), (2.0-1)+√9, 2.0+(1^9), 2+(0*19), 20-19, (2*0)*19 ... Happy New Year 2019!\n" + "2.0+(1*8), (2*0)+(1+8), (2*0)+(1*8), ((2*0)-1)+8, (2*(0-1))+8, -2+((0-1)+8), 20*(1-.8), 2.0+(1^8), 20-18, 2^(0*18), 2*(0*18) ... Happy New Year!\n" ] } ], "source": [ - "for y in range(2011, 2020):\n", - " print('{} ... Happy New Year {}!'.format(littman_countdown(y), y))" + "def littman_countdown(year):\n", + " \"Return a Littman countdown for the year, given as a tuple of numbers.\"\n", + " return ', '.join(unbracket(expressions(year)[i])\n", + " for i in reversed(range(11)))\n", + "\n", + "print(littman_countdown((2, 0, 1, 8)), '... Happy New Year!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similar results, with some alternatives for some numbers. Let's get ready for next year:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "20-(1+9), (2*0)+(1*9), ((2*0)-1)+9, (2*(0-1))+9, -2+((0-1)+9), 20/(1+√9), (2*0)+(1+√9), 2+(0.1+.9), 2+(0*19), 20-19, 2*(0*19) ... Happy New Year!\n" + ] + } + ], + "source": [ + "print(littman_countdown((2, 0, 1, 9)), '... Happy New Year!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# What's Next?\n", + "\n", + "One exercise would be adding even more operators, such as:\n", + "\n", + "- **Floor and Ceiling**: `⌊5.5⌋ == 5, ⌈5.5⌉ == 6`\n", + "- **Nth root**: `3√8 == 2`\n", + "- **Percent**: `5% == 5/100`\n", + "- **Repeating decimal**: `.4_ == .44444444... = 4/9`\n", + "- **Transcendental functions**: `log, sin, cos, tan, arcsin, ...` \n", + "\n", + "What would you like to do?" ] } ],