From d4300cb5afa8bcebb216cedf5816d2d868cd4e8e Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Tue, 7 Jul 2026 18:11:27 -0700 Subject: [PATCH] Add files via upload --- ipynb/Euler.ipynb | 1628 ++++++++++++++++++++++++--------------------- 1 file changed, 869 insertions(+), 759 deletions(-) diff --git a/ipynb/Euler.ipynb b/ipynb/Euler.ipynb index 1ebf399..831a918 100644 --- a/ipynb/Euler.ipynb +++ b/ipynb/Euler.ipynb @@ -15,15 +15,11 @@ "\n", "# Project Euler\n", "\n", - "[Project Euler](https://projecteuler.net) is a collection of math/programming [problems](https://projecteuler.net/archives). Here are my solutions for most of the first 100 problems. (I've done some of the higher-numbereed problems, but the project requests that solutions for them remain private). I think my solutions are reasonably clear, concise, and efficient. \n", + "[Project Euler](https://projecteuler.net) is a collection of math/programming [problems](https://projecteuler.net/archives). Here are my solutions for most of the first 100 problems. I think my solutions are reasonably clear, concise, and efficient. I didn't put in much explanation, because most of these were taken from .py files I did decades ago, and lost the corresponding documentation. I did port them from Python 2 to 3.\n", "\n", - "There are 5 problems I did not complete. I need to study some math before getting to them.\n", - "- 64, 65: continued fractions\n", - "- 66, 94, 100: Pell's equation\n", + "I also asked Claude 4.8 to solve the problems; you can see the result of that [in another notebook](Euler-LLM-AI.ipynb). The two notebooks share [`euler_data.py`](euler_data.py), which defines the expected answers for each problem, and the input data for problems that require input.\n", "\n", - "Project Euler suggests that solutions should run in a minute or less, but that recommendation was written when computers were much slower than today, so I'm aiming for one second per problem. I achieved that for every problem except 78, which takes about 3 seconds.\n", - "\n", - "\n" + "Project Euler suggests that solutions should run in a minute or less, but that recommendation was written when computers were much slower than today, so I aimed for one second per problem, and achieved that, after optimizing the 4 problems that originally ran over a second." ] }, { @@ -33,7 +29,7 @@ "source": [ "## Imports\n", "\n", - "I use the following modules:" + "I import the following modules." ] }, { @@ -43,17 +39,18 @@ "metadata": {}, "outputs": [], "source": [ + "from euler_data import DATA, EXPECTED #### Data and expected answers \n", + "\n", "from ast import literal_eval\n", - "from bisect import bisect_right\n", "from collections import Counter, defaultdict, deque\n", - "from collections.abc import Iterable, Iterator\n", + "from collections.abc import Iterable\n", "from fractions import Fraction\n", "from functools import cache\n", - "from itertools import permutations, combinations, combinations_with_replacement, cycle\n", - "from itertools import islice, repeat, takewhile, product as crossproduct, count as integers\n", + "from itertools import (permutations, combinations, combinations_with_replacement, cycle,\n", + " islice, repeat, takewhile, product as crossproduct)\n", "from math import gcd, lcm, factorial, comb, sqrt, isqrt, prod, log, inf\n", "from pathlib import Path\n", - "from sympy import factorint, sieve, divisors, primerange, isprime, prime\n", + "from sympy import factorint, sieve, divisors, primerange, isprime, prime, sieve as primes\n", "from statistics import mean, median\n", "\n", "import decimal\n", @@ -87,12 +84,9 @@ }, "outputs": [], "source": [ - "#### Constants \n", + "#### Constant\n", "\n", - "million = 1000000\n", - "primes = sieve # from sympy module\n", - "one_nine = '123456789'\n", - "digits = '0123456789'\n", + "million = 1_000_000\n", "\n", "#### Functions on iterables\n", "\n", @@ -108,9 +102,10 @@ " \"\"\"Returns the first true value (according to predicate) or the default if there is no true value.\"\"\"\n", " return next(filter(predicate, iterable), default)\n", "\n", - "def ints(start, end) -> range:\n", - " \"\"\"The integers from start to end, inclusive. Equal to range(start, end+1)\"\"\"\n", - " return range(start, end+1)\n", + "def integers(start, end=10**18, step=1) -> range:\n", + " \"\"\"The integers from start to end, inclusive, with a very big default for end. \n", + " Equal to range(start, end + 1, step)\"\"\"\n", + " return range(start, end + 1, step)\n", "\n", "def powerset(iterable) -> Iterable[tuple]:\n", " \"\"\"Yield all subsets of the iterable.\"\"\"\n", @@ -119,12 +114,12 @@ " for c in combinations(items, r):\n", " yield c\n", "\n", - "def groupby(iterable, key=lambda x: x) -> dict[object, list]:\n", - " \"\"\"Return a dict of {key(item): [items...]} grouping all the items in the iterable.\"\"\"\n", - " groups = defaultdict(list)\n", + "def bucket(iterable, key=lambda x: x) -> dict[object, list]:\n", + " \"\"\"Return a dict of {key(item): [items...]} grouping all the items in the iterable by key.\"\"\"\n", + " buckets = defaultdict(list)\n", " for item in iterable:\n", - " groups[key(item)].append(item)\n", - " return groups\n", + " buckets[key(item)].append(item)\n", + " return buckets\n", "\n", "def shuffled(iterable) -> list:\n", " \"\"\"Randomly shuffle the iterable.\"\"\"\n", @@ -156,30 +151,20 @@ " \"\"\"Are a and b (as printed strings) permutations of each other?\"\"\"\n", " return sorted(str(a)) == sorted(str(b))\n", "\n", - " \n", "def concat(iterable) -> str:\n", " \"\"\"Concatenate the items, converting each to a string first.\"\"\"\n", - " return ''.join(map(str, iterable))\n", - "\n", - "def read(source: Path|str) -> str:\n", - " \"\"\"Read text from a path, if given, or just return the str if given that.\n", - " (The option of a str is so you can pass in some simple text for debugging.)\"\"\"\n", - " match source:\n", - " case Path():\n", - " return (\"DATA\" / source).read_text()\n", - " case str():\n", - " return source # Don't read from a file; use this text as is. \n", + " return ''.join(map(str, iterable)) \n", "\n", "Matrix = list[list[int]]\n", "\n", "def parse_matrix(text: str) -> Matrix:\n", - " \"\"\"Read from a string that represents an array of ints, with each line being a row of items, \n", + " \"\"\"Read from a string that represents an array of integers, with each line being a row of items, \n", " space and/or comma delimited.\"\"\"\n", " text = text.replace(',', ' ') # change commas to space\n", " return [[int(x) for x in line.split()]\n", " for line in text.strip().splitlines()]\n", "\n", - "### Functions on digit strings\n", + "#### Functions on digit strings\n", "\n", "def digitlist(n: int) -> list[int]:\n", " \"\"\"Return a list of the digits in the decimal representation of n.\"\"\"\n", @@ -207,7 +192,11 @@ "def pentagonal(n): return n * (3 * n - 1) // 2 # pentagonal number\n", "def hexagonal(n): return n * (2 * n - 1) # hexagonal number\n", "def heptagonal(n): return n * (5 * n - 3) // 2 # heptagonal number\n", - "def octagonal(n): return n * (3 * n - 2) # octagonal number" + "def octagonal(n): return n * (3 * n - 2) # octagonal number\n", + "\n", + "def is_perfect_square(n: int) -> bool: \n", + " \"\"\"Is n a perfect square?\"\"\"\n", + " return isqrt(n) ** 2 == n" ] }, { @@ -217,7 +206,7 @@ "source": [ "## Answer Reporting\n", "\n", - "Each of my problem solutions Iends with a line of code like `answer(euler_1, 233168)`, which means to call `euler_1()` and check that the result is the expected value, 233168. The results (including the run time) are packaged up into an instance of the `answer` class and stored in `ANSWERS[1]`." + "Each of my problem solutions Iends with a line of code like `run(euler_1)`, which means to call `euler_1()` and check that the result matches the value of `EXPECTED[1]`, which is 233168. The results (including the run time) are packaged up into an instance of the `run` class and stored in `RUNS[1]`. If the problem needed some text data, it would be in `DATA[1]`." ] }, { @@ -233,37 +222,34 @@ }, "outputs": [], "source": [ - "ANSWERS = {} # dict of {problem number: answer_object}\n", + "RUNS = {}\n", "\n", - "class answer:\n", - " \"\"\"Verify that calling `euler_n()` computes the `expected` result. Store results in `ANSWERS`.\"\"\"\n", - " def __init__(self, euler_n, expected=None):\n", - " n = int(euler_n.__name__.split('_')[1])\n", - " self.euler_n, self.expected, self.n = euler_n, expected, n\n", - " ANSWERS[n] = self\n", - " self.run()\n", - " \n", - " def run(self) -> bool:\n", - " \"\"\"Check if euler_n() gets the expected result; record run time.\"\"\"\n", + "class run:\n", + " \"\"\"Verify that calling `euler_n()` computes the `EXPECTED` result.\n", + " Store run objects in `RUNS`.\"\"\"\n", + " def __init__(self, euler_n):\n", + " n = int(euler_n.__name__.split('_')[1])\n", + " self.n = n\n", + " RUNS[n] = self\n", + " self.title = (euler_n.__doc__ or '?:').split(':')[0]\n", " start = time.time()\n", - " self.got = self.euler_n()\n", + " self.got = euler_n()\n", " self.msecs = round((time.time() - start) * 1000)\n", - " \n", + "\n", " def __repr__(self) -> str:\n", - " check = ('āœ…' if (self.got == self.expected) else f'āŒ (expected {self.expected})')\n", - " speed = ('🐌' if self.msecs > 1000 else '') # Snail icon for all times over a second\n", - " title = (self.euler_n.__doc__ or '?:').split(':')[0]\n", - " return f'{self.n:3}: {title:40} {self.msecs:6,d} msec ⇒ {self.got:<16} {check} {speed}'\n", + " expected = EXPECTED[self.n]\n", + " check = ('āœ…' if (self.got == expected) else f'āŒ (expected {expected})')\n", + " speed = ('🐌' if self.msecs > 1000 else '') # Snail icon for run times over a second\n", + " return f'{self.n:3}: {self.title:40} {self.msecs:6,d} msec ⇒ {self.got:<16} {check} {speed}'\n", "\n", - "def summary(problems=range(1, 101)) -> None:\n", - " \"\"\"Summary report on the answers.\"\"\"\n", - " T = [answer.msecs / 1000 for answer in ANSWERS.values()]\n", - " print(f'Missing problems: {sorted(set(problems) - set(ANSWERS)) or None}\\n'\n", - " f'Run time in seconds: '\n", - " f'total: {sum(T):.1f}, max: {max(T):.1f}, mean: {mean(T):.3f}, median: {median(T):.3f}\\n')\n", - "\n", - " for i in sorted(ANSWERS):\n", - " print(ANSWERS[i])" + "def runs(problems=range(1, 101)) -> None:\n", + " \"\"\"Summary report on all the runs.\"\"\"\n", + " T = [answer.msecs / 1000 for answer in RUNS.values()]\n", + " print(f'Problems: {len(T)}\\n'\n", + " f'Run time in seconds: total: {sum(T):.1f}, max: {max(T):.1f}, '\n", + " f'mean: {mean(T):.3f}, median: {median(T):.3f}\\n')\n", + " for i in sorted(RUNS):\n", + " print(RUNS[i]) " ] }, { @@ -302,9 +288,9 @@ "source": [ "def euler_1(N=1000):\n", " \"\"\"Multiples of 3 and 5: Find the sum of all the multiples of 3 or 5 below 1000\"\"\"\n", - " return sum(i for i in range(1, N) if i%3 == 0 or i%5 == 0)\n", + " return sum(i for i in range(1, N) if i % 3 == 0 or i % 5 == 0)\n", "\n", - "answer(euler_1, 233168)" + "run(euler_1)" ] }, { @@ -345,7 +331,7 @@ " yield a\n", " a, b = b, a + b\n", "\n", - "answer(euler_2, 4613732)" + "run(euler_2)" ] }, { @@ -355,7 +341,7 @@ "source": [ "## [Problem 3](https://projecteuler.net/problem=3)\n", "\n", - "I could have used just `max(sympy.factorint(600851475143))` here, but I did this instead, as shown in [my introductory notebook specifically on this problem](Euler3.ipynb)." + "I could have used just `max(sympy.factorint(n))` here, but I did this instead, as shown in [my introductory notebook specifically on this problem](Euler3.ipynb)." ] }, { @@ -390,7 +376,7 @@ " return max(p, largest_prime_factor(n // p))\n", " return n # n is prime or 1\n", "\n", - "answer(euler_3, 6857)" + "run(euler_3)" ] }, { @@ -425,7 +411,7 @@ " products = map(prod, combinations(three_digit_numbers, 2))\n", " return max(filter(palindromic, products))\n", "\n", - "answer(euler_4, 906609)" + "run(euler_4)" ] }, { @@ -459,7 +445,7 @@ " # I can use the math.lcm (least common multiple) function for this\n", " return lcm(*range(1, limit + 1))\n", "\n", - "answer(euler_5, 232792560)" + "run(euler_5)" ] }, { @@ -490,11 +476,11 @@ "source": [ "def euler_6(N=100):\n", " \"\"\"Sum square difference: Find the difference between the sum of the squares of 1..100 and the square of the sum.\"\"\"\n", - " sum_of_squares = sum(map(square, ints(1, N)))\n", - " square_of_sum = square(sum(ints(1, N)))\n", + " sum_of_squares = sum(map(square, integers(1, N)))\n", + " square_of_sum = square(sum(integers(1, N)))\n", " return square_of_sum - sum_of_squares\n", "\n", - "answer(euler_6, 25164150)" + "run(euler_6)" ] }, { @@ -514,7 +500,7 @@ { "data": { "text/plain": [ - " 7: 10,001st prime 3 msec ⇒ 104743 āœ… " + " 7: 10,001st prime 2 msec ⇒ 104743 āœ… " ] }, "execution_count": 10, @@ -528,7 +514,7 @@ " # If you think this is cheating, I could have used my `Primes` class, shown at the end of this notebook\n", " return primes[N]\n", "\n", - "answer(euler_7, 104743)" + "run(euler_7)" ] }, { @@ -557,24 +543,12 @@ } ], "source": [ - "def euler_8():\n", + "def euler_8(data=DATA[8]):\n", " \"\"\"Largest product in a series: Find the 13 adjacent digits that have the greatest product.\"\"\"\n", - " substrings = sliding_window(map(int, data_8), 13)\n", + " substrings = sliding_window(map(int, data.replace('\\n', '')), 13)\n", " return max(map(prod, substrings))\n", "\n", - "data_8 = (\n", - " '7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843'\n", - " '8586156078911294949545950173795833195285320880551112540698747158523863050715693290963295227443043557'\n", - " '6689664895044524452316173185640309871112172238311362229893423380308135336276614282806444486645238749'\n", - " '3035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776'\n", - " '6572733300105336788122023542180975125454059475224352584907711670556013604839586446706324415722155397'\n", - " '5369781797784617406495514929086256932197846862248283972241375657056057490261407972968652414535100474'\n", - " '8216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586'\n", - " '1786645835912456652947654568284891288314260769004224219022671055626321111109370544217506941658960408'\n", - " '0719840385096245544436298123098787992724428490918884580156166097919133875499200524063689912560717606'\n", - " '0588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450')\n", - "\n", - "answer(euler_8, 23514624000)" + "run(euler_8)" ] }, { @@ -613,8 +587,7 @@ "def euler_9(perimeter=1000):\n", " \"\"\"Special Pythagorean triplet: There exists exactly one Pythagorean triplet (a, b, c) for which \n", " the perimeter a + b + c = 1000. Find the product abc.\"\"\"\n", - " (a, b, c) = first(pythagorean_triplets(perimeter))\n", - " return a * b * c\n", + " return prod(first(pythagorean_triplets(perimeter)))\n", "\n", "def pythagorean_triplets(perimeter: int) -> Iterable[tuple[int, int, int]]:\n", " \"\"\"Generate all Pythagorean triplets with a given perimeter\"\"\"\n", @@ -624,7 +597,7 @@ " for c in [perimeter - a - b]\n", " if a ** 2 + b ** 2 == c ** 2)\n", "\n", - "answer(euler_9, 31875000)" + "run(euler_9)" ] }, { @@ -644,7 +617,7 @@ { "data": { "text/plain": [ - " 10: Summation of primes 50 msec ⇒ 142913828922 āœ… " + " 10: Summation of primes 46 msec ⇒ 142913828922 āœ… " ] }, "execution_count": 13, @@ -657,7 +630,7 @@ " \"\"\"Summation of primes: Find the sum of all the primes below two million.\"\"\"\n", " return sum(primes.primerange(N))\n", "\n", - "answer(euler_10, 142913828922)" + "run(euler_10)" ] }, { @@ -694,44 +667,23 @@ } ], "source": [ - "def euler_11():\n", + "def euler_11(data=DATA[11]):\n", " \"\"\"Largest product in a matrix: What is the greatest product of four adjacent numbers in the same direction\n", " (up, left, or diagonally) in the 20x20 matrix?\"\"\"\n", - " matrix = parse_matrix(data_11)\n", + " matrix = parse_matrix(data)\n", " return max(adjacent_number_product(4, matrix, x, y, dx, dy)\n", " for x in range(20)\n", " for y in range(20)\n", - " for (dx, dy) in {(0, 1), (1, 0), (1, 1), (1, -1)})\n", + " for (dx, dy) in ((0, 1), (1, 0), (1, 1), (1, -1)))\n", "\n", "def adjacent_number_product(k, matrix, x, y, dx, dy) -> int:\n", " \"\"\"Product of k adjacent numbers starting at matrix[x][y] and going in direction (dx, dy).\"\"\"\n", " try:\n", " return prod(matrix[x + i * dx][y + i * dy] for i in range(k))\n", " except IndexError:\n", - " return 1\n", + " return 0\n", "\n", - "data_11 = (\"\"\"08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08\n", - "49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00\n", - "81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65\n", - "52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91\n", - "22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80\n", - "24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50\n", - "32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70\n", - "67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21\n", - "24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72\n", - "21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95\n", - "78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92\n", - "16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57\n", - "86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58\n", - "19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40\n", - "04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66\n", - "88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69\n", - "04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36\n", - "20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16\n", - "20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54\n", - "01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48\"\"\")\n", - "\n", - "answer(euler_11, 70600674)" + "run(euler_11)" ] }, { @@ -751,7 +703,7 @@ { "data": { "text/plain": [ - " 12: Highly divisible triangular number 155 msec ⇒ 76576500 āœ… " + " 12: Highly divisible triangular number 150 msec ⇒ 76576500 āœ… " ] }, "execution_count": 15, @@ -766,7 +718,7 @@ " return first(t for t in triangle_numbers \n", " if len(divisors(t)) > N)\n", "\n", - "answer(euler_12, 76576500)" + "run(euler_12)" ] }, { @@ -795,114 +747,12 @@ } ], "source": [ - "def euler_13(): \n", + "def euler_13(data=DATA[13]): \n", " \"\"\"Large sum: Work out the first ten digits of the sum of the following one-hundred 50-digit numbers.\"\"\"\n", - " total = sum(map(int, data_13.split()))\n", + " total = sum(map(int, data.split()))\n", " return int(str(total)[:10])\n", "\n", - "data_13 = \"\"\"\n", - "37107287533902102798797998220837590246510135740250\n", - "46376937677490009712648124896970078050417018260538\n", - "74324986199524741059474233309513058123726617309629\n", - "91942213363574161572522430563301811072406154908250\n", - "23067588207539346171171980310421047513778063246676\n", - "89261670696623633820136378418383684178734361726757\n", - "28112879812849979408065481931592621691275889832738\n", - "44274228917432520321923589422876796487670272189318\n", - "47451445736001306439091167216856844588711603153276\n", - "70386486105843025439939619828917593665686757934951\n", - "62176457141856560629502157223196586755079324193331\n", - "64906352462741904929101432445813822663347944758178\n", - "92575867718337217661963751590579239728245598838407\n", - "58203565325359399008402633568948830189458628227828\n", - "80181199384826282014278194139940567587151170094390\n", - "35398664372827112653829987240784473053190104293586\n", - "86515506006295864861532075273371959191420517255829\n", - "71693888707715466499115593487603532921714970056938\n", - "54370070576826684624621495650076471787294438377604\n", - "53282654108756828443191190634694037855217779295145\n", - "36123272525000296071075082563815656710885258350721\n", - "45876576172410976447339110607218265236877223636045\n", - "17423706905851860660448207621209813287860733969412\n", - "81142660418086830619328460811191061556940512689692\n", - "51934325451728388641918047049293215058642563049483\n", - "62467221648435076201727918039944693004732956340691\n", - "15732444386908125794514089057706229429197107928209\n", - "55037687525678773091862540744969844508330393682126\n", - "18336384825330154686196124348767681297534375946515\n", - "80386287592878490201521685554828717201219257766954\n", - "78182833757993103614740356856449095527097864797581\n", - "16726320100436897842553539920931837441497806860984\n", - "48403098129077791799088218795327364475675590848030\n", - "87086987551392711854517078544161852424320693150332\n", - "59959406895756536782107074926966537676326235447210\n", - "69793950679652694742597709739166693763042633987085\n", - "41052684708299085211399427365734116182760315001271\n", - "65378607361501080857009149939512557028198746004375\n", - "35829035317434717326932123578154982629742552737307\n", - "94953759765105305946966067683156574377167401875275\n", - "88902802571733229619176668713819931811048770190271\n", - "25267680276078003013678680992525463401061632866526\n", - "36270218540497705585629946580636237993140746255962\n", - "24074486908231174977792365466257246923322810917141\n", - "91430288197103288597806669760892938638285025333403\n", - "34413065578016127815921815005561868836468420090470\n", - "23053081172816430487623791969842487255036638784583\n", - "11487696932154902810424020138335124462181441773470\n", - "63783299490636259666498587618221225225512486764533\n", - "67720186971698544312419572409913959008952310058822\n", - "95548255300263520781532296796249481641953868218774\n", - "76085327132285723110424803456124867697064507995236\n", - "37774242535411291684276865538926205024910326572967\n", - "23701913275725675285653248258265463092207058596522\n", - "29798860272258331913126375147341994889534765745501\n", - "18495701454879288984856827726077713721403798879715\n", - "38298203783031473527721580348144513491373226651381\n", - "34829543829199918180278916522431027392251122869539\n", - "40957953066405232632538044100059654939159879593635\n", - "29746152185502371307642255121183693803580388584903\n", - "41698116222072977186158236678424689157993532961922\n", - "62467957194401269043877107275048102390895523597457\n", - "23189706772547915061505504953922979530901129967519\n", - "86188088225875314529584099251203829009407770775672\n", - "11306739708304724483816533873502340845647058077308\n", - "82959174767140363198008187129011875491310547126581\n", - "97623331044818386269515456334926366572897563400500\n", - "42846280183517070527831839425882145521227251250327\n", - "55121603546981200581762165212827652751691296897789\n", - "32238195734329339946437501907836945765883352399886\n", - "75506164965184775180738168837861091527357929701337\n", - "62177842752192623401942399639168044983993173312731\n", - "32924185707147349566916674687634660915035914677504\n", - "99518671430235219628894890102423325116913619626622\n", - "73267460800591547471830798392868535206946944540724\n", - "76841822524674417161514036427982273348055556214818\n", - "97142617910342598647204516893989422179826088076852\n", - "87783646182799346313767754307809363333018982642090\n", - "10848802521674670883215120185883543223812876952786\n", - "71329612474782464538636993009049310363619763878039\n", - "62184073572399794223406235393808339651327408011116\n", - "66627891981488087797941876876144230030984490851411\n", - "60661826293682836764744779239180335110989069790714\n", - "85786944089552990653640447425576083659976645795096\n", - "66024396409905389607120198219976047599490197230297\n", - "64913982680032973156037120041377903785566085089252\n", - "16730939319872750275468906903707539413042652315011\n", - "94809377245048795150954100921645863754710598436791\n", - "78639167021187492431995700641917969777599028300699\n", - "15368713711936614952811305876380278410754449733078\n", - "40789923115535562561142322423255033685442488917353\n", - "44889911501440648020369068063960672322193204149535\n", - "41503128880339536053299340368006977710650566631954\n", - "81234880673210146739058568557934581403627822703280\n", - "82616570773948327592232845941706525094512325230608\n", - "22918802058777319719839450180888072429661980811197\n", - "77158542502016545090413245809786882778948721859617\n", - "72107838435069186155435662884062257473692284509516\n", - "20849603980134001723930671666823555245252804609722\n", - "53503534226472524250874054075591789781264330331690\"\"\"\n", - "\n", - "answer(euler_13, 5537376230)" + "run(euler_13)" ] }, { @@ -922,7 +772,7 @@ { "data": { "text/plain": [ - " 14: Longest Collatz sequence 465 msec ⇒ 837799 āœ… " + " 14: Longest Collatz sequence 402 msec ⇒ 837799 āœ… " ] }, "execution_count": 17, @@ -942,7 +792,7 @@ " collatz_len(n // 2) if n % 2 == 0 else\n", " collatz_len(3 * n + 1))\n", "\n", - "answer(euler_14, 837799)" + "run(euler_14)" ] }, { @@ -978,7 +828,7 @@ " Start in top left, take steps either right or down, end in bottom right.\"\"\"\n", " return factorial(2 * N) // factorial(N) ** 2\n", "\n", - "answer(euler_15, 137846528820)" + "run(euler_15)" ] }, { @@ -1011,7 +861,7 @@ " \"\"\"Power digit sum: What is the sum of the digits of the number 2**1000?\"\"\"\n", " return sum(digitlist(2 ** N))\n", "\n", - "answer(euler_16, 1366)" + "run(euler_16)" ] }, { @@ -1047,9 +897,9 @@ "def euler_17():\n", " \"\"\"Number letter counts: If all the numbers from 1 to 1000 inclusive were written out in words, \n", " how many letters would be used?\"\"\"\n", - " return 21124\n", + " return 21124 # As computed by (loop for i from 1 to 1000 summing (count-if #'alpha-char-p (format nil \"~r\" i)))\n", "\n", - "answer(euler_17, 21124)" + "run(euler_17)" ] }, { @@ -1078,24 +928,7 @@ } ], "source": [ - "data_18 = \"\"\"\n", - "75\n", - "95 64\n", - "17 47 82\n", - "18 35 87 10\n", - "20 04 82 47 65\n", - "19 01 23 75 03 34\n", - "88 02 77 73 07 63 67\n", - "99 65 04 28 06 16 70 92\n", - "41 41 26 56 83 40 80 70 33\n", - "41 48 72 33 47 32 37 16 94 29\n", - "53 71 44 65 25 43 91 52 97 51 14\n", - "70 11 33 28 77 73 17 78 39 68 17 57\n", - "91 71 52 38 17 14 91 43 58 50 27 29 48\n", - "63 66 04 68 89 53 67 30 73 16 69 87 40 31\n", - "04 62 98 27 23 09 70 98 73 93 38 53 60 04 23\"\"\"\n", - "\n", - "def euler_18(data=data_18):\n", + "def euler_18(data=DATA[18]):\n", " \"\"\"Maximum path sum I: Find the maximum total from top to bottom of the triangle\n", " (by starting at the top and moving to adjacent numbers on the row below).\"\"\"\n", " # See also # 67\n", @@ -1116,9 +949,7 @@ " \n", " return cost(0, 0)\n", "\n", - "\n", - "\n", - "answer(euler_18, 1074)" + "run(euler_18)" ] }, { @@ -1138,7 +969,7 @@ { "data": { "text/plain": [ - " 19: Counting Sundays 6 msec ⇒ 171 āœ… " + " 19: Counting Sundays 3 msec ⇒ 171 āœ… " ] }, "execution_count": 22, @@ -1157,7 +988,7 @@ " return quantify(d.day == 1 and d.weekday() == Sunday and 1901 <= d.year <= 2000\n", " for d in days)\n", "\n", - "answer(euler_19, 171)" + "run(euler_19)" ] }, { @@ -1190,7 +1021,7 @@ " \"\"\"Factorial digit sum: Find the sum of the digits in the number 100!\"\"\"\n", " return digitsum(factorial(N))\n", "\n", - "answer(euler_20, 648)" + "run(euler_20)" ] }, { @@ -1218,7 +1049,7 @@ { "data": { "text/plain": [ - " 21: Amicable numbers 108 msec ⇒ 31626 āœ… " + " 21: Amicable numbers 96 msec ⇒ 31626 āœ… " ] }, "execution_count": 24, @@ -1243,7 +1074,7 @@ " \"\"\"The sum of all the divisors of n, except for n itself.\"\"\"\n", " return sum(divisors(n, proper=True))\n", "\n", - "answer(euler_21, 31626)" + "run(euler_21)" ] }, { @@ -1263,7 +1094,7 @@ { "data": { "text/plain": [ - " 22: Names scores 12 msec ⇒ 871198282 āœ… " + " 22: Names scores 7 msec ⇒ 871198282 āœ… " ] }, "execution_count": 25, @@ -1272,12 +1103,12 @@ } ], "source": [ - "def euler_22(path=Path('p022_names.txt')):\n", + "def euler_22(data=DATA[22]):\n", " \"\"\"Names scores: What is the total of all the name scores in the file names.txt?\n", " First sort the names into alphabetical order, \n", " Then the name score is the sum of the alphabetical values of the letters of the name,\n", " multiplied by the position of the name in alphabetical order.\"\"\"\n", - " names = literal_eval(read(path))\n", + " names = literal_eval(data)\n", " alphabetical_order = enumerate(sorted(names), 1)\n", " return sum(alphabetical_value_sum(name) * position for (position, name) in alphabetical_order)\n", "\n", @@ -1285,7 +1116,7 @@ " \"\"\"Sum of the numerical score of each letter in name: 1 for 'A', 2 for 'B', ... .\"\"\"\n", " return sum(1 + ord(c) - ord('A') for c in name)\n", "\n", - "answer(euler_22, 871198282)" + "run(euler_22)" ] }, { @@ -1305,7 +1136,7 @@ { "data": { "text/plain": [ - " 23: Non-abundant sums 472 msec ⇒ 4179871 āœ… " + " 23: Non-abundant sums 496 msec ⇒ 4179871 āœ… " ] }, "execution_count": 26, @@ -1315,18 +1146,19 @@ ], "source": [ "def euler_23(limit=28123):\n", - " \"\"\"Non-abundant sums: Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.\"\"\"\n", + " \"\"\"Non-abundant sums: Find the sum of all the positive integers which \n", + " cannot be written as the sum of two abundant numbers.\"\"\"\n", " # A number n is abundant if the sum of its proper divisors is greater than n.\n", " # We were given: All integers > 28123 can be written as the sum of 2 abundant numbers.\n", " # I'll make a set of all abundants up to that limit,\n", " # and then look for numbers `n` that can be formed by the sum of two abundants: \n", " # `a`, which we know is abundant, and `n - a`, which we check to see if it is in `abundants`.\n", - " abundants = {n for n in ints(1, limit) if sum_proper_divisors(n) > n}\n", - " return sum(n for n in ints(1, limit)\n", + " abundants = {n for n in integers(1, limit) if sum_proper_divisors(n) > n}\n", + " return sum(n for n in integers(1, limit)\n", " if not any(n - a in abundants \n", " for a in abundants))\n", "\n", - "answer(euler_23, 4179871)" + "run(euler_23)" ] }, { @@ -1346,7 +1178,7 @@ { "data": { "text/plain": [ - " 24: Lexicographic permutations 6 msec ⇒ 2783915460 āœ… " + " 24: Lexicographic permutations 7 msec ⇒ 2783915460 āœ… " ] }, "execution_count": 27, @@ -1356,11 +1188,12 @@ ], "source": [ "def euler_24(N=million):\n", - " \"\"\"Lexicographic permutations: What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9.\"\"\"\n", + " \"\"\"Lexicographic permutations: What is the millionth lexicographic permutation \n", + " of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9.\"\"\"\n", " perm = nth(permutations('0123456789'), N - 1)\n", " return int(concat(perm)) \n", "\n", - "answer(euler_24, 2783915460)" + "run(euler_24)" ] }, { @@ -1380,7 +1213,7 @@ { "data": { "text/plain": [ - " 25: 1000-digit Fibonacci number 16 msec ⇒ 4782 āœ… " + " 25: 1000-digit Fibonacci number 19 msec ⇒ 4782 āœ… " ] }, "execution_count": 28, @@ -1393,7 +1226,7 @@ " \"\"\"1000-digit Fibonacci number: What (index number) is the first term in the Fibonacci sequence to contain 1000 digits?\"\"\"\n", " return first(i for (i, n) in enumerate(fibseq(inf)) if len(str(n)) == N)\n", "\n", - "answer(euler_25, 4782)" + "run(euler_25)" ] }, { @@ -1413,7 +1246,7 @@ { "data": { "text/plain": [ - " 26: Reciprocal cycles 270 msec ⇒ 983 āœ… " + " 26: Reciprocal cycles 276 msec ⇒ 983 āœ… " ] }, "execution_count": 29, @@ -1423,17 +1256,18 @@ ], "source": [ "def euler_26(N=1000):\n", - " \"\"\"Reciprocal cycles: Find the value of d < 1000 for which 1/d contains the longest recurring cycle in its decimal fraction part.\"\"\"\n", + " \"\"\"Reciprocal cycles: Find the value of d < 1000 for which 1/d contains \n", + " the longest recurring cycle in its decimal fraction part.\"\"\"\n", " # From wikipedia.org/wiki/Repeating_decimal : \n", - " # \"The period of the repeating decimal of 1/d is equal to the multiplicative order of 10 modulo d.\"\n", + " # \"The period of the repeating decimal of 1/d is the multiplicative order of 10 modulo d.\"\n", " # The multiplicative order of 10 mod d is the smallest k such that 10**k = 1 (mod d)\n", " return max(range(1, N), key=cycle_length)\n", "\n", "def cycle_length(d: int) -> int:\n", " \"\"\"The length of the recurring cycle in the decimal representation of 1/d.\"\"\"\n", - " return first(k for k in ints(1, d) if (10 ** k) % d == 1)\n", + " return first(k for k in integers(1, d) if (10 ** k) % d == 1)\n", "\n", - "answer(euler_26, 983)" + "run(euler_26)" ] }, { @@ -1453,7 +1287,7 @@ { "data": { "text/plain": [ - " 27: Quadratic primes 234 msec ⇒ -59231 āœ… " + " 27: Quadratic primes 251 msec ⇒ -59231 āœ… " ] }, "execution_count": 30, @@ -1466,7 +1300,7 @@ " \"\"\"Quadratic primes: Find the product of the coefficients, a and b, \n", " for the quadratic expression (n**2 + a*n + b), starting with n = 0,\n", " that produces the maximum number of primes for consecutive values of n.\"\"\"\n", - " a_values = ints(-limit, limit) # I guess at a limit for `a`\n", + " a_values = integers(-limit, limit) # I guess at a limit for `a`\n", " b_values = primerange(limit) # `b` must be prime (so that we get a prime when n=0)\n", " coefficients = crossproduct(a_values, b_values)\n", " a, b = max(coefficients, key=count_consecutive_primes)\n", @@ -1479,7 +1313,7 @@ " if not isprime(n * n + a * n + b):\n", " return n\n", "\n", - "answer(euler_27, -59231)" + "run(euler_27)" ] }, { @@ -1535,7 +1369,7 @@ "\n", "assert spiral_diagonals(5) == [1, 3, 5, 7, 9, 13, 17, 21, 25]\n", "\n", - "answer(euler_28, 669171001)" + "run(euler_28)" ] }, { @@ -1565,10 +1399,11 @@ ], "source": [ "def euler_29(N=100):\n", - " \"\"\"Distinct powers: How many distinct terms are in the sequence generated by a**b for 2 <= a <= 100 and 2 <= b <= 100?\"\"\"\n", - " return len({a ** b for a in ints(2, N) for b in ints(2, N)})\n", + " \"\"\"Distinct powers: How many distinct terms are in the sequence generated by a**b \n", + " for 2 <= a <= 100 and 2 <= b <= 100?\"\"\"\n", + " return len({a ** b for a in integers(2, N) for b in integers(2, N)})\n", "\n", - "answer(euler_29, 9183)" + "run(euler_29)" ] }, { @@ -1588,7 +1423,7 @@ { "data": { "text/plain": [ - " 30: Digit fifth powers 190 msec ⇒ 443839 āœ… " + " 30: Digit fifth powers 194 msec ⇒ 443839 āœ… " ] }, "execution_count": 33, @@ -1598,17 +1433,18 @@ ], "source": [ "def euler_30():\n", - " \"\"\"Digit fifth powers: Find the sum of all the numbers that can be written as the sum of fifth powers of their digits.\"\"\"\n", + " \"\"\"Digit fifth powers: Find the sum of all the numbers that can be written as the sum \n", + " of fifth powers of their digits.\"\"\"\n", " # A d-digit number has sum of 5th powers satisfying d*(1**5) <= sum <= d*(9**5).\n", - " # For d=7, the maximum sum is 7*9**5 = 413,343, a 6-digit number, so we need only go up to 6 digits: 6 * 9 ** 5\n", + " # For d=7, the maximum sum is 7*9**5 = 413,343, a 6-digit number, so we need only up to 6 digits: 6 * 9 ** 5\n", " # The problem says 1 doesn't count, so start at 2.\n", - " return sum(filter(equals_sum_5th_powers_of_digits, ints(2, 6 * 9 ** 5 + 1)))\n", + " return sum(filter(equals_sum_5th_powers_of_digits, integers(2, 6 * 9 ** 5 + 1)))\n", "\n", "def equals_sum_5th_powers_of_digits(n: int) -> bool: \n", " \"\"\"Does n equal the sum of the 5th powers of its digits?\"\"\"\n", " return n == sum(d ** 5 for d in digitlist(n))\n", "\n", - "answer(euler_30, 443839)" + "run(euler_30)" ] }, { @@ -1657,7 +1493,7 @@ " coin_sums(n - coins[0], coins) + # multiple ways: with and without using first coin\n", " coin_sums(n, coins[1:]))\n", "\n", - "answer(euler_31, 73682)" + "run(euler_31)" ] }, { @@ -1684,7 +1520,7 @@ { "data": { "text/plain": [ - " 32: Pandigital products 74 msec ⇒ 45228 āœ… " + " 32: Pandigital products 72 msec ⇒ 45228 āœ… " ] }, "execution_count": 35, @@ -1709,7 +1545,7 @@ " Some authors include 0 in the digits, but Project Euler does not.\"\"\"\n", " return len(digit_str) == len(digitset) and set(digit_str) == digitset\n", "\n", - "answer(euler_32, 45228)" + "run(euler_32)" ] }, { @@ -1743,7 +1579,7 @@ " There are 4 non-trivial fractions of this form, less than 1 in value, with two digits\n", " in both denominator and numerator. Multiply the 4 fractions and give the denominator.\"\"\"\n", " # We want a/b with 10 <= a < b < 99\n", - " fractions = {Fraction(a, b) for a in ints(10, 99) for b in ints(a + 1, 99) \n", + " fractions = {Fraction(a, b) for a in integers(10, 99) for b in integers(a + 1, 99) \n", " if cancellable_fraction(a,b)}\n", " assert len(fractions) == 4\n", " return prod(fractions)._denominator\n", @@ -1756,7 +1592,7 @@ " return any(d for d in cancelable_digits\n", " if a * int(B.replace(d,'',1)) == b * int(A.replace(d,'',1)))\n", "\n", - "answer(euler_33, 100)" + "run(euler_33)" ] }, { @@ -1776,7 +1612,7 @@ { "data": { "text/plain": [ - " 34: Digit factorials 468 msec ⇒ 40730 āœ… " + " 34: Digit factorials 460 msec ⇒ 40730 āœ… " ] }, "execution_count": 37, @@ -1792,10 +1628,10 @@ " # So any 7-digit number > that, or any 8+ digit number must be > its factorial sum.\n", " fact = {str(d): factorial(d) for d in range(10)}.get # Cached factorial(digit: str) function\n", " limit = 7 * factorial(9)\n", - " return sum(n for n in ints(10, limit)\n", + " return sum(n for n in integers(10, limit)\n", " if n == sum(map(fact, str(n))))\n", " \n", - "answer(euler_34, 40730)" + "run(euler_34)" ] }, { @@ -1815,7 +1651,7 @@ { "data": { "text/plain": [ - " 35: Circular primes 382 msec ⇒ 55 āœ… " + " 35: Circular primes 368 msec ⇒ 55 āœ… " ] }, "execution_count": 38, @@ -1837,7 +1673,7 @@ " s = str(n)\n", " return [int(s[i:]+s[:i]) for i in range(len(s))]\n", "\n", - "answer(euler_35, 55)" + "run(euler_35)" ] }, { @@ -1859,7 +1695,7 @@ { "data": { "text/plain": [ - " 36: Double-base palindromes 75 msec ⇒ 872187 āœ… " + " 36: Double-base palindromes 76 msec ⇒ 872187 āœ… " ] }, "execution_count": 39, @@ -1874,7 +1710,7 @@ " return sum(i for i in range(N)\n", " if palindromic(i) and palindromic(f'{i:b}'))\n", "\n", - "answer(euler_36, 872187)" + "run(euler_36)" ] }, { @@ -1926,7 +1762,7 @@ " s = str(p)\n", " return p >= 10 and all(isprime(int(s[i:])) for i in range(1, len(s)))\n", "\n", - "answer(euler_37, 748317)" + "run(euler_37)" ] }, { @@ -1952,7 +1788,7 @@ { "data": { "text/plain": [ - " 38: Pandigital multiples 43 msec ⇒ 932718654 āœ… " + " 38: Pandigital multiples 44 msec ⇒ 932718654 āœ… " ] }, "execution_count": 41, @@ -1966,16 +1802,16 @@ " product of an integer with the list (1,2, ... , n) where n > 1? For example:\n", " 192 * 1 = 192; 192 * 2 = 384; 192 * 3 = 576, and 192384576 is pandigital.\n", " Thus each candidate product is of the form total = 'i*1' 'i*2' ... 'i*n' , for some i and n.\"\"\"\n", - " candidates = (concatenated_prod(i, ints(1, n))\n", - " for i in ints(1, 9999)\n", - " for n in ints(2,9))\n", + " candidates = (concatenated_prod(i, integers(1, n))\n", + " for i in integers(1, 9999)\n", + " for n in integers(2,9))\n", " return int(max(filter(pandigital, candidates)))\n", "\n", "def concatenated_prod(n, multipliers) -> str:\n", " \"\"\"E.g. concatenated_prod(192, (1, 2, 3)) = '192384576' because 192*1=192, 192*2=384, 192*3=576\"\"\"\n", " return concat(n * m for m in multipliers)\n", "\n", - "answer(euler_38, 932718654)" + "run(euler_38)" ] }, { @@ -2019,7 +1855,8 @@ ], "source": [ "def euler_39(P=1000):\n", - " \"\"\"Integer right triangles: Which value of the perimeter p <= 1000 has the max number of integral right triangles with perimeter p?\"\"\"\n", + " \"\"\"Integer right triangles: Which value of the perimeter p <= 1000 has the max number of \n", + " integral right triangles with perimeter p?\"\"\"\n", " perimeters = map(sum, integral_right_triangles(P))\n", " [(p, count)] = Counter(perimeters).most_common(1)\n", " return p\n", @@ -2027,12 +1864,12 @@ "def integral_right_triangles(P: int) -> Iterable[tuple[int, int, int]]:\n", " \"\"\"All (a, b, c) tuples that form a right triangle, with a <= b < c, and perimeter a + b + c < P.\"\"\"\n", " return ((a, b, int(c))\n", - " for a, b in combinations(ints(1, P // 2), 2) # Neither a nor b may be more than half the perimeter\n", + " for a, b in combinations(integers(1, P // 2), 2) # Neither a nor b may be more than half the perimeter\n", " if (c:= sqrt(a ** 2 + b ** 2)).is_integer() and a + b + c <= P)\n", "\n", "assert list(integral_right_triangles(30)) == [(3, 4, 5), (5, 12, 13), (6, 8, 10)]\n", "\n", - "answer(euler_39, 840)" + "run(euler_39)" ] }, { @@ -2059,7 +1896,7 @@ { "data": { "text/plain": [ - " 40: Champernowne's constant 39 msec ⇒ 210 āœ… " + " 40: Champernowne's constant 41 msec ⇒ 210 āœ… " ] }, "execution_count": 43, @@ -2076,7 +1913,7 @@ " d = digitlist(concat(range(million // 5)))\n", " return prod(d[10 ** i] for i in range(7))\n", "\n", - "answer(euler_40, 210)" + "run(euler_40)" ] }, { @@ -2121,7 +1958,7 @@ " pandigitals = (int(concat(digits)) for digits in permutations('1234567'))\n", " return first(filter(isprime, sorted(pandigitals, reverse=True)))\n", "\n", - "answer(euler_41, 7652413)" + "run(euler_41)" ] }, { @@ -2141,7 +1978,7 @@ { "data": { "text/plain": [ - " 42: Coded triangle numbers 3 msec ⇒ 162 āœ… " + " 42: Coded triangle numbers 2 msec ⇒ 162 āœ… " ] }, "execution_count": 45, @@ -2150,15 +1987,15 @@ } ], "source": [ - "def euler_42(words=None):\n", + "def euler_42(data=DATA[42]):\n", " \"\"\"Coded triangle numbers: How many words are triangle words \n", " (their alphabetical value (see euler_22) is a triangle number)?\"\"\"\n", - " words = words or literal_eval(read(Path('p042_words.txt')))\n", + " words = literal_eval(data)\n", " triangle_numbers = set(map(triangle, range(1, 100)))\n", - " return quantify(alphabetical_value_sum(w) in triangle_numbers \n", - " for w in words)\n", + " return quantify(alphabetical_value_sum(word) in triangle_numbers \n", + " for word in words)\n", "\n", - "answer(euler_42, 162)" + "run(euler_42)" ] }, { @@ -2185,7 +2022,7 @@ { "data": { "text/plain": [ - " 43: Sub-string divisibility 12 msec ⇒ 16695334890 āœ… " + " 43: Sub-string divisibility 16 msec ⇒ 16695334890 āœ… " ] }, "execution_count": 46, @@ -2194,7 +2031,7 @@ } ], "source": [ - "def euler_43(digits=set(digits)):\n", + "def euler_43(digits=set('0123456789')):\n", " \"\"\"Sub-string divisibility: Find the sum of all 0-to-9 pandigital numbers with the substring divisibility property:\n", " 3-digit substrings of n are each divisible by the respective first 7 primes; that is,\n", " d2d3d4 is divisible by 2; d3d4d5 is divisible by 3; ... d8d9d10 is divisible by 17.\n", @@ -2213,7 +2050,7 @@ " return all(int(concat(n[i:i+3])) % p == 0\n", " for (i, p) in enumerate(primes, start))\n", "\n", - "answer(euler_43, 16695334890)" + "run(euler_43)" ] }, { @@ -2233,7 +2070,7 @@ { "data": { "text/plain": [ - " 44: Pentagon numbers 110 msec ⇒ 5482660 āœ… " + " 44: Pentagon numbers 116 msec ⇒ 5482660 āœ… " ] }, "execution_count": 47, @@ -2252,7 +2089,7 @@ " if pk + pj in pentagonals \n", " and pk - pj in pentagonals)\n", "\n", - "answer(euler_44, 5482660)" + "run(euler_44)" ] }, { @@ -2272,7 +2109,7 @@ { "data": { "text/plain": [ - " 45: Triangular/pentagonal/hexagonal 18 msec ⇒ 1533776805 āœ… " + " 45: Triangular/pentagonal/hexagonal 17 msec ⇒ 1533776805 āœ… " ] }, "execution_count": 48, @@ -2281,15 +2118,16 @@ } ], "source": [ - "def euler_45(N=100000):\n", - " \"\"\"Triangular/pentagonal/hexagonal: Find the next triangle number after 40755 that is also pentagonal and hexagonal.\"\"\"\n", - " triangles = (triangle(n) for n in integers(40755))\n", + "def euler_45(N=100_000):\n", + " \"\"\"Triangular/pentagonal/hexagonal: Find the next triangle number > 40755 that is pentagonal and hexagonal.\n", + " This assumes that 100,000 pentagonal and hexagonal numbers will be enough.\"\"\"\n", + " triangles = (triangle(n) for n in integers(start=40756))\n", " pentagonals = {pentagonal(n) for n in range(N)}\n", " hexagonals = {hexagonal(n) for n in range(N)}\n", " return first(t for t in triangles\n", " if t in pentagonals and t in hexagonals)\n", "\n", - "answer(euler_45, 1533776805)" + "run(euler_45)" ] }, { @@ -2328,9 +2166,9 @@ " \"\"\"Is there no way to express n as the sum of a prime and twice a square?\"\"\"\n", " # If n = p + 2 * i ** 2, for some prime p and some i, then n - 2 * i ** 2 must be prime\n", " return not any(isprime(n - 2 * i ** 2) \n", - " for i in ints(1, n))\n", + " for i in integers(1, n))\n", "\n", - "answer(euler_46, 5777)" + "run(euler_46)" ] }, { @@ -2350,7 +2188,7 @@ { "data": { "text/plain": [ - " 47: Distinct primes factors 272 msec ⇒ 134043 āœ… " + " 47: Distinct primes factors 268 msec ⇒ 134043 āœ… " ] }, "execution_count": 50, @@ -2363,7 +2201,7 @@ " \"\"\"Distinct primes factors: Find the first N=4 consecutive integers to have exactly 4 distinct primes factors.\n", " What is the first of these 4 numbers?\"\"\"\n", " consecutive = 0\n", - " for i in integers():\n", + " for i in integers(start=1):\n", " if consecutive == N:\n", " return i - N\n", " elif len(factorint(i)) == N:\n", @@ -2373,7 +2211,7 @@ "\n", "assert euler_47(2) == 14 and euler_47(3) == 644 # As specified in the problem description\n", "\n", - "answer(euler_47, 134043)" + "run(euler_47)" ] }, { @@ -2405,9 +2243,9 @@ "def euler_48(N=1000, M=10**10):\n", " \"\"\"Self powers: Find the last ten digits of the (sum of the) series, 1**1 + 2**2 + 3**3 + ... + 1000**1000.\"\"\"\n", " # To deal only with the last 10 digits, operate modulo M = 10**10\n", - " return sum(pow(i, i, mod=M) for i in ints(1, N)) % M\n", + " return sum(pow(i, i, mod=M) for i in integers(1, N)) % M\n", "\n", - "answer(euler_48, 9110846700)" + "run(euler_48)" ] }, { @@ -2452,7 +2290,7 @@ " else:\n", " return None\n", "\n", - "answer(euler_49, 2969_6299_9629)" + "run(euler_49)" ] }, { @@ -2503,7 +2341,7 @@ " total += sequence[i] - sequence[i - n]\n", " return None\n", "\n", - "answer(euler_50, 997651)" + "run(euler_50)" ] }, { @@ -2533,7 +2371,7 @@ { "data": { "text/plain": [ - " 51: Prime digit replacements 315 msec ⇒ 121313 āœ… " + " 51: Prime digit replacements 317 msec ⇒ 121313 āœ… " ] }, "execution_count": 54, @@ -2554,14 +2392,14 @@ " s = str(p)\n", " for d in set(s):\n", " family = [int(s.replace(d, d2)) \n", - " for d2 in digits\n", + " for d2 in '0123456789'\n", " if not (d2 == '0' and d == s[0])] # Don't replace leading digit with a '0'\n", " prime_family = [n for n in family if isprime(n)]\n", " if len(prime_family) >= length:\n", " return prime_family\n", "\n", "\n", - "answer(euler_51, 121313)" + "run(euler_51)" ] }, { @@ -2592,7 +2430,7 @@ { "data": { "text/plain": [ - " 52: Permuted multiples 93 msec ⇒ 142857 āœ… " + " 52: Permuted multiples 94 msec ⇒ 142857 āœ… " ] }, "execution_count": 56, @@ -2602,12 +2440,13 @@ ], "source": [ "def euler_52():\n", - " \"\"\"Permuted multiples: find the smallest positive integer, x, such that x, 2x, 3x, 4x, 5x, and 6x, contain the same digits.\"\"\"\n", + " \"\"\"Permuted multiples: find the smallest positive integer, x, \n", + " such that x, 2x, 3x, 4x, 5x, and 6x, contain the same digits.\"\"\"\n", " s = sorted_characters # Use `s` as an abbreviation for the sorted_characters utility function\n", " return first(x for x in integers(start=1)\n", " if s(x) == s(2*x) == s(3*x) == s(4*x) == s(5*x) == s(6*x))\n", "\n", - "answer(euler_52, 142857)" + "run(euler_52)" ] }, { @@ -2640,10 +2479,10 @@ " \"\"\"Combinatoric selections: How many values of n C r, for 1 <= n <= 100, are greater than a million?\n", " They need not be distinct.\"\"\"\n", " return quantify(comb(n,r) > million\n", - " for n in ints(1,100) \n", - " for r in ints(1,n))\n", + " for n in integers(1,100) \n", + " for r in integers(1,n))\n", "\n", - "answer(euler_53, 4075)" + "run(euler_53)" ] }, { @@ -2653,19 +2492,17 @@ "source": [ "## [Problem 54](https://projecteuler.net/problem=54)\n", "\n", - "We decide which poker hand wins by computing the `poker_value` for each hand, and comparing the values with `>`. The value is a tuple, where the first element is an integer denoting the category (8 for straight flush, 4 for straight, etc.) and the second element is a list of list of \"kickers,\" the exact rank of each card, organized with the most important ones first.\n", + "We decide which poker hand wins by computing the `poker_value` for each hand, and comparing the values with `>`. The value is a tuple, where the first element is an integer denoting the hand's category (8 for straight flush, 4 for straight, etc.) and the second element is a list of list of tiebreakers: the ranks of cards, grouped most important first. (That is, with a pair of 8s and three 5s, the 5 is more important than the 8 for breaking ties.)\n", "\n", "|variable|value|explanation|\n", "|---|---|---|\n", - "|hand | ['8S', '5C', '8D', '5D', '5S'] | example hand of cards: 8ā™  5♣ 8♦ 5♦ 5ā™ |\n", - "|ranks| [8, 5, 8, 5, 5] | ranks of the five cards|\n", - "|suits | {'S', 'C', 'D'} | set of the suits of the cards |\n", - "|flush | False| not a flush|\n", - "|straight|False|not a straight|\n", - "| kind |[[], [], [8], [5], []]|kind[2] = 8 (pair) and kind[3] = 5 (3-of-a-kind)|\n", - "| category | 6 | full house: a 3-of-a-kind and a 2-of-a-kind (pair)|\n", - "| kickers | [[], [5], [8], [], []]|the 5 (3-of-a-kind) is more important than the 8 (pair)|\n", - "| return |(6, [[], [5], [8], [], []]|major category and minor tiebreakers|" + "|`hand` | `['8S', '5C', '8D', '5D', '5S']` | example hand of cards: 8ā™  5♣ 8♦ 5♦ 5ā™ |\n", + "|`ranks`| `[8, 5, 8, 5, 5]` | ranks of the five cards|\n", + "|`flush` | `False`| not a flush|\n", + "|`straight`|`False`|not a straight|\n", + "| `kind` |`[[], [], [8], [5], []]`|kind[2] = [8] (pair) and kind[3] = [5] (3-of-a-kind)|\n", + "| `category` | `6` | full house: a 3-of-a-kind and a pair|\n", + "| **`return`** |`6, [[], [5], [8], [], []]`|category and tiebreakers|" ] }, { @@ -2677,7 +2514,7 @@ { "data": { "text/plain": [ - " 54: Poker hands 8 msec ⇒ 376 āœ… " + " 54: Poker hands 6 msec ⇒ 376 āœ… " ] }, "execution_count": 58, @@ -2686,36 +2523,33 @@ } ], "source": [ - "def euler_54(path=Path(\"p054_poker.txt\")):\n", - " \"\"\"Poker hands: In how many hands of poker does Player 1 win in the file poker.txt, \n", - " which has 2 hands of 5 cards on each line?\"\"\"\n", - " lines = map(str.split, read(path).splitlines())\n", + "def euler_54(data=DATA[54]):\n", + " \"\"\"Poker hands: Given lines with ten Poker casrds each, \n", + " how many times do the first 5 cards beat the last 5?\"\"\"\n", + " lines = map(str.split, data.splitlines())\n", " return quantify(poker_value(line[:5]) > poker_value(line[5:])\n", " for line in lines)\n", "\n", - "def poker_value(hand: list[str]) -> tuple[int, list]:\n", - " \"\"\"Return a value for a poker hand. The value can be used to compare to other poker hands,\n", - " and consists of a category value (0 to 8) followed by kickers (tiebreakers).\"\"\"\n", - " # kind[k] is a list of the ranks that have k-of-a-kind\n", + "def poker_value(hand:tuple[str]) -> tuple:\n", + " \"\"\"Return a value indicating how high the hand ranks.\"\"\"\n", " ranks = ['..23456789TJQKA'.index(r) for r,s in hand]\n", - " suits = {s for r,s in hand}\n", - " flush = len(suits) == 1\n", - " straight = (max(ranks) - min(ranks)) == 4 and len(set(ranks)) == 5\n", - " straight = all(r in ranks for r in ints(min(ranks), max(ranks)))\n", - " kind = [[r for r in range(14, 2, -1) if ranks.count(r) == i] for i in range(5)]\n", - " category = (8 if straight and flush else\n", - " 7 if kind[4] else\n", - " 6 if kind[3] and kind[2] else\n", - " 5 if flush else\n", - " 4 if straight else\n", - " 3 if kind[3] else\n", - " 2 if len(kind[2]) == 2 else\n", - " 1 if kind[2] else\n", - " 0)\n", - " kickers = kind[::-1]\n", - " return (category, kickers)\n", + " straight = len(set(ranks)) == 5 and max(ranks) - min(ranks) == 4\n", + " flush = len({s for r,s in hand}) == 1\n", + " kind = [[r for r in sorted(set(ranks))[::-1] if ranks.count(r) == i]\n", + " for i in range(5)]\n", + " category = (\n", + " 8 if straight and flush else\n", + " 7 if kind[4] else\n", + " 6 if kind[3] and kind[2] else\n", + " 5 if flush else\n", + " 4 if straight else\n", + " 3 if kind[3] else\n", + " 2 if len(kind[2]) == 2 else\n", + " 1 if kind[2] else\n", + " 0)\n", + " return category, kind[::-1]\n", "\n", - "answer(euler_54, 376)" + "run(euler_54)" ] }, { @@ -2735,7 +2569,7 @@ { "data": { "text/plain": [ - " 55: Lychrel numbers 10 msec ⇒ 249 āœ… " + " 55: Lychrel numbers 8 msec ⇒ 249 āœ… " ] }, "execution_count": 59, @@ -2760,7 +2594,7 @@ " return False\n", " return True\n", "\n", - "answer(euler_55, 249)" + "run(euler_55)" ] }, { @@ -2780,7 +2614,7 @@ { "data": { "text/plain": [ - " 56: Powerful digit sum 44 msec ⇒ 972 āœ… " + " 56: Powerful digit sum 42 msec ⇒ 972 āœ… " ] }, "execution_count": 60, @@ -2794,7 +2628,7 @@ " what is the maximum sum of the digits?\"\"\"\n", " return max(digitsum(a**b) for a in consider for b in consider)\n", "\n", - "answer(euler_56, 972)" + "run(euler_56)" ] }, { @@ -2814,7 +2648,7 @@ { "data": { "text/plain": [ - " 57: Square root convergents 5 msec ⇒ 153 āœ… " + " 57: Square root convergents 6 msec ⇒ 153 āœ… " ] }, "execution_count": 61, @@ -2829,14 +2663,14 @@ " return quantify(len(str(x._numerator)) > len(str(x._denominator))\n", " for x in islice(root2(), N))\n", "\n", - "def root2() -> Fraction:\n", + "def root2() -> Iterable[Fraction]:\n", " \"\"\"Yield the successive expansions of sqrt(2).\"\"\"\n", " x = Fraction(1, 2)\n", " while True:\n", " yield 1 + x\n", " x = Fraction(1, 2 + x)\n", "\n", - "answer(euler_57, 153)" + "run(euler_57)" ] }, { @@ -2856,7 +2690,7 @@ { "data": { "text/plain": [ - " 58: Spiral primes 43 msec ⇒ 26241 āœ… " + " 58: Spiral primes 41 msec ⇒ 26241 āœ… " ] }, "execution_count": 62, @@ -2889,7 +2723,7 @@ " if nprimes / (2 * s - 1.0) < proportion:\n", " return s\n", "\n", - "answer(euler_58, 26241)" + "run(euler_58)" ] }, { @@ -2909,7 +2743,7 @@ { "data": { "text/plain": [ - " 59: XOR decryption 771 msec ⇒ 107359 āœ… " + " 59: XOR decryption 651 msec ⇒ 107359 āœ… " ] }, "execution_count": 63, @@ -2918,11 +2752,11 @@ } ], "source": [ - "def euler_59(path=Path(\"p059_cipher.txt\")):\n", - " \"\"\"XOR decryption: Using cipher1.txt, a file containing the encrypted ASCII codes, and the knowledge that the plain text \n", - " must contain common English words, and has been XOR-encoded with a pad of three lowercase letters, decode the message \n", - " and find the sum of the ASCII values in the original text.\"\"\"\n", - " text = literal_eval(read(path))\n", + "def euler_59(data=DATA[59]):\n", + " \"\"\"XOR decryption: Using a file containing comma-separated encrypted ASCII codes, \n", + " and the knowledge that the plain text must contain common English words, and has been XOR-encoded \n", + " with a pad of three lowercase letters, decode the message and find the sum of the ASCII values in the original text.\"\"\"\n", + " text = literal_eval(data)\n", " def decrypt(text, pad) -> int:\n", " \"\"\"XOR the text with the pad (cycling through the pad if it runs out).\"\"\"\n", " return [t ^ p for (t, p) in zip(text, cycle(pad))]\n", @@ -2930,11 +2764,11 @@ " \"\"\"How many common words can you find in text?\"\"\"\n", " strtext = concat(map(chr, text))\n", " return len(re.findall(' the | of | and | to | in | a | is ', strtext, re.I))\n", - " lower = range(ord('a'), ord('z')+1)\n", - " candidates = [decrypt(text, pad) for pad in crossproduct(lower, lower, lower)]\n", + " lower = range(ord('a'), ord('z') + 1)\n", + " candidates = (decrypt(text, pad) for pad in crossproduct(lower, lower, lower))\n", " return sum(max(candidates, key=score))\n", "\n", - "answer(euler_59, 107359)" + "run(euler_59)" ] }, { @@ -2954,7 +2788,7 @@ { "data": { "text/plain": [ - " 60: Prime pair sets 875 msec ⇒ 26033 āœ… " + " 60: Prime pair sets 825 msec ⇒ 26033 āœ… " ] }, "execution_count": 64, @@ -2963,9 +2797,6 @@ } ], "source": [ - "import warnings\n", - "warnings.filterwarnings('default') # Show all warnings\n", - " \n", "def euler_60(N=5, limit=9000) -> int:\n", " \"\"\"Prime pair sets: Find the lowest sum for a set of five primes for which any two concatenate to produce another prime.\"\"\"\n", " # I guessed that limit=9000 would be high enough, and as it turned out, it worked.\n", @@ -2987,7 +2818,7 @@ " \"\"\"Do p and q concatenate to form a prime, in both orders?\"\"\"\n", " return isprime(int(p + q)) and isprime(int(q + p))\n", "\n", - "answer(euler_60, 26033)" + "run(euler_60)" ] }, { @@ -3050,12 +2881,12 @@ " for n in P\n", " if (nums == []) or cyclical_numbers(nums[-1], n)\n", " if N > 1 or cyclical_numbers(n, nums[0]))\n", - "\n", + " \n", "def cyclical_numbers(n, m) -> bool:\n", " \"\"\"True if n ends with the same two digits that m starts with.\"\"\"\n", " return m // 100 == n % 100\n", "\n", - "answer(euler_61, 28684)" + "run(euler_61)" ] }, { @@ -3093,7 +2924,7 @@ " if len(D[key]) == N:\n", " return min(D[key])**3\n", "\n", - "answer(euler_62, 127035954683)" + "run(euler_62)" ] }, { @@ -3130,7 +2961,159 @@ " for b in range(1, 10) \n", " for n in range(1, 100))\n", "\n", - "answer(euler_63, 49)" + "run(euler_63)" + ] + }, + { + "cell_type": "markdown", + "id": "aa68bf7e-0cf1-4428-b686-8d844ca1ee65", + "metadata": {}, + "source": [ + "## [Problem 64](https://projecteuler.net/problem=64)\n", + "\n", + "I had to look up [periodic continued fractions](https://en.wikipedia.org/wiki/Periodic_continued_fraction) on Wikipedia, and consult the \"Canonical form and repetend\" section for this one. From there it is just following the formula, using Wikipedia's notation, " + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "08b1bb09-8ae5-4518-8504-5727376ca3a4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + " 64: Odd Period Square Roots 19 msec ⇒ 1322 āœ… " + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def euler_64(limit=10_000):\n", + " \"\"\"Odd Period Square Roots: How many continued fractions for irrational square roots \n", + " of N <= 10,000 have an odd period?\"\"\"\n", + " return quantify(square_root_period(N) % 2 == 1 \n", + " for N in integers(1, limit) if not is_perfect_square(N))\n", + "\n", + "def square_root_period(n: int) -> int:\n", + " \"\"\"The period length of the continued fraction of irrational sqrt(n).\"\"\"\n", + " a0 = isqrt(n)\n", + " m, d, a = 0, 1, a0\n", + " for period in integers(start=0):\n", + " if a == 2 * a0:\n", + " return period\n", + " m = a * d - m\n", + " d = (n - m * m) // d\n", + " a = (a0 + m) // d \n", + "\n", + "run(euler_64)" + ] + }, + { + "cell_type": "markdown", + "id": "9ed51e9f-d658-443a-9a2a-255c429bd076", + "metadata": {}, + "source": [ + "## [Problem 65](https://projecteuler.net/problem=65)\n", + "\n", + "As in the previous problem, we just follow the formula. I use `p_n_1` for *p**n*-1" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "5d416d38-f841-467b-8f2a-6dc6492d65ee", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + " 65: Convergents of e 0 msec ⇒ 272 āœ… " + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def euler_65(N=100):\n", + " \"\"\"Convergents of e: Find the sum of digits in the numerator of the 100th convergent of the continued fraction for e.\"\"\"\n", + " p_n_2, p_n_1 = 0, 1\n", + " for a_n in islice(e_coefficients(), 0, N):\n", + " p_n = a_n * p_n_1 + p_n_2\n", + " p_n_2, p_n_1 = p_n_1, p_n\n", + " return digitsum(p_n_1)\n", + "\n", + "def e_coefficients() -> Iterable[int]:\n", + " \"\"\"Infinite stream of coefficients of continued fraction of e: \n", + " 2, 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8, 1, 1, 10, 1, 1, ...\"\"\"\n", + " yield from (2, 1)\n", + " for i in integers(start=1): # continue forever\n", + " yield from (2 * i, 1, 1)\n", + "\n", + "run(euler_65)" + ] + }, + { + "cell_type": "markdown", + "id": "8fd90990-d966-4011-8fff-2ac2a02d8d6a", + "metadata": {}, + "source": [ + "## [Problem 66](https://projecteuler.net/problem=66)\n", + "\n", + "A bit of research leads to the Wikipedia entry for [Pell's equation](https://en.wikipedia.org/wiki/Pell%27s_equation): a Diophantine equation (one for which only integer solutions are allowed) of the form *x*2 - *Dy*2 = 1. Again, follow the formula." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "e3c33014-a1cd-4e2c-a5ae-57b1157575a2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + " 66: Diophantine Equation 2 msec ⇒ 661 āœ… " + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def euler_66(N=1000):\n", + " \"\"\"Diophantine Equation: For x^2 - Dy^2 = 1, find the value of D in minimal solutions of x \n", + " for which the largest value of x is obtained.\"\"\"\n", + " return max(integers(2, N), key=lambda D: pell_minimal_solution(D)[0])\n", + "\n", + "def pell_minimal_solution(D: int) -> tuple[int, int]:\n", + " \"\"\"The minimal (x, y) pair that solve x^2 - Dy^2 = 1.\n", + " Or return None if D is a perfect square.\"\"\"\n", + " a0 = int(isqrt(D))\n", + " if a0 ** 2 == D:\n", + " return (-inf, -inf) # Perfect squares have no valid solutions; use this\n", + " \n", + " m, denom, a = 0, 1, a0\n", + " p_n_2, p_n_1 = 0, 1\n", + " q_n_2, q_n_1 = 1, 0\n", + " \n", + " while True:\n", + " p = a * p_n_1 + p_n_2\n", + " q = a * q_n_1 + q_n_2\n", + " if p ** 2 - D * q ** 2 == 1:\n", + " return p, q\n", + " p_n_2, p_n_1 = p_n_1, p\n", + " q_n_2, q_n_1 = q_n_1, q\n", + " m = a * denom - m\n", + " denom = (D - m * m) // denom\n", + " a = (a0 + m) // denom\n", + "\n", + "run(euler_66)" ] }, { @@ -3143,30 +3126,30 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 71, "id": "cell-0154", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 67: Maximum path sum II 4 msec ⇒ 7273 āœ… " + " 67: Maximum path sum II 1 msec ⇒ 7273 āœ… " ] }, - "execution_count": 68, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_67(path=Path(\"p067_triangle.txt\")):\n", + "def euler_67(data=DATA[67]):\n", " \"\"\"Maximum path sum II: Find the maximum total from top to bottom in the file, \n", " which contains a triangle with one-hundred rows.\"\"\"\n", " # See euler_18 where `maxroute_in_triangle` is defined. \n", - " # I used @cache there, so it is fast enough to use here.\n", - " return maxroute_in_triangle(parse_matrix(read(path)))\n", + " # I used @cache there, so it is fast enough to use again here.\n", + " return maxroute_in_triangle(parse_matrix(data))\n", "\n", - "answer(euler_67, 7273)" + "run(euler_67)" ] }, { @@ -3181,17 +3164,17 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 72, "id": "714da3ca-21aa-4fa0-8afd-f40bf13d38df", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 68: Magic 5-gon ring 23 msec ⇒ 6531031914842725 āœ… " + " 68: Magic 5-gon ring 18 msec ⇒ 6531031914842725 āœ… " ] }, - "execution_count": 69, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } @@ -3211,7 +3194,7 @@ " if A+a+b == B+b+c == C+c+d == D+d+e == E+e+a:\n", " yield concat((A, a, b, B, b, c, C, c, d, D, d, e, E, e, a))\n", "\n", - "answer(euler_68, 6531031914842725)" + "run(euler_68)" ] }, { @@ -3224,17 +3207,17 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 73, "id": "cell-0156", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 69: Totient maximum 108 msec ⇒ 510510 āœ… " + " 69: Totient maximum 102 msec ⇒ 510510 āœ… " ] }, - "execution_count": 70, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } @@ -3257,7 +3240,7 @@ " phi_n = quantify(all(gcd(i, f)==1 for f in factors) for i in range(1,n))\n", " return n / phi_n, n, phi_n, factors\n", "\n", - "answer(euler_69, 510510)" + "run(euler_69)" ] }, { @@ -3272,17 +3255,17 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 74, "id": "61177e1b-a397-4085-8a73-80c563b1e06b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 70: Totient Permutation 31 msec ⇒ 8319823 āœ… " + " 70: Totient Permutation 28 msec ⇒ 8319823 āœ… " ] }, - "execution_count": 71, + "execution_count": 74, "metadata": {}, "output_type": "execute_result" } @@ -3298,10 +3281,10 @@ " if p * q < limit and is_permutation(p * q, φ(p, q)))\n", " return n\n", "\n", - "def φ(p, q) -> int: return (p - 1) * (q - 1)\n", - "def ratio(p, q) -> float: return (p * q) / φ(p, q)\n", + "def φ(p, q) -> int: return (p - 1) * (q - 1)\n", + "def ratio(p, q) -> float: return (p * q) / φ(p, q)\n", "\n", - "answer(euler_70, 8319823)" + "run(euler_70)" ] }, { @@ -3322,7 +3305,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 75, "id": "cell-0157", "metadata": {}, "outputs": [ @@ -3332,7 +3315,7 @@ " 71: Ordered fractions 0 msec ⇒ 428570 āœ… " ] }, - "execution_count": 72, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -3344,7 +3327,7 @@ " # This is a guess, but it worked:\n", " return 3 * (limit // 7) - 1\n", "\n", - "answer(euler_71, 428570)" + "run(euler_71)" ] }, { @@ -3357,17 +3340,17 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 76, "id": "6b34a0be-0de1-46dc-8622-67de22376b93", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 72: Counting fractions 379 msec ⇒ 303963552391 āœ… " + " 72: Counting fractions 285 msec ⇒ 303963552391 āœ… " ] }, - "execution_count": 73, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } @@ -3389,7 +3372,7 @@ " # Sum of all totients (starting from 2)\n", " return sum(φ[2:])\n", "\n", - "answer(euler_72, 303_963_552_391)" + "run(euler_72)" ] }, { @@ -3402,17 +3385,17 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 77, "id": "cell-0158", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 73: Counting fractions in a range 756 msec ⇒ 7295372 āœ… " + " 73: Counting fractions in a range 737 msec ⇒ 7295372 āœ… " ] }, - "execution_count": 74, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -3428,7 +3411,7 @@ " for n in range(1, D // 2 + 1)\n", " for d in range(2 * n + 1, min(3 * n, D + 1)))\n", "\n", - "answer(euler_73, 7295372)" + "run(euler_73)" ] }, { @@ -3441,7 +3424,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 78, "id": "cell-0159", "metadata": { "editable": true, @@ -3454,10 +3437,10 @@ { "data": { "text/plain": [ - " 74: Digit factorial chains 536 msec ⇒ 402 āœ… " + " 74: Digit factorial chains 506 msec ⇒ 402 āœ… " ] }, - "execution_count": 75, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -3485,7 +3468,7 @@ "\n", " return quantify(chain_length(n) == 60 for n in range(1, N))\n", "\n", - "answer(euler_74, 402)" + "run(euler_74)" ] }, { @@ -3498,17 +3481,17 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 79, "id": "cell-0161", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 75: Singular integer right triangles 99 msec ⇒ 161667 āœ… " + " 75: Singular integer right triangles 89 msec ⇒ 161667 āœ… " ] }, - "execution_count": 76, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -3532,7 +3515,7 @@ " ntriangles[p]+=1\n", " return ntriangles.count(1)\n", "\n", - "answer(euler_75, 161667)" + "run(euler_75)" ] }, { @@ -3545,7 +3528,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 80, "id": "cell-0162", "metadata": {}, "outputs": [ @@ -3555,7 +3538,7 @@ " 76: Counting summations 1 msec ⇒ 190569291 āœ… " ] }, - "execution_count": 77, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -3574,7 +3557,7 @@ " 0 if (n < 0 or n < k) else\n", " npartitions(n, k+1) + npartitions(n-k, k))\n", "\n", - "answer(euler_76, 190569291)" + "run(euler_76)" ] }, { @@ -3587,7 +3570,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 81, "id": "cell-0163", "metadata": {}, "outputs": [ @@ -3597,14 +3580,15 @@ " 77: Prime summations 1 msec ⇒ 71 āœ… " ] }, - "execution_count": 78, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def euler_77(N=5000):\n", - " \"\"\"Prime summations: What is the first integer that can be written as the sum of primes in over five thousand different ways?\"\"\"\n", + " \"\"\"Prime summations: What is the first integer that can be written as the sum of primes \n", + " in over five thousand different ways?\"\"\"\n", " return first(n for n in integers(start=2) \n", " if prime_sum_ways(n) > N)\n", "\n", @@ -3617,7 +3601,7 @@ " # Let's not blow the recursion stack: consider k copies of p in one recursion, not k\n", " sum(prime_sum_ways(n - kp, i + 1) for kp in range(0, n + 1, p)))\n", "\n", - "answer(euler_77, 71)" + "run(euler_77)" ] }, { @@ -3632,17 +3616,17 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 82, "id": "a4d57e1f-3b9e-4d6f-9360-304f8fa0df2f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 78: Coin partitions 807 msec ⇒ 55374 āœ… " + " 78: Coin partitions 725 msec ⇒ 55374 āœ… " ] }, - "execution_count": 79, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -3672,7 +3656,7 @@ " return first(n for n in range(1, limit) \n", " if p_mod_M(n) == 0)\n", "\n", - "answer(euler_78, 55374)" + "run(euler_78)" ] }, { @@ -3687,32 +3671,32 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 83, "id": "b18a810f-7cd6-4df8-94bc-c916c85ef340", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 79: Passcode Derivation 6 msec ⇒ 73162890 āœ… " + " 79: Passcode Derivation 11 msec ⇒ 73162890 āœ… " ] }, - "execution_count": 80, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_79(path=Path('0079_keylog.txt')):\n", + "def euler_79(data=DATA[79]):\n", " \"\"\"Passcode Derivation: A security method is to ask the user for three random characters from a passcode, \n", " e.g. 2nd, 3rd, and 5th. `keylog` contains fifty successful login attempts. Given that the three characters \n", " are always asked for in order (e.g. never 5th, 3rd, 2nd), determine the shortest possible secret passcode.\"\"\"\n", - " keylog = read(path).split()\n", + " keylog = data.split()\n", " matchers = [re.compile('.*'.join(login)).search for login in keylog]\n", " return first(int(n) for n in map(concat, permutations(set(concat(keylog))))\n", " if all(match(n) for match in matchers))\n", "\n", - "answer(euler_79, 73162890)" + "run(euler_79)" ] }, { @@ -3725,7 +3709,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 84, "id": "cell-0166", "metadata": {}, "outputs": [ @@ -3735,7 +3719,7 @@ " 80: Square root digital expansion 1 msec ⇒ 40886 āœ… " ] }, - "execution_count": 81, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } @@ -3745,19 +3729,14 @@ " \"\"\"Square root digital expansion: For the first one hundred natural numbers, find the total of the digital sums\n", " of the first one hundred decimal digits for all the irrational square roots.\"\"\"\n", " decimal.getcontext().prec = N+3\n", - " return sum(digital_sum(decimal.Decimal(i).sqrt(), N)\n", - " for i in ints(1, N) if not is_perfect_square(i))\n", + " return sum(digitsum(first_N_digits(decimal.Decimal(i).sqrt(), N))\n", + " for i in integers(1, N) if not is_perfect_square(i))\n", "\n", - "def digital_sum(x, N) -> int:\n", - " \"\"\"The sum of the first N digits in the number x (including digits on both side of decimal point, if any).\"\"\"\n", - " s = str(x).replace('.', '')[:N] # Get rid of the decimal point\n", - " return digitsum(int(s))\n", + "def first_N_digits(d, N) -> int:\n", + " \"\"\"The first N digits in the number d (including digits on both side of decimal point, if any), as an int.\"\"\"\n", + " return int(str(d).replace('.', '')[:N]) # Get rid of the decimal point\n", "\n", - "def is_perfect_square(x: int) -> bool: \n", - " \"\"\"Is x a perfect square? It is if its square root is an integer.\"\"\"\n", - " return sqrt(x).is_integer()\n", - "\n", - "answer(euler_80, 40886)" + "run(euler_80)" ] }, { @@ -3778,26 +3757,26 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 85, "id": "cell-0167", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 81: Path Sum, Two ways 3 msec ⇒ 427337 āœ… " + " 81: Path Sum, Two ways 2 msec ⇒ 427337 āœ… " ] }, - "execution_count": 82, + "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_81(path=Path(\"p081_matrix.txt\")):\n", + "def euler_81(data=DATA[81]):\n", " \"\"\"Path Sum, Two ways: Find the minimal path sum from the top left to the bottom right,\n", " only moving right and down.\"\"\"\n", - " return minroute2ways(parse_matrix(read(path)))\n", + " return minroute2ways(parse_matrix(data))\n", "\n", "def minroute2ways(matrix) -> int:\n", " \"\"\"Find the sum of the minimal route through matrix, moving 2 ways.\"\"\"\n", @@ -3811,7 +3790,7 @@ " # Start at end point, work backwards towards (0, 0)\n", " return cost(len(matrix) - 1, len(matrix[0]) - 1) \n", "\n", - "answer(euler_81, 427337)" + "run(euler_81)" ] }, { @@ -3824,25 +3803,25 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 86, "id": "eaff80dd-0329-4271-a54f-7245fa3276bc", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 82: Path Sum, Three Ways 388 msec ⇒ 260324 āœ… " + " 82: Path Sum, Three Ways 374 msec ⇒ 260324 āœ… " ] }, - "execution_count": 83, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_82(path=Path(\"0082_matrix.txt\")):\n", + "def euler_82(data=DATA[82]):\n", " \"\"\"Path Sum, Three Ways: Now we can go up or down in a column; then move right.\"\"\"\n", - " return minroute3ways(parse_matrix(read(path)))\n", + " return minroute3ways(parse_matrix(data))\n", "\n", "def minroute3ways(matrix) -> int:\n", " \"\"\"Find sum of minimum route through matrix, moving 3 ways (right and up/down).\n", @@ -3864,7 +3843,7 @@ " section = range(min(r1, r2), max(r1, r2) + 1)\n", " return sum(matrix[r][c] for r in section)\n", "\n", - "answer(euler_82, 260324)" + "run(euler_82)" ] }, { @@ -3877,25 +3856,25 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 87, "id": "a5eb2986-c712-4a1c-8468-84459f89a737", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 83: Path Sum, Four Ways 6 msec ⇒ 425185 āœ… " + " 83: Path Sum, Four Ways 5 msec ⇒ 425185 āœ… " ] }, - "execution_count": 84, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_83(path=Path(\"0083_matrix.txt\")):\n", + "def euler_83(data=DATA[83]):\n", " \"\"\"Path Sum, Four Ways: Now we can go in all four directions.\"\"\"\n", - " return minroute4ways(parse_matrix(read(path)))\n", + " return minroute4ways(parse_matrix(data))\n", "\n", "def minroute4ways(matrix) -> int:\n", " \"\"\"Use Dijkstra's algorithm rather than @cache, to avoid infinite loops.\"\"\"\n", @@ -3920,7 +3899,7 @@ " return [(r + dr, c + dc) for (dr, dc) in ((-1, 0), (1, 0), (0, -1), (0, 1))\n", " if 0 <= r + dr < height and 0 <= c + dc < width]\n", "\n", - "answer(euler_83, 425185)" + "run(euler_83)" ] }, { @@ -3933,149 +3912,67 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 88, "id": "18003bf9-c1b2-4d74-a951-12c3238f65a1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 84: Monopoly odds 430 msec ⇒ 101524 āœ… " + " 84: Monopoly odds 44 msec ⇒ 101524 āœ… " ] }, - "execution_count": 85, + "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_84(sides=4, steps=million):\n", + "def euler_84(sides=4, steps=100_000):\n", " \"\"\"Monopoly odds: Find the most common 3 squares to land on in a Monopoly board,\n", - " assuming we are playing with dice with the given number of sides.\"\"\"\n", + " assuming we are playing with dice with the given number of sides.\n", + " I guessed that 100,000 simulation steps would be enough to give accurate rankings.\"\"\"\n", " counts = Counter(monopoly(sides, steps))\n", " return int(concat(f'{square:02d}' for (square, _) in counts.most_common(3)))\n", "\n", - "Square = int # Data type for a square on the board: the square number\n", - "Deck = Iterator # Data type for a deck of cards; you can take the `next` one.\n", + "Square = int # Data type for a square on the board\n", "\n", "def monopoly(sides: int, steps: int) -> Iterable[Square]:\n", " \"\"\"Simulate a Monopoly game for `N` dice rolls, yielding the squares visited.\"\"\"\n", - " square = GO # Current location on board\n", - " doubles = 0 # Count of consecutive doubles rolled\n", + " here = GO # Current location on board\n", + " doubles = 0 # Count of consecutive doubles rolled\n", " for _ in range(steps):\n", - " yield square\n", + " yield here\n", " d1, d2 = random.choices(range(1, sides + 1), k=2) # Roll two dice\n", - " square = (square + d1 + d2) % len(board) # Move ahead, maybe pass Go\n", + " here = (here + d1 + d2) % 40 # Move ahead, maybe pass Go\n", " doubles = (doubles + 1 if d1 == d2 else 0)\n", - " if square == G2J or doubles == 3: # Go to Jail\n", + " if here == G2J or doubles == 3: # Go to Jail\n", " doubles = 0\n", - " square = JAIL \n", - " elif square in (CC1, CC2, CC3): # Community Chest card\n", - " square = do_card(next(CC_cards), square)\n", - " elif square in (CH1, CH2, CH3): # Chance card\n", - " square = do_card(next(CH_cards), square)\n", + " here = JAIL \n", + " elif here in (CC1, CC2, CC3): # Community Chest card\n", + " here = do_card(next(CC_cards), here)\n", + " elif here in (CH1, CH2, CH3): # Chance card\n", + " here = do_card(next(CH_cards), here)\n", "\n", "(GO, A1, CC1, A2, T1, R1, B1, CH1, B2, B3,\n", " JAIL, C1, U1, C2, C3, R2, D1, CC2, D2, D3, \n", " FP, E1, CH2, E2, E3, R3, F1, F2, U2, F3, \n", - " G2J, G1, G2, CC3, G3, R4, CH3, H1, T2, H2) = board = range(40)\n", + " G2J, G1, G2, CC3, G3, R4, CH3, H1, T2, H2) = range(40)\n", "\n", "RRs = {R1, R2, R3, R4}\n", "\n", - "def deck(cards: list) -> Deck:\n", - " \"\"\"Make a deck of cards: an infinite iterable cycling through the (shuffled) cards.\"\"\"\n", - " random.shuffle(cards)\n", - " return cycle(cards)\n", - " \n", - "CC_cards = deck([None] * 14 + [GO, JAIL])\n", - "CH_cards = deck([None] * 6 + [GO, JAIL, C1, E3, H2, R1, RRs, RRs, {U1, U2}, -3])\n", + "CC_cards = cycle(shuffled([None] * 14 + [GO, JAIL]))\n", + "CH_cards = cycle(shuffled([None] * 6 + [GO, JAIL, C1, E3, H2, R1, RRs, RRs, {U1, U2}, -3]))\n", "\n", - "def do_card(card, square: Square) -> Square:\n", - " \"\"\"Update location from `square` to new location, depending on what `card` says.\"\"\"\n", - " return ( square if card is None # Don't move (card is about money)\n", - " else square - 3 if card == -3 # Go back 3 spaces\n", - " else card if isinstance(card, Square) # Go to square named on card\n", - " else min([s for s in card if s > square] or card)) # Advance to nearest\n", + "def do_card(card, here: Square) -> Square:\n", + " \"\"\"Starting from `here`, return new location, depending on what `card` says.\"\"\"\n", + " match card:\n", + " case -3: return here - 3\n", + " case Square(): return card\n", + " case set(): return min([s for s in card if s > here] or card)\n", + " case None: return here\n", "\n", - "answer(euler_84, 101524)" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "id": "cell-0168", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - " 84: Monopoly odds 615 msec ⇒ 101524 āœ… " - ] - }, - "execution_count": 86, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def euler_84(sides=4, steps=million):\n", - " \"\"\"Monopoly odds: Find the most common 3 squares to land on in a Monopoly board,\n", - " assuming we are playing with dice with the given number of sides.\"\"\"\n", - " counts = monopoly(sides, steps)\n", - " return int(concat(f'{square:02d}' for (square, count) in counts.most_common(3)))\n", - "\n", - "# MONOPOLY\n", - "\n", - "(GO, A1, CC1, A2, T1, R1, B1, CH1, B2, B3,\n", - " JAIL, C1, U1, C2, C3, R2, D1, CC2, D2, D3, \n", - " FP, E1, CH2, E2, E3, R3, F1, F2, U2, F3, \n", - " G2J, G1, G2, CC3, G3, R4, CH3, H1, T2, H2) = board = range(40)\n", - "\n", - "Deck = deque\n", - "\n", - "CC_deck = Deck(shuffled([GO, JAIL] + 14 * [None]))\n", - "\n", - "CH_deck = Deck(shuffled([GO, JAIL, C1, E3, H2, R1, {R1, R2, R3, R4}, {R1, R2, R3, R4}, {U1, U2}, -3] + 6 * [None]))\n", - "\n", - "def monopoly(sides, steps) -> Counter:\n", - " \"\"\"Simulate given number of steps of Monopoly game, \n", - " yielding the number of the current square after each step.\"\"\"\n", - " goto(GO)\n", - " counts = Counter() #[0] * len(board)\n", - " doubles = 0\n", - " for _ in range(steps):\n", - " d1, d2 = random.randint(1, sides), random.randint(1, sides)\n", - " doubles = doubles + 1 if d1 == d2 else 0\n", - " goto(here + d1 + d2)\n", - " if here == G2J or doubles == 3:\n", - " doubles = 0\n", - " goto(JAIL)\n", - " elif here in (CC1, CC2, CC3):\n", - " do_card(CC_deck)\n", - " elif here in (CH1, CH2, CH3):\n", - " do_card(CH_deck)\n", - " if doubles == 0:\n", - " counts[here] += 1\n", - " return counts\n", - "\n", - "def goto(square) -> None:\n", - " global here\n", - " here = square % len(board) \n", - "\n", - "def do_card(deck) -> None:\n", - " \"\"\"Take the top card from deck and do what it says.\"\"\"\n", - " card = deck[0] # The top card\n", - " deck.rotate(-1) # Move top card to bottom of deck\n", - " if card == None: # Don't move\n", - " pass\n", - " elif card == -3: # Go back 3 spaces\n", - " goto(here - 3)\n", - " elif isinstance(card, set): # Advance to next railroad or utility\n", - " goto(min({place for place in card if place > here} or card))\n", - " else: # Go to destination named on card\n", - " goto(card)\n", - "\n", - "answer(euler_84, 101524)" + "run(euler_84)" ] }, { @@ -4088,29 +3985,28 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 89, "id": "cell-0169", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 85: Counting rectangles 807 msec ⇒ 2772 āœ… " + " 85: Counting rectangles 768 msec ⇒ 2772 āœ… " ] }, - "execution_count": 87, + "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def euler_85(N=2*million):\n", - " \"\"\"Counting rectangles: find the area of the matrix that contains a number of sub-rectangles closest to 2 million.\"\"\"\n", + " \"\"\"Counting rectangles: find the area of the grid that contains a number of sub-rectangles closest to 2 million.\"\"\"\n", " # By experimentation, nrectangles(100, 100) = 25,502,500, so\n", " # I'll limit one dimension to cube root of N, and increment the other dimension until nrectangles > N+delta\n", - " # So I guess that \n", " best = (N-1, 1, 1) # delta, i, j\n", - " for i in ints(1, int(N ** (1/3))):\n", + " for i in integers(1, int(N ** (1/3))):\n", " for j in integers(start=1):\n", " n = nrectangles(i, j)\n", " delta = abs(n - N)\n", @@ -4127,9 +4023,9 @@ " def nrectangles_of_size(a, b): \n", " \"\"\"How many rectangles of exactly size axb fit into the big rectangle of size ixj?\"\"\"\n", " return (i + 1 - a) * (j + 1 - b)\n", - " return sum(nrectangles_of_size(a, b) for a in ints(1, i) for b in ints(1, j))\n", + " return sum(nrectangles_of_size(a, b) for a in integers(1, i) for b in integers(1, j))\n", "\n", - "answer(euler_85, 2772)" + "run(euler_85)" ] }, { @@ -4139,12 +4035,14 @@ "source": [ "## [Problem 86](https://projecteuler.net/problem=86)\n", "\n", + "If you fold the cuboid flat, you see that the shortest path is the hypotenuse of a right triangle with one side equal to *M* (one of the dimensions of the cuboid) and the other side equal *K* + *L* (the sum of the other two dimensions). So I don't have to iterate over all *K* and *L* separately; I can iterate over their sum, *S*.\n", + "\n", "![](euler_86c.png)" ] }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 90, "id": "547f3d89-a358-4c88-a0a8-703c788c4de5", "metadata": { "deletable": true, @@ -4158,18 +4056,18 @@ { "data": { "text/plain": [ - " 86: Cuboid Route 242 msec ⇒ 1818 āœ… " + " 86: Cuboid Route 279 msec ⇒ 1818 āœ… " ] }, - "execution_count": 88, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def euler_86(limit=1_000_000):\n", - " \"\"\"Cuboid Route: Find the least value of M such that the number of solutions to the problem of\n", - " finding integral shortest paths between opposite corners on a cuboid first exceeds one million.\"\"\"\n", + " \"\"\"Cuboid Route: Find the least value of M such that the number of integral shortest paths \n", + " between opposite corners on a cuboid with all sides <= M first exceeds one million.\"\"\"\n", " solutions = 0\n", " for M in integers(start=1):\n", " solutions += cuboids_with_long_side(M)\n", @@ -4187,7 +4085,7 @@ " for S in range(2, 2 * M)\n", " if is_perfect_square(M ** 2 + S ** 2))\n", "\n", - "answer(euler_86, 1818)" + "run(euler_86)" ] }, { @@ -4200,22 +4098,32 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 91, "id": "c2391f60-280d-48dc-b0c8-a8fa8123d572", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 87: Prime power triples 559 msec ⇒ 1097343 āœ… " + " 87: Prime power triples 501 msec ⇒ 1097343 āœ… " ] }, - "execution_count": 89, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "def euler_87(limit=50*million):\n", + " \"\"\"Prime power triples: How many numbers below fifty million can be expressed as the sum of\n", + " a prime square, prime cube, and prime fourth power?\"\"\"\n", + " # I can enumerate all possibilities, but I need to form a set, not a list, of numbers, since \n", + " # some numbers can be made multiple ways, e.g. 2**2 + 5**3 + 2**4 == 11**2 + 2**3 + 2**4\n", + " return len({p ** 4 + q ** 3 + r ** 2\n", + " for p in primerange(limit ** (1/4))\n", + " for q in primerange(int((limit - p ** 4) ** (1/3)))\n", + " for r in primerange(isqrt(limit - p ** 4 - q ** 3))})\n", + "\n", "def euler_87(limit=50*million):\n", " \"\"\"Prime power triples: How many numbers below fifty million can be expressed as the sum of\n", " a prime square, prime cube, and prime fourth power?\"\"\"\n", @@ -4226,7 +4134,7 @@ " for q in primerange((limit - p ** 2) ** (1/3))\n", " for r in primerange((limit - p ** 2 - q ** 3) ** (1/4))})\n", "\n", - "answer(euler_87, 1097343)" + "run(euler_87)" ] }, { @@ -4241,17 +4149,17 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 92, "id": "6eeb24d7-604a-4946-ba1a-ff64c1980e57", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 88: Product-sum Numbers 76 msec ⇒ 7587457 āœ… " + " 88: Product-sum Numbers 74 msec ⇒ 7587457 āœ… " ] }, - "execution_count": 90, + "execution_count": 92, "metadata": {}, "output_type": "execute_result" } @@ -4279,7 +4187,7 @@ " find_factors(first_factor, first_factor, 1, first_factor)\n", " return sum(set(min_product_sum[2:k_limit+1]))\n", "\n", - "answer(euler_88, 7587457)" + "run(euler_88)" ] }, { @@ -4292,26 +4200,26 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 93, "id": "ab9986fe-616d-4828-9821-0a9796c0c74f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 89: Roman numerals 4 msec ⇒ 743 āœ… " + " 89: Roman numerals 3 msec ⇒ 743 āœ… " ] }, - "execution_count": 91, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_89(path=Path('p089_roman.txt')):\n", + "def euler_89(data=DATA[89]):\n", " \"\"\"Roman numerals: Find the number of characters saved by writing each of the roman numerals\n", " in roman.txt in their minimal form (e.g. as IV rather than IIII).\"\"\"\n", - " original = read(path).split()\n", + " original = data.split()\n", " efficient = [roman_from_int(int_from_roman(r)) for r in original]\n", " return len(concat(original)) - len(concat(efficient))\n", "\n", @@ -4327,7 +4235,7 @@ " return '' if n == 0 else next(r + roman_from_int(n - i) \n", " for r, i in roman.items() if i <= n)\n", "\n", - "answer(euler_89, 743)" + "run(euler_89)" ] }, { @@ -4340,7 +4248,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 94, "id": "cell-0172", "metadata": {}, "outputs": [ @@ -4350,7 +4258,7 @@ " 90: Cube digit pairs 9 msec ⇒ 1217 āœ… " ] }, - "execution_count": 92, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -4367,7 +4275,7 @@ " for (t, d) in targets)\n", " for A, B in combinations(dice, 2))\n", "\n", - "answer(euler_90, 1217)" + "run(euler_90)" ] }, { @@ -4388,17 +4296,17 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 95, "id": "614d95dc-eefa-4001-b9ac-185e2dff2390", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 91: Right Triangles with Integer Coordinates 769 msec ⇒ 14234 āœ… " + " 91: Right Triangles with Integer Coordinates 759 msec ⇒ 14234 āœ… " ] }, - "execution_count": 93, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -4421,7 +4329,7 @@ " return (a2 + b2 == c2) or (b2 + c2 == a2) or (c2 + a2 == b2)\n", "\n", "\n", - "answer(euler_91, 14234)" + "run(euler_91)" ] }, { @@ -4438,17 +4346,17 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 96, "id": "0ce6dd51-9058-4180-9c72-bd87d8df8a53", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 92: Square digit chains 34 msec ⇒ 8581146 āœ… " + " 92: Square digit chains 38 msec ⇒ 8581146 āœ… " ] }, - "execution_count": 94, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -4468,7 +4376,7 @@ " return n \n", "\n", "def canonical_number_counts(Ndigits=7) -> dict[int: int]:\n", - " \"\"\"A dict of {canonical_number: how many integers in [1, 10**digits) it is a permutation of}.\n", + " \"\"\"A dict of {canonical_number: how many integers in [1, 10**Ndigits) it is a permutation of}.\n", " A canonical number is the minimum of all the permutations.\n", " The count is the multinomial coefficient: Ndigits! / āˆ digit_count!\n", " For example, {3456789: 720, 1111111: 1, 1111123: 42}.\"\"\"\n", @@ -4476,7 +4384,7 @@ " return {int(concat(digits)): factorial(Ndigits) // prod(map(factorial, Counter(digits).values()))\n", " for digits in islice(digit_combos, 1, None)} # islice to skip (\"0\", ...)\n", "\n", - "answer(euler_92, 8581146)" + "run(euler_92)" ] }, { @@ -4489,17 +4397,17 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 97, "id": "cell-0174", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 93: Arithmetic expressions 39 msec ⇒ 1258 āœ… " + " 93: Arithmetic expressions 45 msec ⇒ 1258 āœ… " ] }, - "execution_count": 95, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -4510,7 +4418,7 @@ " it is possible to form different positive integer targets. Find the set of four distinct digits,\n", " a < b < c < d, for which the longest set of consecutive positive integers, 1 to n, can be obtained,\n", " giving your answer as a string: abcd.\"\"\"\n", - " return int(concat(max(combinations(ints(1, 9), 4), key=expressiveness)))\n", + " return int(concat(max(combinations(integers(1, 9), 4), key=expressiveness)))\n", "\n", "def expressiveness(Nums):\n", " \"\"\"How many consecutive integers, 1 to n, can be made from the set Nums?\"\"\"\n", @@ -4539,7 +4447,135 @@ " if a != 0: result.add(b / a)\n", " return result\n", "\n", - "answer(euler_93, 1258)" + "run(euler_93)" + ] + }, + { + "cell_type": "markdown", + "id": "39734478-25d2-4ce7-8caa-ea754b448244", + "metadata": {}, + "source": [ + "## [Problem 94](https://projecteuler.net/problem=94)\n", + "\n", + "An almost-equilateral isosceles triangle with two equal sides $a$ and base $b$ has height $h = \\sqrt{a^2 - (\\frac{b}{2})^2} = \\frac{1}{2} \\sqrt{4a^2 - b^2}.$\n", + "\n", + "So the area is $\\frac{1}{2} b h = \\frac{b}{4} \\sqrt{4a^2 - b^2}$.\n", + "\n", + "For this to be an integer, $4a^2 - b^2$ must be a perfect square, and when multiplied by $b$ must be divisible by 4.\n", + "\n", + "It is simple to write code to test all almost-equilateral triangles (*a*, *a*, *b*) for all *a* up to 1/3 the perimeter limit and *b* in *a*±1, Here I time the function for the less ambitious limit of a million:" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "id": "7348f10e-c358-40f0-8cba-8f690c5e95de", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 91.6 ms, sys: 1.45 ms, total: 93.1 ms\n", + "Wall time: 92.3 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "716032" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def euler_94_slow(limit=million):\n", + " \"\"\"Almost Equilateral Triangles: SLOW VERSION.\n", + " Find the sum of the perimeters of all almost equilateral triangles \n", + " with integral side lengths and area, and whose perimeters do not exceed one billion.\n", + " An almost equilateral triangle has sides of length n, n, and n±1.\"\"\"\n", + " return sum(a + a + b\n", + " for a in range(2, limit // 3 + 1)\n", + " for b in (a - 1, a + 1)\n", + " for radical in [4 * a * a - b * b]\n", + " if is_perfect_square(radical) and (radical * b) % 4 == 0)\n", + "\n", + "%time euler_94_slow(million)" + ] + }, + { + "cell_type": "markdown", + "id": "54eaaf65-da25-4ddb-bb36-80f3639569c7", + "metadata": {}, + "source": [ + "That's correct, but slow. Extrapolating to a limit of a billion would give us a run time of about two minutes. Before doing problem 64, I would have had no idea what else to do and would have accepted the two-minute run time. Now I can see that this is something like Pell's equation, where we need to find integral solutions for a quadratic. But first we have to do a bunch more math. \n", + "\n", + "There are two cases, $b$ can be one more or one less than $a$. When $b = a + 1$:\n", + "\n", + "$$4a^2 - (a+1)^2 = 4a^2 - (a^2 + 2a + 1) = 3a^2 - 2a - 1$$\n", + "\n", + "We want this to be a perfect square, let's call it $y^2$:\n", + "\n", + "$$3a^2 - 2a - 1 = y^2$$\n", + "\n", + "To turn this into a Pell equation, multiply by 3 and add 4 to both sides, completing the square:\n", + "\n", + "$$(3a - 1)^2 - 4 = 3y^2$$\n", + "\n", + "Now if we let $x = 3a - 1$, we get the generalized Pell equation:\n", + "\n", + "$$x^2 - 3y^2 = 4$$\n", + "\n", + "I won't go through it here, but it turns out that for the case where $b = a - 1$, we get the same equation.\n", + "\n", + "I was going to write a function to find the fundamental solution, but I can solve it by inspection: when $y = 0$ and $x = 2$, we have $4 - 0 = 4$. Then we can use the Pell recurrence relations:\n", + "\n", + "$$x_{n+1} = 2x_n + 3y_n, \\quad y_{n+1} = x_n + 2y_n$$\n", + "\n", + "Now we have to map backwards from $x$ to get the value of $a = \\frac{x \\pm 1}{3}$, and add up perimeters, returning the total sum when the first perimeter exceeds the limit." + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "id": "59051a98-9c61-4285-867b-6268f6bab842", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + " 94: Almost Equilateral Triangles 0 msec ⇒ 518408346 āœ… " + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def euler_94(limit=1_000_000_000):\n", + " \"\"\"Almost Equilateral Triangles: Find the sum of the perimeters of all almost equilateral triangles \n", + " with integral side lengths and area, and whose perimeters do not exceed one billion.\n", + " An almost equilateral triangle has sides of length a, a, and a±1.\"\"\"\n", + " x, y = 2, 0 # Fundamental solution for x^2 - 3y^2 = 4 (by inspection)\n", + " sum_of_perimeters = 0\n", + " while True:\n", + " # Generate the next Pell solution with the recurrence relation\n", + " x, y = 2 * x + 3 * y, x + 2 * y\n", + " for Ī” in [-1, +1]:\n", + " if (x + Ī”) % 3 == 0: # a must be an integer, so x + Ī” must be divisible by 3\n", + " a = (x + Ī”) // 3\n", + " b = a + Ī”\n", + " perimeter = 2 * a + b\n", + " if perimeter > limit:\n", + " return sum_of_perimeters\n", + " elif a > 0 and b > 0:\n", + " sum_of_perimeters += perimeter\n", + " \n", + "run(euler_94)" ] }, { @@ -4560,17 +4596,17 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 100, "id": "73a5d5f2-620b-4010-9fc3-6737ac45ceed", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 95: Amicable chains 569 msec ⇒ 14316 āœ… " + " 95: Amicable chains 541 msec ⇒ 14316 āœ… " ] }, - "execution_count": 96, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -4611,7 +4647,7 @@ " d[2*divisor:limit:divisor] += divisor # vectorized assignment\n", " return d\n", "\n", - "answer(euler_95, 14316)" + "run(euler_95)" ] }, { @@ -4619,12 +4655,14 @@ "id": "hdr-0087", "metadata": {}, "source": [ - "## [Problem 96](https://projecteuler.net/problem=96)" + "## [Problem 96](https://projecteuler.net/problem=96)\n", + "\n", + "I cover Sudoku in [another notebook](https://github.com/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb)." ] }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 101, "id": "cell-0175", "metadata": { "editable": true, @@ -4637,20 +4675,20 @@ { "data": { "text/plain": [ - " 96: SuDoku 51 msec ⇒ 24702 āœ… " + " 96: SuDoku 52 msec ⇒ 24702 āœ… " ] }, - "execution_count": 97, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_96(path=Path(\"p096_sudoku.txt\")):\n", + "def euler_96(data=DATA[96]):\n", " \"\"\"SuDoku: Solve puzzles and return the sum of `scores`.\"\"\"\n", - " pictures = re.split(r\"Grid.*\", read(path))\n", - " return sum(sudoku_score(sudoku_search(constrain(parse_sudoku_matrix(picture))))\n", - " for picture in pictures if len(picture) >= 81)\n", + " grids = re.split(r\"Grid.*\", data)\n", + " return sum(sudoku_score(sudoku_search(constrain(parse_sudoku_matrix(grid))))\n", + " for grid in grids if len(grid) >= 81)\n", "\n", "# SUDOKU \n", "\n", @@ -4665,7 +4703,7 @@ " return tuple(a + b for a in A for b in B)\n", "\n", "rows = 'ABCDEFGHI'\n", - "cols = one_nine\n", + "cols = '123456789'\n", "squares = cross(rows, cols)\n", "all_boxes = [cross(rs, cs) for rs in ('ABC','DEF','GHI') for cs in ('123','456','789')]\n", "all_units = [cross(rows, c) for c in cols] + [cross(r, cols) for r in rows] + all_boxes\n", @@ -4683,7 +4721,7 @@ " \"\"\"Convert a Picture to a matrix.\"\"\"\n", " vals = re.findall(r\"[0-9]\", picture)\n", " assert len(vals) == 81\n", - " return {s: one_nine if (v == '0') else v\n", + " return {s: '123456789' if (v == '0') else v\n", " for s, v in zip(squares, vals)}\n", "\n", "def fill(matrix, s, d) -> Matrix:\n", @@ -4714,7 +4752,7 @@ "\n", "def constrain(matrix) -> Matrix:\n", " \"\"\"Propagate constraints on a copy of matrix to yield a new constrained matrix.\"\"\"\n", - " constrained: matrix = {s: one_nine for s in squares}\n", + " constrained: matrix = {s: '123456789' for s in squares}\n", " for s in matrix:\n", " d = matrix[s]\n", " if len(d) == 1:\n", @@ -4734,7 +4772,7 @@ " return solution\n", " return Fail\n", "\n", - "answer(euler_96, 24702)" + "run(euler_96)" ] }, { @@ -4747,7 +4785,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 102, "id": "cell-0177", "metadata": { "editable": true, @@ -4763,7 +4801,7 @@ " 97: Large non-Mersenne prime 0 msec ⇒ 8739992577 āœ… " ] }, - "execution_count": 98, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -4773,7 +4811,7 @@ " \"\"\"Large non-Mersenne prime: ???\"\"\"\n", " return (28433 * pow(2, 7830457, M) + 1) % M\n", "\n", - "answer(euler_97, 8739992577)" + "run(euler_97)" ] }, { @@ -4786,36 +4824,36 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 103, "id": "cell-0178", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - " 98: Anagramic squares 40 msec ⇒ 18769 āœ… " + " 98: Anagramic squares 37 msec ⇒ 18769 āœ… " ] }, - "execution_count": 99, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_98(path=Path('p098_words.txt')):\n", + "def euler_98(data=DATA[98]):\n", " \"\"\"Anagramic squares: What is the largest square number formed by any member of such a pair?.\"\"\"\n", - " words = literal_eval(read(path))\n", + " words = literal_eval(data)\n", " pairs = anagrams(words)\n", " L = max(len(w1) for (w1, w2) in pairs)\n", " squares = set(str(i**2) for i in range(int(10**(L/2.))))\n", - " squaresby = dict(groupby(squares, signature))\n", + " squaresby = bucket(squares, signature)\n", " return max(result[0]\n", " for (w1, w2) in anagrams(words)\n", " for result in squarepairs(w1, w2, squares, squaresby))\n", "\n", "def anagrams(words) -> Iterable[tuple[str, str]]:\n", " \"\"\"Generate all pairs of words that are anagrams of each other.\"\"\"\n", - " for group in groupby(words, key=lambda n: sorted_characters(n)).values():\n", + " for group in bucket(words, key=lambda n: sorted_characters(n)).values():\n", " yield from combinations(group, 2)\n", "\n", "def signature(word) -> str:\n", @@ -4836,7 +4874,7 @@ " i1, i2 = sorted([int(s1), int(s2)])\n", " yield (i2, i1, w1, w2)\n", "\n", - "answer(euler_98, 18769)" + "run(euler_98)" ] }, { @@ -4849,7 +4887,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 104, "id": "cell-0179", "metadata": {}, "outputs": [ @@ -4859,21 +4897,81 @@ " 99: Largest exponential 3 msec ⇒ 709 āœ… " ] }, - "execution_count": 100, + "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def euler_99(path=Path('0099_base_exp.txt')):\n", + "def euler_99(data=DATA[99]):\n", " \"\"\"Largest exponential: Which line number in `data` has the b,e pair that maximizes b ** e?\"\"\"\n", - " pairs = map(literal_eval, read(path).splitlines())\n", + " pairs = map(literal_eval, data.splitlines())\n", " # Use logarithms to prevent very big integers. \n", " _, line_number = max((e * log(b), line_number) \n", " for (line_number, (b, e)) in enumerate(pairs, 1))\n", " return line_number\n", "\n", - "answer(euler_99, 709)" + "run(euler_99)" + ] + }, + { + "cell_type": "markdown", + "id": "472d5764-6769-44d4-80ec-d1bdb46688b8", + "metadata": {}, + "source": [ + "## [Problem 100](https://projecteuler.net/problem=100)\n", + "\n", + "Another Pell equation! If there are $t$ total disks and $b$ blue ones then we have:\n", + "\n", + "$$\\frac{b}{t} \\times \\frac{b-1}{t-1} = \\frac{1}{2}$$\n", + "\n", + "which simplifies to\n", + "\n", + "$$2b^2 - 2b = t^2 - t$$\n", + "\n", + "Multiplying by 8 and completing the square we get:\n", + "\n", + "$$(4b - 2)^2 = 2(2t - 1)^2 + 2$$\n", + "\n", + "Now let $x = 2t - 1$ and $y = 4b - 2$, giving us:\n", + "\n", + "$$y^2 - 2x^2 = 2$$\n", + "\n", + "By inspection, $(x, y) = (1, 2)$ is a solution. That leads to the recurrence relations:\n", + "\n", + "$$x_{n+1} = 3x_n + 2y_n, \\quad y_{n+1} = 4x_n + 3y_n$$." + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "5a865f50-1a87-4745-87c6-6b1b932298c2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "100: Arranged Probability 0 msec ⇒ 756872327473 āœ… " + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def euler_100(limit=10**12):\n", + " \"\"\"Arranged Probability: a box contains some blue and red disks, and the probability\n", + " of drawing out two blue disks at random is exactly 1/2. What is the smallest number of blue disks\n", + " greater than 10^12 for which this is true?\"\"\"\n", + " x, y = 1, 1\n", + " while True:\n", + " x, y = 3*x + 4*y, 2*x + 3*y # multiply by fundamental unit 3 + 2*sqrt(2)\n", + " t, b = (x + 1) // 2, (y + 1) // 2\n", + " if t > limit:\n", + " return b\n", + "\n", + "run(euler_100)" ] }, { @@ -4895,7 +4993,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 106, "id": "b8a6b6dd-198a-4fac-972d-b7ef1db68355", "metadata": {}, "outputs": [], @@ -4910,8 +5008,7 @@ " ā„™.upto(12) => 2 3 5 7 11 # iterate through primes up to a limit\"\"\"\n", "\n", " def __init__(self, n=million):\n", - " \"\"\"Create an iterable generator of primes, with initial cache of all primes <= n\n", - " .\"\"\"\n", + " \"\"\"Create an iterable generator of primes, with initial cache of all primes <= n.\"\"\"\n", " # sieve keeps track of odd numbers: sieve[i] is True iff (2*i + 1) has no factors (yet) \n", " N = n // 2 # length of sieve\n", " sieve = [True] * N\n", @@ -4961,14 +5058,14 @@ "id": "8aead06a-0e3f-4122-8e43-8128a24a3c25", "metadata": {}, "source": [ - "# Summary of Answers\n", + "# Summary of Runs\n", "\n", - "Here are all my answers and their run times. I didn't do 5 problems, but the other 95 all run in under a second each." + "Here are all my answers and their run times. " ] }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 107, "id": "3ce96f67-2432-49e2-bf49-e7bc690c217a", "metadata": {}, "outputs": [ @@ -4976,8 +5073,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Missing problems: [64, 65, 66, 94, 100]\n", - "Run time in seconds: total: 12.7, max: 0.9, mean: 0.134, median: 0.012\n", + "Problems: 100\n", + "Run time in seconds: total: 11.6, max: 0.8, mean: 0.116, median: 0.009\n", "\n", " 1: Multiples of 3 and 5 0 msec ⇒ 233168 āœ… \n", " 2: Even Fibonacci numbers 0 msec ⇒ 4613732 āœ… \n", @@ -4985,101 +5082,114 @@ " 4: Largest palindrome product 46 msec ⇒ 906609 āœ… \n", " 5: Smallest multiple 0 msec ⇒ 232792560 āœ… \n", " 6: Sum square difference 0 msec ⇒ 25164150 āœ… \n", - " 7: 10,001st prime 3 msec ⇒ 104743 āœ… \n", + " 7: 10,001st prime 2 msec ⇒ 104743 āœ… \n", " 8: Largest product in a series 0 msec ⇒ 23514624000 āœ… \n", " 9: Special Pythagorean triplet 13 msec ⇒ 31875000 āœ… \n", - " 10: Summation of primes 50 msec ⇒ 142913828922 āœ… \n", + " 10: Summation of primes 46 msec ⇒ 142913828922 āœ… \n", " 11: Largest product in a matrix 1 msec ⇒ 70600674 āœ… \n", - " 12: Highly divisible triangular number 155 msec ⇒ 76576500 āœ… \n", + " 12: Highly divisible triangular number 150 msec ⇒ 76576500 āœ… \n", " 13: Large sum 0 msec ⇒ 5537376230 āœ… \n", - " 14: Longest Collatz sequence 465 msec ⇒ 837799 āœ… \n", + " 14: Longest Collatz sequence 402 msec ⇒ 837799 āœ… \n", " 15: Lattice paths 0 msec ⇒ 137846528820 āœ… \n", " 16: Power digit sum 0 msec ⇒ 1366 āœ… \n", " 17: Number letter counts 0 msec ⇒ 21124 āœ… \n", " 18: Maximum path sum I 0 msec ⇒ 1074 āœ… \n", - " 19: Counting Sundays 6 msec ⇒ 171 āœ… \n", + " 19: Counting Sundays 3 msec ⇒ 171 āœ… \n", " 20: Factorial digit sum 0 msec ⇒ 648 āœ… \n", - " 21: Amicable numbers 108 msec ⇒ 31626 āœ… \n", - " 22: Names scores 12 msec ⇒ 871198282 āœ… \n", - " 23: Non-abundant sums 472 msec ⇒ 4179871 āœ… \n", - " 24: Lexicographic permutations 6 msec ⇒ 2783915460 āœ… \n", - " 25: 1000-digit Fibonacci number 16 msec ⇒ 4782 āœ… \n", - " 26: Reciprocal cycles 270 msec ⇒ 983 āœ… \n", - " 27: Quadratic primes 234 msec ⇒ -59231 āœ… \n", + " 21: Amicable numbers 96 msec ⇒ 31626 āœ… \n", + " 22: Names scores 7 msec ⇒ 871198282 āœ… \n", + " 23: Non-abundant sums 496 msec ⇒ 4179871 āœ… \n", + " 24: Lexicographic permutations 7 msec ⇒ 2783915460 āœ… \n", + " 25: 1000-digit Fibonacci number 19 msec ⇒ 4782 āœ… \n", + " 26: Reciprocal cycles 276 msec ⇒ 983 āœ… \n", + " 27: Quadratic primes 251 msec ⇒ -59231 āœ… \n", " 28: Number spiral diagonals 0 msec ⇒ 669171001 āœ… \n", " 29: Distinct powers 2 msec ⇒ 9183 āœ… \n", - " 30: Digit fifth powers 190 msec ⇒ 443839 āœ… \n", + " 30: Digit fifth powers 194 msec ⇒ 443839 āœ… \n", " 31: Coin sums 0 msec ⇒ 73682 āœ… \n", - " 32: Pandigital products 74 msec ⇒ 45228 āœ… \n", + " 32: Pandigital products 72 msec ⇒ 45228 āœ… \n", " 33: Digit canceling fractions 3 msec ⇒ 100 āœ… \n", - " 34: Digit factorials 468 msec ⇒ 40730 āœ… \n", - " 35: Circular primes 382 msec ⇒ 55 āœ… \n", - " 36: Double-base palindromes 75 msec ⇒ 872187 āœ… \n", + " 34: Digit factorials 460 msec ⇒ 40730 āœ… \n", + " 35: Circular primes 368 msec ⇒ 55 āœ… \n", + " 36: Double-base palindromes 76 msec ⇒ 872187 āœ… \n", " 37: Truncatable primes 0 msec ⇒ 748317 āœ… \n", - " 38: Pandigital multiples 43 msec ⇒ 932718654 āœ… \n", + " 38: Pandigital multiples 44 msec ⇒ 932718654 āœ… \n", " 39: Integer right triangles 8 msec ⇒ 840 āœ… \n", - " 40: Champernowne's constant 39 msec ⇒ 210 āœ… \n", + " 40: Champernowne's constant 41 msec ⇒ 210 āœ… \n", " 41: Pandigital prime 1 msec ⇒ 7652413 āœ… \n", - " 42: Coded triangle numbers 3 msec ⇒ 162 āœ… \n", - " 43: Sub-string divisibility 12 msec ⇒ 16695334890 āœ… \n", - " 44: Pentagon numbers 110 msec ⇒ 5482660 āœ… \n", - " 45: Triangular/pentagonal/hexagonal 18 msec ⇒ 1533776805 āœ… \n", + " 42: Coded triangle numbers 2 msec ⇒ 162 āœ… \n", + " 43: Sub-string divisibility 16 msec ⇒ 16695334890 āœ… \n", + " 44: Pentagon numbers 116 msec ⇒ 5482660 āœ… \n", + " 45: Triangular/pentagonal/hexagonal 17 msec ⇒ 1533776805 āœ… \n", " 46: Goldbach's other conjecture 4 msec ⇒ 5777 āœ… \n", - " 47: Distinct primes factors 272 msec ⇒ 134043 āœ… \n", + " 47: Distinct primes factors 268 msec ⇒ 134043 āœ… \n", " 48: Self powers 1 msec ⇒ 9110846700 āœ… \n", " 49: Prime permutations 0 msec ⇒ 296962999629 āœ… \n", " 50: Consecutive prime sum 3 msec ⇒ 997651 āœ… \n", - " 51: Prime digit replacements 315 msec ⇒ 121313 āœ… \n", - " 52: Permuted multiples 93 msec ⇒ 142857 āœ… \n", + " 51: Prime digit replacements 317 msec ⇒ 121313 āœ… \n", + " 52: Permuted multiples 94 msec ⇒ 142857 āœ… \n", " 53: Combinatoric selections 0 msec ⇒ 4075 āœ… \n", - " 54: Poker hands 8 msec ⇒ 376 āœ… \n", - " 55: Lychrel numbers 10 msec ⇒ 249 āœ… \n", - " 56: Powerful digit sum 44 msec ⇒ 972 āœ… \n", - " 57: Square root convergents 5 msec ⇒ 153 āœ… \n", - " 58: Spiral primes 43 msec ⇒ 26241 āœ… \n", - " 59: XOR decryption 771 msec ⇒ 107359 āœ… \n", - " 60: Prime pair sets 875 msec ⇒ 26033 āœ… \n", + " 54: Poker hands 6 msec ⇒ 376 āœ… \n", + " 55: Lychrel numbers 8 msec ⇒ 249 āœ… \n", + " 56: Powerful digit sum 42 msec ⇒ 972 āœ… \n", + " 57: Square root convergents 6 msec ⇒ 153 āœ… \n", + " 58: Spiral primes 41 msec ⇒ 26241 āœ… \n", + " 59: XOR decryption 651 msec ⇒ 107359 āœ… \n", + " 60: Prime pair sets 825 msec ⇒ 26033 āœ… \n", " 61: Cyclical figurate numbers 3 msec ⇒ 28684 āœ… \n", " 62: Cubic permutations 6 msec ⇒ 127035954683 āœ… \n", " 63: Powerful digit counts 0 msec ⇒ 49 āœ… \n", - " 67: Maximum path sum II 4 msec ⇒ 7273 āœ… \n", - " 68: Magic 5-gon ring 23 msec ⇒ 6531031914842725 āœ… \n", - " 69: Totient maximum 108 msec ⇒ 510510 āœ… \n", - " 70: Totient Permutation 31 msec ⇒ 8319823 āœ… \n", + " 64: Odd Period Square Roots 19 msec ⇒ 1322 āœ… \n", + " 65: Convergents of e 0 msec ⇒ 272 āœ… \n", + " 66: Diophantine Equation 2 msec ⇒ 661 āœ… \n", + " 67: Maximum path sum II 1 msec ⇒ 7273 āœ… \n", + " 68: Magic 5-gon ring 18 msec ⇒ 6531031914842725 āœ… \n", + " 69: Totient maximum 102 msec ⇒ 510510 āœ… \n", + " 70: Totient Permutation 28 msec ⇒ 8319823 āœ… \n", " 71: Ordered fractions 0 msec ⇒ 428570 āœ… \n", - " 72: Counting fractions 379 msec ⇒ 303963552391 āœ… \n", - " 73: Counting fractions in a range 756 msec ⇒ 7295372 āœ… \n", - " 74: Digit factorial chains 536 msec ⇒ 402 āœ… \n", - " 75: Singular integer right triangles 99 msec ⇒ 161667 āœ… \n", + " 72: Counting fractions 285 msec ⇒ 303963552391 āœ… \n", + " 73: Counting fractions in a range 737 msec ⇒ 7295372 āœ… \n", + " 74: Digit factorial chains 506 msec ⇒ 402 āœ… \n", + " 75: Singular integer right triangles 89 msec ⇒ 161667 āœ… \n", " 76: Counting summations 1 msec ⇒ 190569291 āœ… \n", " 77: Prime summations 1 msec ⇒ 71 āœ… \n", - " 78: Coin partitions 807 msec ⇒ 55374 āœ… \n", - " 79: Passcode Derivation 6 msec ⇒ 73162890 āœ… \n", + " 78: Coin partitions 725 msec ⇒ 55374 āœ… \n", + " 79: Passcode Derivation 11 msec ⇒ 73162890 āœ… \n", " 80: Square root digital expansion 1 msec ⇒ 40886 āœ… \n", - " 81: Path Sum, Two ways 3 msec ⇒ 427337 āœ… \n", - " 82: Path Sum, Three Ways 388 msec ⇒ 260324 āœ… \n", - " 83: Path Sum, Four Ways 6 msec ⇒ 425185 āœ… \n", - " 84: Monopoly odds 615 msec ⇒ 101524 āœ… \n", - " 85: Counting rectangles 807 msec ⇒ 2772 āœ… \n", - " 86: Cuboid Route 242 msec ⇒ 1818 āœ… \n", - " 87: Prime power triples 559 msec ⇒ 1097343 āœ… \n", - " 88: Product-sum Numbers 76 msec ⇒ 7587457 āœ… \n", - " 89: Roman numerals 4 msec ⇒ 743 āœ… \n", + " 81: Path Sum, Two ways 2 msec ⇒ 427337 āœ… \n", + " 82: Path Sum, Three Ways 374 msec ⇒ 260324 āœ… \n", + " 83: Path Sum, Four Ways 5 msec ⇒ 425185 āœ… \n", + " 84: Monopoly odds 44 msec ⇒ 101524 āœ… \n", + " 85: Counting rectangles 768 msec ⇒ 2772 āœ… \n", + " 86: Cuboid Route 279 msec ⇒ 1818 āœ… \n", + " 87: Prime power triples 501 msec ⇒ 1097343 āœ… \n", + " 88: Product-sum Numbers 74 msec ⇒ 7587457 āœ… \n", + " 89: Roman numerals 3 msec ⇒ 743 āœ… \n", " 90: Cube digit pairs 9 msec ⇒ 1217 āœ… \n", - " 91: Right Triangles with Integer Coordinates 769 msec ⇒ 14234 āœ… \n", - " 92: Square digit chains 34 msec ⇒ 8581146 āœ… \n", - " 93: Arithmetic expressions 39 msec ⇒ 1258 āœ… \n", - " 95: Amicable chains 569 msec ⇒ 14316 āœ… \n", - " 96: SuDoku 51 msec ⇒ 24702 āœ… \n", + " 91: Right Triangles with Integer Coordinates 759 msec ⇒ 14234 āœ… \n", + " 92: Square digit chains 38 msec ⇒ 8581146 āœ… \n", + " 93: Arithmetic expressions 45 msec ⇒ 1258 āœ… \n", + " 94: Almost Equilateral Triangles 0 msec ⇒ 518408346 āœ… \n", + " 95: Amicable chains 541 msec ⇒ 14316 āœ… \n", + " 96: SuDoku 52 msec ⇒ 24702 āœ… \n", " 97: Large non-Mersenne prime 0 msec ⇒ 8739992577 āœ… \n", - " 98: Anagramic squares 40 msec ⇒ 18769 āœ… \n", - " 99: Largest exponential 3 msec ⇒ 709 āœ… \n" + " 98: Anagramic squares 37 msec ⇒ 18769 āœ… \n", + " 99: Largest exponential 3 msec ⇒ 709 āœ… \n", + "100: Arranged Probability 0 msec ⇒ 756872327473 āœ… \n" ] } ], "source": [ - "summary()" + "runs()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2f33656-8ada-4146-a2bd-61b5df951e69", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": {