Compare commits

...
11 Commits
7 changed files with 49803 additions and 49747 deletions

No files matched your search

+27 -17
View File
@@ -34,11 +34,26 @@ For each notebook you can hover on the title to see a description, or click the
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/TruncatablePrimes.ipynb) | 2026 | <a href="ipynb/TruncatablePrimes.ipynb" title="Prime numbers where you can remove digits and still get primes.">Truncatable Primes</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) | 2025 | <a href="ipynb/Advent-2025.ipynb" title="Puzzle site with a coding puzzle for 12 days of Christmas, December 2025">Advent of Code 2025</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) | 2025 | <a href="ipynb/Advent-2025-AI.ipynb" title="Using large language coding models to solve AoC puzzles">Advent of Code 2025: AI LLM Edition</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) | 2025 | <a href="ipynb/Advent-2025-AI.ipynb" title="Using large language coding models to solve AoC puzzles">Advent of Code 2025: AI LLM Edition</a> |
|Open|Year|Large Language Models|
|---|---|---|
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/AlphaCode.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/AlphaCode.ipynb) | 2022 | <a href="ipynb/AlphaCode.ipynb" title="Analysis of AlphaCode's automated solution to a coding problem">AlphaCode Automated Programming</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) | 2025 | <a href="ipynb/Advent-2025-AI.ipynb" title="Using large language coding models to solve AoC puzzles">Advent of Code 2025: AI LLM Edition</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Goldberg.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Goldberg.ipynb) | 2019 | <a href="ipynb/Goldberg.ipynb" title="A re-implementation in Python 3 of Yoav Goldberg's unreasonably effective character-level n-gram language model.">Effectiveness of Language Models</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) | 2018 | <a href="ipynb/How%20to%20Do%20Things%20with%20Words.ipynb" title="Spelling Correction, Secret Codes, Word Segmentation, and more">How to Do Things with Words: NLP in Python</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) | 2024 | <a href="ipynb/Triplets.ipynb" title="LLMs do better at producing a program to solve this puzzle than they do at solving it directly">The Languages of English, Math, and Programming</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) | 2024 | <a href="ipynb/CherylMind.ipynb" title="Do LLMs have enough theory of mind to solve the Cheryl's Birthday puzzle?">LLMs, Theory of Mind, and Cheryl's Birthday</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/OneLetterOff.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/OneLetterOff.ipynb) | 2023 | <a href="ipynb/OneLetterOff.ipynb" title="Word game; use of large language model word embeddings to generate clues.">One Letter Off</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler.ipynb) | 2026 | <a href="ipynb/Euler.ipynb" title="Solutions to the first 100 Project Euler math/programming problems">Project Euler #1–100 by a Human</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler-Fable.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler-Fable.ipynb) | 2026 | <a href="ipynb/Euler-Fable.ipynb" title="Solutions to Project Euler math/programming problems by Claude Fable LLM">Project Euler #1–100 by Fable LLM</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler-Opus.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler-Opus.ipynb) | 2026 | <a href="ipynb/Euler-Opus.ipynb" title="Solutions to Project Euler math/programming problems by Claude Opus LLM">Project Euler #1–100 by Opus LLM</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler-Kimi.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler-Kimi.ipynb) | 2026 | <a href="ipynb/Euler-Kimi.ipynb" title="Solutions to Project Euler math/programming problems by Kimi LLM">Project Euler #1–100 by Kimi LLM</a> |
|Open|Year|Programming Examples|
|---|---|---|
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/AlphaCode.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/AlphaCode.ipynb) | 2022 | <a href="ipynb/AlphaCode.ipynb" title="Analysis of AlphaCode's automated solution to a coding problem">AlphaCode Automated Programming</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/RationalPi.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/RationalPi.ipynb) | 2026 | <a href="ipynb/RationalPi.ipynb" title="Using continued fractions to find fractions (like 22/7) that are close to pi.">Approximating Pi with a Fraction</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Babylonian%20digits.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Babylonian%20digits.ipynb) | 2022 | <a href="ipynb/Babylonian%20digits.ipynb" title="Translating between Babylonian and traditional number systems.">The Babylonian Number System</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Beal.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Beal.ipynb) | 2018 | <a href="ipynb/Beal.ipynb" title="A search for counterexamples to Beal's Conjecture">Beal's Conjecture Revisited</a> |
@@ -47,7 +62,7 @@ For each notebook you can hover on the title to see a description, or click the
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sierpinski.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sierpinski.ipynb) | 2019 | <a href="ipynb/Sierpinski.ipynb" title="A surprising appearance of the Sierpinski triangle in a random walk">Chaos with Triangles</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Life.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Life.ipynb) | 2017 | <a href="ipynb/Life.ipynb" title="The cellular automata zero-player game">Conway's Game of Life</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Maze.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Maze.ipynb) | 2020 | <a href="ipynb/Maze.ipynb" title="Make a maze by generating a random tree superimposed on a grid and solve it">Generating and Solving Mazes</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Triplets.ipynb) | 2024 | <a href="ipynb/Triplets.ipynb" title="LLMs do better at producing a program to solve this puzzle than they do at solving it directly">The Languages of English, Math, and Programming</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler3.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler3.ipynb) | 2026 | <a href="ipynb/Euler3.ipynb" title="For beginner programmers: How to find the largest prime factor of a number. Project Euler #3.">Largest prime factor</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Konane.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Konane.ipynb) | 2021 | <a href="ipynb/Konane.ipynb" title="Solving the game of Konane (Hawaiian checkers).">Mel's Konane Board</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/PhotoFocalLengths.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/PhotoFocalLengths.ipynb) | 2020 | <a href="ipynb/PhotoFocalLengths.ipynb" title="Generate charts of what focal lengths were used on a photo trip">Photo Focal Lengths</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Pickleball.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Pickleball.ipynb) | 2018 | <a href="ipynb/Pickleball.ipynb" title="Scheduling a doubles tournament fairly and efficiently">Pickleball Tournament</a> |
@@ -55,7 +70,7 @@ For each notebook you can hover on the title to see a description, or click the
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Electoral%20Votes.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Electoral%20Votes.ipynb) | 2020 | <a href="ipynb/Electoral%20Votes.ipynb" title="How many electoral votes would Trump get if he wins the state where he has positive net approval?">Tracking Polls: Electoral Votes</a> |
|Open|Year|Advent of Code and Project Euler|
|Open|Year|Advent of Code|
|---|---|---|
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025-AI.ipynb) | 2025 | <a href="ipynb/Advent-2025-AI.ipynb" title="Using large language coding models to solve AoC puzzles">Advent of Code 2025: AI LLM Edition</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2025.ipynb) | 2025 | <a href="ipynb/Advent-2025.ipynb" title="Puzzle site with a coding puzzle for 12 days of Christmas, December 2025">Advent of Code 2025</a> |
@@ -68,16 +83,10 @@ For each notebook you can hover on the title to see a description, or click the
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2017.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2017.ipynb) | 2017 | <a href="ipynb/Advent-2017.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2017">Advent of Code 2017</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Advent-2016.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Advent-2016.ipynb) | 2016 | <a href="ipynb/Advent-2016.ipynb" title="Puzzle site with a coding puzzle each day of Advent, December 2016">Advent of Code 2016</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/AdventUtils.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/AdventUtils.ipynb) | 2022 | <a href="ipynb/AdventUtils.ipynb" title="Utility functions for Advent of Code puzzles">Advent of Code Utilities</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler.ipynb) | 2026 | <a href="ipynb/Euler.ipynb" title="Solutions to the first 100 Project Euler math/programming problems">Project Euler #1–100 by a Human</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler-Fable.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler-Fable.ipynb) | 2026 | <a href="ipynb/Euler-Fable.ipynb" title="Solutions to Project Euler math/programming problems by Claude Fable LLM">Project Euler #1–100 by Fable LLM</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler-Opus.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler-Opus.ipynb) | 2026 | <a href="ipynb/Euler-Opus.ipynb" title="Solutions to Project Euler math/programming problems by Claude Opus LLM">Project Euler #1–100 by Opus LLM</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler-Kimi.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler-Kimi.ipynb) | 2026 | <a href="ipynb/Euler-Kimi.ipynb" title="Solutions to Project Euler math/programming problems by Kimi LLM">Project Euler #1–100 by Kimi LLM</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler3.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler3.ipynb) | 2026 | <a href="ipynb/Euler3.ipynb" title="For beginner programmers: How to find the largest prime factor of a number. Project Euler #3.">Largest prime factor</a> |
|Open|Year|Probability and Uncertainty|
|---|---|---|
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Goldberg.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Goldberg.ipynb) | 2019 | <a href="ipynb/Goldberg.ipynb" title="A re-implementation in Python 3 of Yoav Goldberg's unreasonably effective character-level n-gram language model.">Effectiveness of Language Models</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Probability.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Probability.ipynb) | 2018 | <a href="ipynb/Probability.ipynb" title="Code and examples of the basic principles of Probability Theory">A Concrete Introduction to Probability</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ProbabilityParadox.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ProbabilityParadox.ipynb) | 2016 | <a href="ipynb/ProbabilityParadox.ipynb" title="Some classic paradoxes in Probability Theory, and how to think about disagreements">Probability, Paradox, and the Reasonable Person Principle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/ProbabilitySimulation.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/ProbabilitySimulation.ipynb) | 2020 | <a href="ipynb/ProbabilitySimulation.ipynb" title="When the sample space is too complex, simulations can estimate probabilities">Estimating Probabilities with Simulations</a> |
@@ -98,23 +107,26 @@ For each notebook you can hover on the title to see a description, or click the
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Euler's%20Conjecture.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Euler's%20Conjecture.ipynb) | 2018 | <a href="ipynb/Euler's%20Conjecture.ipynb" title="Solving a 200-year-old puzzle by finding integers that satisfy a<sup>5</sup> + b<sup>5</sup> + c<sup>5</sup> + d<sup>5</sup> = e<sup>5</sup>">Euler's Sum of Powers Conjecture</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Countdown.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Countdown.ipynb) | 2020 | <a href="ipynb/Countdown.ipynb" title="Solving the equation 10 _ 9 _ 8 _ 7 _ 6 _ 5 _ 4 _ 3 _ 2 _ 1 = 2016. Originally from an Alex Bellos puzzle">Four 4s, Five 5s, and Countdowns</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/How%20To%20Count%20Things.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/How%20To%20Count%20Things.ipynb) | 2020 | <a href="ipynb/How%20To%20Count%20Things.ipynb" title="Combinatorial math: how to count how many things there are, when there are a lot of them">How to Count Things</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/KenKen.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/KenKen.ipynb) | 2021 | <a href="ipynb/KenKen.ipynb" title="A Sudoku-like puzzle, but with arithmetic.">KenKen (Sudoku-like Puzzle)</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/NumberBracelets.ipynb) | 2024 | <a href="ipynb/NumberBracelets.ipynb" title="A game involving numbered beads on a circular bracelet.">Number Bracelets Game</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Socks.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Socks.ipynb) | 2019 | <a href="ipynb/Socks.ipynb" title="What is the probability that you will be able to pair up socks as you randomly pull them out of the dryer?">Pairing Socks</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sicherman%20Dice.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sicherman%20Dice.ipynb) | 2018 | <a href="ipynb/Sicherman%20Dice.ipynb" title="Find a pair of dice that is like a regular pair of dice, only different">Sicherman Dice</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Golomb-Puzzle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Golomb-Puzzle.ipynb) | 2014 | <a href="ipynb/Golomb-Puzzle.ipynb" title="A Puzzle involving placing rectangles of different sizes inside a square">Sol Golomb's Rectangle Puzzle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Stubborn.ipynb) | 2024 | <a href="ipynb/Stubborn.ipynb" title="Any number ending in 5 has a square that also ends in 5. What other endings are like this?">Stubborn number endings</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/StarBattle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/StarBattle.ipynb) | 2021 | <a href="ipynb/StarBattle.ipynb" title="Fill-in-the-grid puzzle similar to Sudoku">Star Battle (Sudoku-like Puzzle)</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb) | 2006 | <a href="ipynb/Sudoku.ipynb" title="Classic fill-in-the-grid puzzle">Sudoku</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SudokuJava.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SudokuJava.ipynb) | 2021 | <a href="ipynb/SudokuJava.ipynb" title="A version of the Sudoku solver using parallel threads and other optimizations">Sudoku: 200,000 puzzles/second in Java</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SquareSum.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SquareSum.ipynb) | 2020 | <a href="ipynb/SquareSum.ipynb" title="Place the numbers from 1 to n in a chain (or a circle) such that adjacent pairs sum to a perfect square">Square Sum Puzzle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SquareSum.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SquareSum.ipynb) | 2020 | <a href="ipynb/SquareSum.ipynb" title="Place the numbers from 1 to n in a chain (or a circle) such that adjacent pairs sum to a perfect square">Square Sum Chain Puzzle</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cheryl.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cheryl.ipynb) | 2020 | <a href="ipynb/Cheryl.ipynb" title="Solving the *Cheryl's Birthday* logic puzzle">When is Cheryl's Birthday?</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Cheryl-and-Eve.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Cheryl-and-Eve.ipynb) | 2015 | <a href="ipynb/Cheryl-and-Eve.ipynb" title="Inventing new puzzles in the Style of Cheryl's Birthday">When Cheryl Met Eve: A Birthday Story</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/CherylMind.ipynb) | 2024 | <a href="ipynb/CherylMind.ipynb" title="Do LLMs have enough theory of mind to solve the Cheryl's Birthday puzzle?">LLMs, Theory of Mind, and Cheryl's Birthday</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/xkcd1313.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/xkcd1313.ipynb) | 2015 | <a href="ipynb/xkcd1313.ipynb" title="Find the smallest regular expression; inspired by Randall Munroe">xkcd 1313: Regex Golf</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/xkcd1313-part2.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/xkcd1313-part2.ipynb) | 2015 | <a href="ipynb/xkcd1313-part2.ipynb" title="Regex Golf: better, faster, funner (with Stefan Pochmann)">xkcd 1313: Regex Golf (Part 2: Infinite Problems)</a> |
|Open|Year|Sudoku and Similar Puzzles|
|---|---|---|
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Sudoku.ipynb) | 2006 | <a href="ipynb/Sudoku.ipynb" title="Classic fill-in-the-grid puzzle solved in Python">Sudoku</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SudokuJava.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SudokuJava.ipynb) | 2021 | <a href="ipynb/SudokuJava.ipynb" title="A Java version of the Sudoku solver using parallel threads and other optimizations">Sudoku: 400,000 puzzles/second in Java</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/KenKen.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/KenKen.ipynb) | 2021 | <a href="ipynb/KenKen.ipynb" title="A Sudoku-like puzzle, but with arithmetic.">KenKen (Sudoku-like Puzzle)</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/StarBattle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/StarBattle.ipynb) | 2021 | <a href="ipynb/StarBattle.ipynb" title="Fill-in-the-grid puzzle similar to Sudoku">Star Battle (Sudoku-like Puzzle)</a> |
|Open|Year|Word Puzzles|
|---|---|---|
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Boggle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Boggle.ipynb) | 2020 | <a href="ipynb/Boggle.ipynb" title="Find all the words on a Boggle board; then find a board with a lot of words">Boggle / Inverse Boggle</a> |
@@ -123,9 +135,7 @@ For each notebook you can hover on the title to see a description, or click the
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/equilength-numbers.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/equilength-numbers.ipynb) | 2020 | <a href="ipynb/equilength-numbers.ipynb" title="What number names have the same letter count as the number they name (such as FOUR)?">Equilength Numbers: FOUR = 4</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Gesture%20Typing.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Gesture%20Typing.ipynb) | 2017 | <a href="ipynb/Gesture%20Typing.ipynb" title="What word has the longest path on a gesture-typing smartphone keyboard?">Gesture Typing</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Ghost.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Ghost.ipynb) | 2017 | <a href="ipynb/Ghost.ipynb" title="The word game Ghost (add letters, try to avoid making a word)">Ghost: A Word game</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) | 2018 | <a href="ipynb/How%20to%20Do%20Things%20with%20Words.ipynb" title="Spelling Correction, Secret Codes, Word Segmentation, and more">How to Do Things with Words: NLP in Python</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Fred%20Buns.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Fred%20Buns.ipynb) | 2015 | <a href="ipynb/Fred%20Buns.ipynb" title="A tale of a bicycle combination lock that uses letters instead of digits. Inspired by Bike Snob NYC">Let's Code About Bike Locks</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/OneLetterOff.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/OneLetterOff.ipynb) | 2023 | <a href="ipynb/OneLetterOff.ipynb" title="Word game; use of a large language model to generate clues.">One Letter Off</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/Scrabble.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/Scrabble.ipynb) | 2017 | <a href="ipynb/Scrabble.ipynb" title="Refactoring the Scrabble / Word with Friends game from Udacity 212">Scrabble: Refactoring a Crossword Game Program</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/SpellingBee.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/SpellingBee.ipynb) | 2020 | <a href="ipynb/SpellingBee.ipynb" title="Find the highest-scoring board for the NY Times Spelling Bee puzzle">Spelling Bee</a> |
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/main/ipynb/PropositionalLogic.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/main/ipynb/PropositionalLogic.ipynb) | 2017 | <a href="ipynb/PropositionalLogic.ipynb" title="Automatically convert informal English sentences into formal Propositional Logic">Translating English into Propositional Logic</a> |
Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.
+179 -170
View File
@@ -1,17 +1,22 @@
import java.io.*;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
//////////////////////////////// Solve Sudoku Puzzles ////////////////////////////////
//////////////////////////////// @author Peter Norvig ////////////////////////////////
//////////////////////////////// 2007, 2021 ////////////////////////////////
//////////////////////////////// 2007, 2021, 2026 ////////////////////////////////
/** Solve Sudoku Puzzles
** @author Peter Norvig
** Mostly 2007, som2 2021, 2026
**
/**
** There are two representations of puzzles that we will use:
** 1. A gridstring is 81 chars, with characters '0' or '.' for blank and '1' to '9' for digits.
** 2. A puzzle grid is an int[81] with a digit d (1-9) represented by the integer (1 << (d - 1));
@@ -25,9 +30,11 @@ import java.util.stream.IntStream;
** - If the guess is consistent, search deeper; if not, try a different guess for the square.
** - If all guesses fail, back up to the previous level.
** - In selecting an empty square, we pick one that has the minimum number of possible digits.
** That makes us more likely to fail early if we guess wrong.
** - To be able to back up, we need to keep the grid from the previous recursive level.
** But we only need to keep one grid for each level, so to save garbage collection,
** we pre-allocate one grid per level (there are 81 levels) in a `gridpool`.
** To save garbage collection, we pre-allocate one grid per level (81 levels) in a `gridpool`.
** - We can work in parallel in multiple threads, each with its own gridpool.
**
** Do constraint propagation with `arcConsistent`, `dualConsistent`, and `nakedPairs`.
**/
@@ -35,22 +42,21 @@ public class Sudoku {
//////////////////////////////// main; command line options //////////////////////////////
static final String USAGE = String.join("\n",
"usage: java Sudoku -(no)[fghnprstuv] | -[RT]<number> | <filename> ...",
"Options and filenames are processed left-to-right. Use '-no' to turn an option off\n",
"E.g.: -v turns verify flag on, -nov turns it off. -R and -T require a number. The options:\n",
" -g(rid) Print each puzzle grid and solution grid (default off)",
" -h(elp) Print this usage message",
" -n(aked) Run the naked pairs strategy (default on)",
" -p(uzzle) Print summary stats for each puzzle (default off)",
" -r(everse) Solve the reverse of each puzzle as well as each puzzle itself (default off)",
" -s(ummary) Print per-file summary stats (default on)",
" -t(hread) Print summary stats for each thread (default off)",
" -u(nitTest) Run a suite of unit tests (default off)",
" -v(erify) Verify each solution is valid (default on)",
" -T<number> Concurrently run <number> threads (default 25)",
" -R<number> Repeat the solving of each puzzle <number> times (default 1)",
" <filename> Solve all puzzles in filename, which has one puzzle per line");
static final String USAGE = """
usage: java Sudoku -(no)[fghnprstuv] | -[RT]<number> | <filename> ...\n
Options and filenames are processed left-to-right. Use '-no' to turn an option off
E.g.: -v turns verify flag on, -nov turns it off. -R and -T require a number. The options:\n
-g(rid) Print each puzzle grid and solution grid (default off)
-h(elp) Print this usage message
-n(aked) Run the naked pairs strategy (default on)
-p(uzzle) Print summary stats for each puzzle (default off)
-r(everse) Solve the reverse of each puzzle as well as each puzzle itself (default off)
-s(ummary) Print per-file summary stats (default on)
-t(hread) Print summary stats for each thread (default off)
-v(erify) Verify each solution is valid (default on)
-T<number> Concurrently run <number> threads (default 28)
-R<number> Repeat the solving of each puzzle <number> times (default 1)
<filename> Solve all puzzles in filename, which has one puzzle per line""";
boolean printGrid = false; // -g
boolean runNakedPairs = true; // -n
@@ -59,15 +65,30 @@ public class Sudoku {
boolean printFileStats = true; // -s
boolean printThreadStats = false; // -t
boolean verifySolution = true; // -v
int nThreads = 25; // -T
int nThreads = 28; // -T
int repeat = 1; // -R
private final AtomicInteger backtracks = new AtomicInteger(0);
private volatile boolean headerPrinted = false;
private static final int WORK_BLOCK_SIZE = 16;
private static final int BOX_SIZE = 3;
/** Mutable state owned by exactly one solver thread. **/
static final class WorkerState {
final int[] root = new int[N * N];
final int[][] gridpool = new int[N * N][N * N];
long backtracks;
int puzzlesSolved;
}
/** Parse command line args and solve puzzles in files. **/
public static void main(String[] args) throws IOException {
Sudoku s = new Sudoku();
// Warm up the JIT code cache by solving a puzzle a few times
String puzzle = "........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3";
s.solveListWithWorkers(Collections.nCopies(9999, s.parseGrid(puzzle)), 1);
// Process the options and filenames
for (String arg : args) {
if (!arg.startsWith("-")) {
s.solveFile(arg);
@@ -81,7 +102,6 @@ public class Sudoku {
case 'r' -> s.reversePuzzle = value;
case 's' -> s.printFileStats = value;
case 't' -> s.printThreadStats = value;
case 'u' -> s.runUnitTests();
case 'v' -> s.verifySolution = value;
case 'T' -> s.nThreads = Integer.parseInt(arg.substring(2));
case 'R' -> s.repeat = Integer.parseInt(arg.substring(2));
@@ -96,62 +116,64 @@ public class Sudoku {
/** Solve all the puzzles in a file. Report timing statistics. **/
void solveFile(String filename) throws IOException {
List<int[]> grids = readPuzzlesFromFile(filename);
var grids = readPuzzlesFromFile(filename);
long startFileTime = System.nanoTime();
if (nThreads == 1) {
solveList(grids);
} else {
solveListThreaded(grids, nThreads);
long backtracks = solveListWithWorkers(grids, nThreads);
if (printFileStats) {
printStats(grids.size() * repeat, startFileTime, filename, backtracks);
}
if (printFileStats) printStats(grids.size() * repeat, startFileTime, filename);
}
}
/** Solve a list of puzzles in a single thread.
** repeat -R<number> times; print each puzzle's stats if -p; print grid if -g; verify if -v. **/
void solveList(List<int[]> grids) {
int[] puzzle = new int[N * N];
int[][] gridpool = new int[N * N][N * N];
for (int g = 0; g < grids.size(); ++g) {
int[] grid = grids.get(g);
System.arraycopy(grid, 0, puzzle, 0, grid.length);
for (int i = 0; i < repeat; ++i) {
long startTime = printPuzzleStats ? System.nanoTime() : 0;
int[] solution = initialize(grid);
solution = search(solution, gridpool, 0);
if (printPuzzleStats) {
printStats(1, startTime, "Puzzle " + (g + 1));
}
if (i == 0 && (printGrid || (verifySolution && !verify(solution, puzzle)))) {
printGrids("Puzzle " + (g + 1), grid, solution);
}
}
}
}
/** Break a list of puzzles into nThreads sublists and solve each sublist in a separate thread. **/
void solveListThreaded(List<int[]> grids, int nThreads) {
try {
final long startTime = System.nanoTime();
int nGrids = grids.size();
final CountDownLatch latch = new CountDownLatch(nThreads);
int size = nGrids / nThreads;
for (int c = 0; c < nThreads; ++c) {
int end = (c == nThreads - 1) ? nGrids : (c + 1) * size;
final List<int[]> sublist = grids.subList(c * size, end);
new Thread(() -> {
solveList(sublist);
latch.countDown();
if (printThreadStats) {
printStats(repeat * sublist.size(), startTime, "Thread");
/** Use nThreads workers to pull puzzle grids off the list and solve them.
Keep track of the number of backtracks each worker does and return the sum. **/
long solveListWithWorkers(List<int[]> grids, int nThreads) {
int nGrids = grids.size();
var nextGrid = new AtomicInteger(0);
try (var pool = Executors.newFixedThreadPool(nThreads)) {
var tasks = IntStream.range(0, nThreads)
.<Callable<Long>>mapToObj(c -> () -> {
var worker = new WorkerState();
for (int start; (start = nextGrid.getAndAdd(WORK_BLOCK_SIZE)) < nGrids; ) {
solveRange(grids, start, Math.min(start + WORK_BLOCK_SIZE, nGrids), worker);
}
}).start();
return worker.backtracks;
}).toList();
long totalBacktracks = 0;
for (var future : pool.invokeAll(tasks)) {
try {
totalBacktracks += future.get();
} catch (ExecutionException e) {
throw new RuntimeException("Solver worker failed", e.getCause());
}
}
latch.await();
return totalBacktracks;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Solver thread was interrupted.");
return 0;
}
}
/** Solve grids in [start, end) using one worker in a thread. **/
void solveRange(List<int[]> grids, int start, int end, WorkerState worker) {
for (int g = start; g < end; ++g) {
var grid = grids.get(g);
for (int i = 0; i < repeat; ++i) {
long startTime = printPuzzleStats ? System.nanoTime() : 0;
long startingBacktracks = worker.backtracks;
initialize(grid, worker.root);
var solution = search(worker.root, worker, 0);
if (printPuzzleStats) {
printStats(1, startTime, "Puzzle " + (g + 1),
worker.backtracks - startingBacktracks);
}
if (i == 0 && (printGrid || (verifySolution && !verify(solution, grid)))) {
printGrids("Puzzle " + (g + 1), grid, solution);
}
}
++worker.puzzlesSolved;
}
}
@@ -159,8 +181,8 @@ public class Sudoku {
//////////////////////////////// Utility functions ////////////////////////////////
/** Return an array of all squares in the intersection of these rows and cols **/
int[] cross(int[] rows, int[] cols) {
int[] result = new int[rows.length * cols.length];
static int[] cross(int[] rows, int[] cols) {
var result = new int[rows.length * cols.length];
int i = 0;
for (int r : rows) {
for (int c : cols) {
@@ -171,10 +193,10 @@ public class Sudoku {
}
/** Return true iff item is an element of array. **/
boolean member(int item, int[] array) { return member(item, array, array.length); }
static boolean member(int item, int[] array) { return member(item, array, array.length); }
/** Return true iff item appears within array[0..end). **/
boolean member(int item, int[] array, int end) {
static boolean member(int item, int[] array, int end) {
for (int i = 0; i < end; ++i) {
if (array[i] == item) return true;
}
@@ -184,20 +206,21 @@ public class Sudoku {
//////////////////////////////// Constants ////////////////////////////////
final int N = 9;
final int[] DIGITS = {1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 1<<8};
final int ALL_DIGITS = Integer.parseInt("111111111", 2);
final int[] ROWS = IntStream.range(0, N).toArray();
final int[] COLS = ROWS;
final int[] SQUARES = IntStream.range(0, N * N).toArray();
final int[][] BLOCKS = {{0,1,2},{3,4,5},{6,7,8}};
final int[][] ALL_UNITS = new int[3 * N][];
final int[][][] UNITS = new int[N * N][3][N];
final int[][] PEERS = new int[N * N][20];
final int[] NUM_DIGITS = new int[ALL_DIGITS + 1];
final int[] HIGHEST_DIGIT = new int[ALL_DIGITS + 1];
static final int N = 9; // Number of distinct digits, also length of side of grid
static final int[] DIGITS = {1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 1<<8};
static final int ALL_DIGITS = 0b111111111;
static final int[] ROWS = IntStream.range(0, N).toArray();
static final int[] COLS = ROWS;
static final int[] SQUARES = IntStream.range(0, N * N).toArray();
static final int[][] BLOCKS = {{0,1,2},{3,4,5},{6,7,8}};
static final int[][] ALL_UNITS = new int[3 * N][];
static final int[][][] UNITS = new int[N * N][3][N];
static final int[][] PEERS = new int[N * N][20];
static final int[] NUM_DIGITS = new int[ALL_DIGITS + 1];
static final int[] HIGHEST_DIGIT = new int[ALL_DIGITS + 1];
{
static { // Initialize UNITS, ALL_UNITS, PEERS, NUM_DIGITS, HIGHEST_DIGIT
int i = 0;
for (int r : ROWS) { ALL_UNITS[i++] = cross(new int[]{r}, COLS); }
for (int c : COLS) { ALL_UNITS[i++] = cross(ROWS, new int[]{c}); }
@@ -234,17 +257,16 @@ public class Sudoku {
/** Search for a solution to grid. If there is an unfilled square, select one
** and try--that is, search recursively--every possible digit for the square. **/
int[] search(int[] grid, int[][] gridpool, int level) {
int[] search(int[] grid, WorkerState worker, int level) {
if (grid == null) return null;
int s = select_square(grid);
if (s == -1) return grid; // All squares filled — puzzle is solved.
for (int d : DIGITS) {
if ((d & grid[s]) > 0) {
System.arraycopy(grid, 0, gridpool[level], 0, grid.length);
int[] result = search(fill(gridpool[level], s, d), gridpool, level + 1);
if (result != null) return result;
backtracks.incrementAndGet(); // thread-safe
}
int square = selectSquare(grid);
if (square == -1) return grid; // All squares filled — puzzle is solved.
for (int candidates = grid[square]; candidates != 0; candidates &= candidates - 1) {
int digitMask = candidates & -candidates; // lowest set bit
System.arraycopy(grid, 0, worker.gridpool[level], 0, grid.length);
var result = search(fill(worker.gridpool[level], square, digitMask), worker, level + 1);
if (result != null) return result;
++worker.backtracks;
}
return null;
}
@@ -270,7 +292,7 @@ public class Sudoku {
/** Choose the unfilled square with the fewest possible values.
** Return -1 if all squares are filled (puzzle complete). **/
int select_square(int[] grid) {
int selectSquare(int[] grid) {
int square = -1;
int min = N + 1;
for (int s : SQUARES) {
@@ -285,12 +307,12 @@ public class Sudoku {
}
/** Fill grid[s] = d. Return null if this creates a contradiction. **/
int[] fill(int[] grid, int s, int d) {
if (grid == null || (grid[s] & d) == 0) return null;
grid[s] = d;
for (int p : PEERS[s]) {
if (!eliminate(grid, p, d)) return null;
/** Fill grid[s] = d. Return grid, or return null if this creates a contradiction. **/
int[] fill(int[] grid, int square, int digitMask) {
if (grid == null || (grid[square] & digitMask) == 0) return null;
grid[square] = digitMask;
for (int peer : PEERS[square]) {
if (!eliminate(grid, peer, digitMask)) return null;
}
return grid;
}
@@ -299,10 +321,12 @@ public class Sudoku {
/** Eliminate digit d as a possibility for grid[s].
** Run all three constraint-propagation routines.
** Return false if a contradiction is detected. **/
boolean eliminate(int[] grid, int s, int d) {
if ((grid[s] & d) == 0) return true; // Already eliminated
grid[s] -= d;
return arc_consistent(grid, s) && dual_consistent(grid, s, d) && naked_pairs(grid, s);
boolean eliminate(int[] grid, int square, int digitMask) {
if ((grid[square] & digitMask) == 0) return true; // Already eliminated
grid[square] &= ~digitMask;
return arcConsistent(grid, square)
&& dualConsistent(grid, square, digitMask)
&& nakedPairs(grid, square);
}
@@ -310,25 +334,26 @@ public class Sudoku {
/** Check arc consistency: either s has multiple possibilities, or its single
** remaining value can be filled without contradiction. **/
boolean arc_consistent(int[] grid, int s) {
int count = NUM_DIGITS[grid[s]];
return count >= 2 || (count == 1 && fill(grid, s, grid[s]) != null);
boolean arcConsistent(int[] grid, int square) {
int count = NUM_DIGITS[grid[square]];
return count >= 2 || (count == 1 && fill(grid, square, grid[square]) != null);
}
/** After eliminating d from grid[s], ensure d still has at least one valid
** position in each of s's units. If exactly one remains, fill it. **/
boolean dual_consistent(int[] grid, int s, int d) {
for (int[] u : UNITS[s]) {
int dPlaces = 0;
int dPlace = -1;
for (int s2 : u) {
if ((grid[s2] & d) > 0) {
if (++dPlaces > 1) break;
dPlace = s2;
boolean dualConsistent(int[] grid, int square, int digitMask) {
for (int[] unit : UNITS[square]) {
int possiblePlaces = 0;
int onlyPlace = -1;
for (int otherSquare : unit) {
if ((grid[otherSquare] & digitMask) != 0) {
if (++possiblePlaces > 1) break;
onlyPlace = otherSquare;
}
}
if (dPlaces == 0 || (dPlaces == 1 && fill(grid, dPlace, d) == null)) {
if (possiblePlaces == 0
|| (possiblePlaces == 1 && fill(grid, onlyPlace, digitMask) == null)) {
return false;
}
}
@@ -337,19 +362,20 @@ public class Sudoku {
/** If two squares in a unit share exactly the same two possible values, eliminate
** those values from every other square in that unit (naked pairs strategy). **/
boolean naked_pairs(int[] grid, int s) {
if (!runNakedPairs || NUM_DIGITS[grid[s]] != 2) return true;
int val = grid[s];
for (int s2 : PEERS[s]) {
if (grid[s2] == val) {
for (int[] u : UNITS[s]) {
if (member(s2, u)) {
int d = HIGHEST_DIGIT[val];
int d2 = val - d;
for (int s3 : u) {
if (s3 != s && s3 != s2) {
if (!eliminate(grid, s3, d) || !eliminate(grid, s3, d2)) {
** those values from every other square in that unit. **/
boolean nakedPairs(int[] grid, int square) {
if (!runNakedPairs || NUM_DIGITS[grid[square]] != 2) return true;
int pairMask = grid[square];
for (int peer : PEERS[square]) {
if (grid[peer] == pairMask) {
for (int[] unit : UNITS[square]) {
if (member(peer, unit)) {
int firstDigitMask = HIGHEST_DIGIT[pairMask];
int secondDigitMask = pairMask & ~firstDigitMask;
for (int otherSquare : unit) {
if (otherSquare != square && otherSquare != peer) {
if (!eliminate(grid, otherSquare, firstDigitMask)
|| !eliminate(grid, otherSquare, secondDigitMask)) {
return false;
}
}
@@ -364,10 +390,11 @@ public class Sudoku {
//////////////////////////////// Input ////////////////////////////////
/** Read one puzzle per line from filename and return a list of puzzle grids. **/
/** Read one puzzle per line from filename and return a list of puzzle grids.
** if -reverse option is true, also include the reversed puzzle. **/
List<int[]> readPuzzlesFromFile(String filename) throws IOException {
try (BufferedReader in = new BufferedReader(new FileReader(filename))) {
List<int[]> grids = new ArrayList<>(1000);
try (BufferedReader in = Files.newBufferedReader(Path.of(filename))) {
var grids = new ArrayList<int[]>(400);
String gridstring;
while ((gridstring = in.readLine()) != null) {
grids.add(parseGrid(gridstring));
@@ -382,7 +409,7 @@ public class Sudoku {
/** Parse a gridstring into a puzzle grid. **/
int[] parseGrid(String gridstring) {
int[] grid = new int[N * N];
var grid = new int[N * N];
int s = 0;
for (int i = 0; i < gridstring.length(); ++i) {
char c = gridstring.charAt(i);
@@ -401,24 +428,22 @@ public class Sudoku {
/** Initialize a fresh grid from puzzle, then fill known squares to trigger constraint propagation. **/
int[] initialize(int[] puzzle) {
int[] grid = new int[N * N];
void initialize(int[] puzzle, int[] grid) {
Arrays.fill(grid, ALL_DIGITS);
for (int s : SQUARES) {
if (puzzle[s] != ALL_DIGITS) fill(grid, s, puzzle[s]);
}
return grid;
}
//////////////////////////////// Output and Tests ////////////////////////////////
/** Print stats: puzzles solved, average µs, KHz, threads, backtracks, and name. **/
void printStats(int nGrids, long startTime, String name) {
void printStats(int nGrids, long startTime, String name, long backtracks) {
double usecs = (System.nanoTime() - startTime) / 1_000.0;
int bt = backtracks.getAndSet(0); // thread-safe
String line = String.format("%7d %6.1f %7.3f %7d %10.1f %s",
nGrids, usecs / nGrids, 1_000 * nGrids / usecs, nThreads, bt * 1.0 / nGrids, name);
nGrids, usecs / nGrids, 1_000 * nGrids / usecs, nThreads,
backtracks * 1.0 / nGrids, name);
synchronized (this) {
if (!headerPrinted) {
System.out.println("Puzzles μsec KHz Threads Backtracks Name\n"
@@ -448,29 +473,13 @@ public class Sudoku {
/** Return a String representing one row of the grid. **/
String rowString(int[] grid, int r) {
StringBuilder row = new StringBuilder(30);
for (int s = r * 9; s < (r + 1) * 9; ++s) {
int nd = NUM_DIGITS[grid[s]];
char cell = nd == 9 ? '.' : nd != 1 ? '?' : (char)('1' + Integer.numberOfTrailingZeros(grid[s]));
var row = new StringBuilder(30);
for (int s = r * N; s < (r + 1) * N; ++s) {
int candidateCount = NUM_DIGITS[grid[s]];
char cell = candidateCount == N ? '.' : candidateCount != 1 ? '?' : (char)('1' + Integer.numberOfTrailingZeros(grid[s]));
row.append(cell);
row.append(s % 9 == 2 || s % 9 == 5 ? " | " : " ");
row.append(s % N == BOX_SIZE - 1 || s % N == 2 * BOX_SIZE - 1 ? " | " : " ");
}
return row.toString();
}
/** Unit Tests. **/
void runUnitTests() {
assert N == 9;
assert SQUARES.length == 81;
for (int s : SQUARES) {
assert UNITS[s].length == 3;
assert PEERS[s].length == 20;
}
assert Arrays.equals(PEERS[19],
new int[]{18,20,21,22,23,24,25,26,1,10,28,37,46,55,64,73,0,2,9,11});
assert Arrays.deepToString(UNITS[19]).equals(
"[[18, 19, 20, 21, 22, 23, 24, 25, 26], [1, 10, 19, 28, 37, 46, 55, 64, 73], [0, 1, 2, 9, 10, 11, 18, 19, 20]]");
System.out.println("Unit tests pass.");
}
}
+132 -102
View File
@@ -4,9 +4,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<div style=\"text-align:right\" align=\"right\"><i>Peter Norvig<br>Oct. 2021</i></div>\n",
"<div style=\"text-align:right\" align=\"right\"><i>Peter Norvig<br>2007, 2026</i></div>\n",
"\n",
"# Solving Sudoku at 200,000 puzzles per second\n",
"# Solving Sudoku at 400,000 puzzles per second\n",
"\n",
"The rules of [**Sudoku**](http://en.wikipedia.org/wiki/Sudoku) are [simple and finite](https://clip.cafe/legally-blonde-2001/the-rules-of-hair-care-are-simple-finite-s1/?srsltid=AfmBOoqLAWkreHwp-ZaP5JeYIlVYwljjgG-7aHmt-kLQ_D0q8zvxd4GG): fill in the empty squares in the puzzle so that each column, row, and 3x3 box in the solution contains all the digits from 1 to 9. For example:\n",
"\n",
@@ -15,10 +15,18 @@
"|![](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Sudoku_Puzzle_by_L2G-20050714_standardized_layout.svg/250px-Sudoku_Puzzle_by_L2G-20050714_standardized_layout.svg.png)|![](https://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Sudoku_Puzzle_by_L2G-20050714_solution_standardized_layout.svg/250px-Sudoku_Puzzle_by_L2G-20050714_solution_standardized_layout.svg.png)|\n",
"\n",
"\n",
"In 2006, I wrote a [Python program](http://norvig.com/sudoku.html) to solve Sudoku. Soon after that, [Peter Seibel](https://gigamonkeys.com/) invited me to publish an article in [*Code Quarterly*](https://gigamonkeys.com/code-quarterly/) magazine which would go into more detail about backtracking search and constraint propagation in general, and would feature a more efficient program. Unfortunately, the magazine [folded](https://gigamonkeys.wordpress.com/2011/10/17/end-of-the-line-for-code-quarterly/) before I could finish the article. In 2021 I [updated the Python Sudoku program](Sudoku.ipynb) to Jupyter notebook form, with modern coding idioms, and now I do the same for the [more efficient Java Sudoku program](Sudoku.java). (I apologize for some remaining legacy Java idioms from decades ago).\n",
"In 2006, I wrote a [Python program](http://norvig.com/sudoku.html) designed to solve any Sudoku puzzle. As computer security expert Ben Laurie has stated, Sudoku is \"a denial of service attack on human intellect\", and I thought maybe my program would convince people they didn't need to spend excessive time playing Sudoku. \n",
"\n",
"In 2007, [Peter Seibel](https://gigamonkeys.com/) invited me to publish an article in [*Code Quarterly*](https://gigamonkeys.com/code-quarterly/) magazine which would go into more detail about backtracking search and constraint propagation in general, and would feature a more efficient program. Unfortunately, the magazine [folded](https://gigamonkeys.wordpress.com/2011/10/17/end-of-the-line-for-code-quarterly/) before I could finish [the article](https://www.norvig.com/CQ/sudoku.html). In 2021 I [updated the Python Sudoku program](Sudoku.ipynb) to Jupyter notebook form with modern coding idioms, and now I do the same for the [more efficient Java Sudoku program](Sudoku.java). \n",
"\n",
"I asked Claude and Codex to help me update my code from Java JDK 1.6 to Java JDK 26. They were very good at that. I also asked them for suggestions to make the code faster, but they didn't have any significant ideas (although the switch to a more modern threading API did speed throughput a few percent). I guess I did a good job of writing efficient Java way back in 2007.\n",
"\n",
"- [**Sudoku.java**](Sudoku.java) is the Java program discussed in this notebook.\n",
"- [**Sudoku.ipynb**](Sudoku.ipynb) is the Python notebook."
"- [**Sudoku.ipynb**](Sudoku.ipynb) is the original Python notebook.\n",
"\n",
"<div class=\"alert alert-block alert-success\">\n",
"<b><i>TLDR: The program solves over 400,000 puzzles per second , and over 30,000 puzzles per second on the hardest puzzles.</b> <i>\n",
"</div>"
]
},
{
@@ -27,7 +35,7 @@
"source": [
"# Solving a Puzzle\n",
"\n",
"In the file [sudokus1.txt](sudokus1.txt) is the single puzzle shown at the top of the page, in this format (one puzzle per line):"
"In the file [sudokus1.txt](sudokus1.txt) is the single puzzle shown at the top of the page, in this format:"
]
},
{
@@ -89,7 +97,7 @@
"source": [
"# Command Line Options\n",
"\n",
"Here are all the options for the `java Sudoku` command:"
"Here are all the options for my `java Sudoku` command:"
]
},
{
@@ -102,7 +110,9 @@
"output_type": "stream",
"text": [
"usage: java Sudoku -(no)[fghnprstuv] | -[RT]<number> | <filename> ...\n",
"E.g., -v turns verify flag on, -nov turns it off. -R and -T require a number. The options:\n",
"\n",
"Options and filenames are processed left-to-right. Use '-no' to turn an option off\n",
"E.g.: -v turns verify flag on, -nov turns it off. -R and -T require a number. The options:\n",
"\n",
" -g(rid) Print each puzzle grid and solution grid (default off)\n",
" -h(elp) Print this usage message\n",
@@ -111,10 +121,9 @@
" -r(everse) Solve the reverse of each puzzle as well as each puzzle itself (default off)\n",
" -s(ummary) Print per-file summary stats (default on)\n",
" -t(hread) Print summary stats for each thread (default off)\n",
" -u(nitTest) Run a suite of unit tests (default off)\n",
" -v(erify) Verify each solution is valid (default on)\n",
" -T<number> Concurrently run <number> threads (default 25)\n",
" -R<number> Repeat each puzzle <number> times (default 1)\n",
" -T<number> Concurrently run <number> threads (default 28)\n",
" -R<number> Repeat the solving of each puzzle <number> times (default 1)\n",
" <filename> Solve all puzzles in filename, which has one puzzle per line\n"
]
}
@@ -131,10 +140,10 @@
"\n",
"I gathered some Sudoku puzzles from various sources:\n",
"- [sudokus_hard.txt](sudokus_hard.txt): Allegedly hardest problems, form the [New Sudoku Players Forum](http://forum.enjoysudoku.com/the-hardest-sudokus-new-thread-t6539.html#p65791).\n",
"- [sudokus_17.txt](sudokus_17.txt): Puzzles with exactly 17 filled-in squares, from [Gordon Royle at the University of Western Australia](https://web.archive.org/web/20131019184812if_/http://school.maths.uwa.edu.au/~gordon/sudokumin.php).\n",
"- [sudokus_17.txt](sudokus_17.txt): Puzzles with exactly 17 filled-in squares (the minimum), from [Gordon Royle at the University of Western Australia](https://web.archive.org/web/20131019184812if_/http://school.maths.uwa.edu.au/~gordon/sudokumin.php).\n",
"- [sudokus.txt](sudokus.txt): My collection, from various sources, and some automatically generated (by permuting rows, or swapping digits).\n",
"\n",
"Hee are the number of puzzles in each file:"
"Here are the number of puzzles in each file:"
]
},
{
@@ -161,7 +170,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's run the `java Sudoku` program on all these puzzles. To get even more puzzles (more puzzles should lower variance), I use the `-r` option to include the reverse of each puzzle (the last square becomes the first, etc.) and `-R` to repeat puzzles."
"Let's run the `java Sudoku` program on all these puzzles, and show the timing results. To get even more puzzles (to lower timing variance), I use the `-r` option to include the reverse of each puzzle (the last square becomes the first, etc.) and `-R` to repeat puzzles."
]
},
{
@@ -175,35 +184,34 @@
"text": [
"Puzzles μsec KHz Threads Backtracks Name\n",
"======= ====== ======= ======= ========== ====\n",
" 3000 60.0 16.668 25 330.7 sudokus_hard.txt\n",
" 196604 4.8 210.408 25 41.7 sudokus_17.txt\n",
"1000000 3.2 311.832 25 32.5 sudokus.txt\n"
" 30000 29.1 34.403 28 330.7 sudokus_hard.txt\n",
" 983020 2.0 497.578 28 41.7 sudokus_17.txt\n",
"1000000 2.5 402.050 28 32.5 sudokus.txt\n"
]
}
],
"source": [
"!java Sudoku -r -R4 sudokus_hard.txt -R2 sudokus_17.txt sudokus.txt "
"!java Sudoku -r -R40 sudokus_hard.txt -R10 sudokus_17.txt -R2 sudokus.txt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The columns of the output are:\n",
"So the program is solving 34,000 puzzles per second on the hardest puzzles, and about 400,000 to 500,000 on the other files of more average-difficulty puzzles. The columns of the output are:\n",
"\n",
"- `Puzzles`: the total number of puzzles in each file.\n",
"- `Puzzles`: the total number of puzzles solved.\n",
"- `μsec`: the mean time to solve a puzzle in microseconds (millionths of a second).\n",
"- `KHz`: the throughput expressed as thousands of puzzles solved per second (the inverse of the μsec column).\n",
"- `Threads`: the number of concurrent threads used (the default is 25).\n",
"- `Threads`: the number of concurrent threads used.\n",
"- `Backtracks`: the average number of times per puzzle that the search guessed wrong and had to back up.\n",
"- `Name`: here the name of the file; could also be a puzzle number.\n",
"- `Name`: here the name of the file; could also be a puzzle number when -p option is on.\n",
"\n",
"**Conclusion: The program solves about 16,000 puzzles per second with the hardest puzzles, and 200,000 to 300,000 per second with easier puzzles.**\n",
"One of the fastest known Sudoku solvers, [**tdoku**](https://github.com/t-dillon/tdoku), reports similar speed (but that was in 2020, so they probably used a slower computer than my 28-core 96 GB Mac Studio M3 Ultra).\n",
"\n",
"This is a pretty good performance for such a simple program! One of the fastest known Sudoku solvers, [**tdoku**](https://github.com/t-dillon/tdoku), is twice as fast on the hardest puzzles, and only about 15% faster for the 17-clue puzzles, but is a much more complex program.\n",
"# Throughput versus Latency\n",
"\n",
"\n",
"Note that the μsec and KHz columns are displaying **throughput**: the number of puzzles that can be solved in a given amount of time. This is different than **latency**: the time it takes to solve a single puzzle from start to end. Throughput and latency are different because there are multiple threads working in parallel. If, say, there are 25 threads working in parallel on 25 CPUs and each puzzle takes 1 second to solve, then latency would be 1 second but throughput would be 25 puzzles per second. I did some experiments, and on my machine (an M3 Ultra Mac Studio), the best performance is somewhere around 25 threads (although there is 5% to 10% variance from run to run, so I couldn't nail down an optimal number). \n",
"Note that the μsec and KHz columns are displaying **throughput**: the number of puzzles that can be solved in a given amount of time. This is different than **latency**: the time it takes to solve a single puzzle from start to end. Throughput and latency are different because there are multiple threads working in parallel. If, say, there are 28 threads working in parallel on 28 CPUs and each puzzle takes 1 second to solve, then latency would be 1 second but throughput would be 28 puzzles per second. \n",
"\n",
"To measure the latency, use the `-T1` option to request a single thread:"
]
@@ -219,9 +227,9 @@
"text": [
"Puzzles μsec KHz Threads Backtracks Name\n",
"======= ====== ======= ======= ========== ====\n",
" 3000 250.5 3.992 1 292.2 sudokus_hard.txt\n",
" 49151 65.1 15.363 1 40.2 sudokus_17.txt\n",
" 250000 61.6 16.238 1 34.2 sudokus.txt\n"
" 3000 281.9 3.547 1 292.2 sudokus_hard.txt\n",
" 49151 69.3 14.430 1 40.2 sudokus_17.txt\n",
" 250000 66.3 15.082 1 34.2 sudokus.txt\n"
]
}
],
@@ -233,7 +241,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The average latency is about 250 microseconds for the hard puzzles and 60 to 65 microseconds for the easier puzzles."
"The average latency is about 300 microseconds for the hard puzzles and 70 microseconds for the easier puzzles."
]
},
{
@@ -242,7 +250,7 @@
"source": [
"# Effect of Naked Pairs Strategy\n",
"\n",
"The Java program explicitly uses the so-called [naked pairs strategy](https://www.learn-sudoku.com/naked-pairs.html) to eliminate some possible digits, whereas the Python program did not. We can check how much the naked pairs strategy helps by disabling it with the `-nonaked` option:"
"This Java program uses the so-called [naked pairs strategy](https://www.learn-sudoku.com/naked-pairs.html): If two squares in a unit share exactly the same two possible values, eliminate those values from every other square in that unit. My previous Python program did not use this strategy. The following invocation yields two lines of output, the first using the naked pairs strategy, the second without:"
]
},
{
@@ -256,20 +264,20 @@
"text": [
"Puzzles μsec KHz Threads Backtracks Name\n",
"======= ====== ======= ======= ========== ====\n",
" 250000 10.7 93.850 25 34.2 sudokus.txt\n",
" 250000 13.6 73.619 25 109.1 sudokus.txt\n"
"1000000 2.5 395.567 28 32.5 sudokus.txt\n",
"1000000 4.2 237.418 28 107.4 sudokus.txt\n"
]
}
],
"source": [
"!java Sudoku sudokus.txt -nonaked sudokus.txt"
"!java Sudoku -r -R2 -naked sudokus.txt -nonaked sudokus.txt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The program is three times as fast with naked pairs compared to without. Perhaps implementing [other strategies](https://bestofsudoku.com/sudoku-strategy) could give similar speedups. However, my feeling is that the pr ogram is already fast enough, and there would probably be diminishing returns from further strategies."
"The program is 50% faster with naked pairs compared to without. Perhaps implementing [other strategies](https://bestofsudoku.com/sudoku-strategy) could give similar speedups. However, my feeling is that the program is already fast enough, and there would probably be diminishing returns from further strategies."
]
},
{
@@ -280,7 +288,7 @@
"\n",
"What's the distribution of puzzle run times, or of backtracks, across the benchmark puzzles? \n",
"\n",
"I can get data for individual puzzles with the `-p` option, then eliminate the header lines with `tail`."
"I can get data for individual puzzles with the `-p` option, eliminate the header lines with `tail`, and pipe the results to the file `sudata.txt`:"
]
},
{
@@ -289,7 +297,7 @@
"metadata": {},
"outputs": [],
"source": [
"!java Sudoku -T1 -p sudokus_17.txt sudokus_hard.txt | tail -n +3 > sudata.txt"
"!java Sudoku -T1 -p sudokus_17.txt sudokus_hard.txt | tail -n +3 > sudata.txt"
]
},
{
@@ -333,50 +341,74 @@
" <tbody>\n",
" <tr>\n",
" <th>count</th>\n",
" <td>49528.000000</td>\n",
" <td>49528.000000</td>\n",
" <td>49528.000000</td>\n",
" <td>49527.000000</td>\n",
" <td>49527.000000</td>\n",
" <td>49527.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>mean</th>\n",
" <td>74.740426</td>\n",
" <td>21.117872</td>\n",
" <td>42.138952</td>\n",
" <td>74.695403</td>\n",
" <td>23.262107</td>\n",
" <td>42.144939</td>\n",
" </tr>\n",
" <tr>\n",
" <th>std</th>\n",
" <td>135.958244</td>\n",
" <td>9.120126</td>\n",
" <td>187.339951</td>\n",
" <td>152.543361</td>\n",
" <td>11.191693</td>\n",
" <td>187.344951</td>\n",
" </tr>\n",
" <tr>\n",
" <th>min</th>\n",
" <td>11.800000</td>\n",
" <td>0.118000</td>\n",
" <td>12.000000</td>\n",
" <td>0.099000</td>\n",
" <td>0.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1%</th>\n",
" <td>18.300000</td>\n",
" <td>1.663300</td>\n",
" <td>0.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10%</th>\n",
" <td>27.800000</td>\n",
" <td>8.029000</td>\n",
" <td>0.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25%</th>\n",
" <td>37.700000</td>\n",
" <td>15.152000</td>\n",
" <td>32.800000</td>\n",
" <td>15.414000</td>\n",
" <td>1.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50%</th>\n",
" <td>45.200000</td>\n",
" <td>22.120000</td>\n",
" <td>42.400000</td>\n",
" <td>23.599000</td>\n",
" <td>5.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75%</th>\n",
" <td>66.000000</td>\n",
" <td>26.549000</td>\n",
" <td>64.900000</td>\n",
" <td>30.534000</td>\n",
" <td>24.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>90%</th>\n",
" <td>124.500000</td>\n",
" <td>36.036000</td>\n",
" <td>80.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>99%</th>\n",
" <td>601.058000</td>\n",
" <td>54.546000</td>\n",
" <td>646.740000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>max</th>\n",
" <td>8487.500000</td>\n",
" <td>85.106000</td>\n",
" <td>10108.800000</td>\n",
" <td>83.626000</td>\n",
" <td>13438.000000</td>\n",
" </tr>\n",
" </tbody>\n",
@@ -385,14 +417,18 @@
],
"text/plain": [
" μsec KHz Backtracks\n",
"count 49528.000000 49528.000000 49528.000000\n",
"mean 74.740426 21.117872 42.138952\n",
"std 135.958244 9.120126 187.339951\n",
"min 11.800000 0.118000 0.000000\n",
"25% 37.700000 15.152000 1.000000\n",
"50% 45.200000 22.120000 5.000000\n",
"75% 66.000000 26.549000 24.000000\n",
"max 8487.500000 85.106000 13438.000000"
"count 49527.000000 49527.000000 49527.000000\n",
"mean 74.695403 23.262107 42.144939\n",
"std 152.543361 11.191693 187.344951\n",
"min 12.000000 0.099000 0.000000\n",
"1% 18.300000 1.663300 0.000000\n",
"10% 27.800000 8.029000 0.000000\n",
"25% 32.800000 15.414000 1.000000\n",
"50% 42.400000 23.599000 5.000000\n",
"75% 64.900000 30.534000 24.000000\n",
"90% 124.500000 36.036000 80.000000\n",
"99% 601.058000 54.546000 646.740000\n",
"max 10108.800000 83.626000 13438.000000"
]
},
"execution_count": 9,
@@ -404,15 +440,16 @@
"import pandas as pd\n",
"\n",
"columns = 'Puzzles μsec KHz Threads Backtracks Puzzle Number'.split()\n",
"sudata = pd.read_csv('sudata.txt', sep='\\s+', names=columns).drop(columns=['Puzzles', 'Puzzle', 'Threads', 'Number'])\n",
"sudata.describe()"
"sudata = pd.read_csv('sudata.txt', sep='\\s+', usecols=(1, 2, 4))\n",
"sudata.columns = ('μsec', 'KHz', 'Backtracks')\n",
"sudata.describe(percentiles=(.01, .10, .25, .50, .75, .90, .99))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The mean latency is 74 microseconds, the median is 45 microseconds, and the maximum is about 8 milliseconds. For backtracks, the mean is 42, the median is only 5, and the maximum is over 13,000. \n",
"The mean latency is 74 microseconds, the median is about 42 microseconds, and the maximum is about 10 milliseconds. For number of backtracks, the mean is 42, the median is only 5, at least 25% have zero or one backtrack, and the maximum is over 13,000. \n",
"\n",
"Here is a scatterplot:"
]
@@ -424,7 +461,7 @@
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAlcAAAGwCAYAAACEkkAjAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAQD9JREFUeJzt3XtcVVXC//HvARRR8SgYIIoKxVSGZaEp2qSmqTNeZmpmrCzKpjGbvNFN63H61TSTVk+T2mhlTY/OdHmsppynpsYEx7wiOiipmXlDxQuRigct5HbW7w9ly+Em4IbDgc/79eL1Yu+9zt5rr2k8X9Zae22HMcYIAAAAtvDzdgUAAACaEsIVAACAjQhXAAAANiJcAQAA2IhwBQAAYCPCFQAAgI0IVwAAADYK8HYFfIXb7daRI0cUHBwsh8Ph7eoAAIAaMMbo1KlTioyMlJ9fw/QpEa5q6MiRI4qKivJ2NQAAQB1kZWWpS5cuDXItwlUNBQcHSzr7P067du28XBsAAFATeXl5ioqKsr7HGwLhqoZKhwLbtWtHuAIAwMc05JQeJrQDAADYiHAFAABgI8IVAACAjQhXAAAANiJcAQAA2IhwBQAAYCPCFQAAgI0IVwAAADYiXAEAANiIcAUAAGAjwhUAAICNCFcAAAA24sXNAADAa4pL3Fqwcq827T+hPt1DNGnwpQrw9+2+H8IVAADwmgUr92puyi4ZSev2HJMkTRsa691KXSTfjoYAAMCnbdp/Qubc7+bctq8jXAEAAK/p0z1EjnO/O85t+zqGBQEAgNdMGnypJHnMufJ1hCsAAOA1Af5+Pj/HqjyGBQEAAGxEuAIAALAR4QoAAMBGhCsAAAAbEa4AAABsRLgCAACwEeEKAADARoQrAAAAGxGuAAAAbES4AgAAsBHhCgAAwEaEKwAAABt5NVwVFxfrd7/7naKjoxUUFKSYmBg988wzcrvdVhljjJ5++mlFRkYqKChIgwYN0ldffeVxnoKCAk2ZMkUdO3ZUmzZtNGbMGB06dMijTG5urhITE+V0OuV0OpWYmKiTJ082xG0CAIBmxKvh6vnnn9drr72m+fPn6+uvv9YLL7yg//7v/9af//xnq8wLL7ygl156SfPnz9emTZsUERGhm2++WadOnbLKJCUlaenSpVqyZInWrl2r06dPa9SoUSopKbHKjBs3ThkZGVq2bJmWLVumjIwMJSYmNuj9AgCAps9hjDHeuvioUaMUHh6uN99809r3i1/8Qq1bt9Zbb70lY4wiIyOVlJSkGTNmSDrbSxUeHq7nn39eEydOlMvl0iWXXKK33npLt912myTpyJEjioqK0meffabhw4fr66+/Vo8ePbRhwwb17dtXkrRhwwYlJCRo586duvzyyyvUraCgQAUFBdZ2Xl6eoqKi5HK51K5du/psFgAAYJO8vDw5nc4G/f72as/VDTfcoBUrVmjXrl2SpC+//FJr167VT3/6U0lSZmamsrOzNWzYMOszgYGBGjhwoNavXy9JSk9PV1FRkUeZyMhIxcXFWWVSU1PldDqtYCVJ/fr1k9PptMqUN3v2bGsI0el0Kioqyt6bBwAATVKANy8+Y8YMuVwuXXHFFfL391dJSYmeffZZ3XHHHZKk7OxsSVJ4eLjH58LDw3XgwAGrTMuWLdWhQ4cKZUo/n52drbCwsArXDwsLs8qU98QTT+jhhx+2tkt7rgAAAKrj1XD13nvv6e2339a7776rq666ShkZGUpKSlJkZKTuueceq5zD4fD4nDGmwr7yypeprHx15wkMDFRgYGBtbgcAAMC74eqxxx7T448/rttvv12S1LNnTx04cECzZ8/WPffco4iICElne546depkfS4nJ8fqzYqIiFBhYaFyc3M9eq9ycnLUv39/q8y3335b4frfffddhV4xAACAi+HVOVc//PCD/Pw8q+Dv728txRAdHa2IiAglJydbxwsLC7Vq1SorOMXHx6tFixYeZY4ePart27dbZRISEuRyubRx40arTFpamlwul1UGAADADl7tuRo9erSeffZZde3aVVdddZW2bNmil156Sb/+9a8lnR3KS0pK0qxZsxQbG6vY2FjNmjVLrVu31rhx4yRJTqdT9913nx555BGFhoYqJCREjz76qHr27KmhQ4dKkq688kqNGDFCEyZM0MKFCyVJ999/v0aNGlXpk4IAAAB15dVw9ec//1lPPvmkHnzwQeXk5CgyMlITJ07U//t//88qM336dOXn5+vBBx9Ubm6u+vbtq+XLlys4ONgqM2fOHAUEBGjs2LHKz8/XkCFDtHjxYvn7+1tl3nnnHU2dOtV6qnDMmDGaP39+w90sAABoFry6zpUv8cY6GQAA4OI0u3WuAAAAmhrCFQAAgI0IVwAAADYiXAEAANiIcAUAAGAjwhUAAICNCFcAAAA2IlwBAADYiHAFAABgI8IVAACAjQhXAAAANiJcAQAA2IhwBQAAYCPCFQAAgI0IVwAAADYiXAEAANiIcAUAAGAjwhUAAICNCFcAAAA2IlwBAADYiHAFAABgI8IVAACAjQhXAAAANiJcAQAA2IhwBQAAYCPCFQAAgI0IVwAAADYiXAEAANiIcAUAAGAjwhUAAICNCFcAAAA2IlwBAADYiHAFAABgI8IVAACAjQhXAAAANiJcAQAA2IhwBQAAYCPCFQAAgI0IVwAAADYiXAEAANiIcAUAAGAjwhUAAICNCFcAAAA2IlwBAADYiHAFAABgI8IVAACAjQhXAAAANiJcAQAA2IhwBQAAYCPCFQAAgI0IVwAAADYiXAEAANiIcAUAAGAjwhUAAICNCFcAAAA2IlwBAADYiHAFAABgI8IVAACAjQhXAAAANiJcAQAA2IhwBQAAYCPCFQAAgI0IVwAAADYiXAEAANiIcAUAAGAjwhUAAICNCFcAAAA2IlwBAADYyOvh6vDhw7rrrrsUGhqq1q1bq1evXkpPT7eOG2P09NNPKzIyUkFBQRo0aJC++uorj3MUFBRoypQp6tixo9q0aaMxY8bo0KFDHmVyc3OVmJgop9Mpp9OpxMREnTx5siFuEQAANCNeDVe5ubkaMGCAWrRooX/961/asWOH/vSnP6l9+/ZWmRdeeEEvvfSS5s+fr02bNikiIkI333yzTp06ZZVJSkrS0qVLtWTJEq1du1anT5/WqFGjVFJSYpUZN26cMjIytGzZMi1btkwZGRlKTExsyNsFAADNgMMYY7x18ccff1zr1q3TmjVrKj1ujFFkZKSSkpI0Y8YMSWd7qcLDw/X8889r4sSJcrlcuuSSS/TWW2/ptttukyQdOXJEUVFR+uyzzzR8+HB9/fXX6tGjhzZs2KC+fftKkjZs2KCEhATt3LlTl19++QXrmpeXJ6fTKZfLpXbt2tnUAgAAoD554/vbqz1XH3/8sXr37q1f/epXCgsL07XXXqs33njDOp6Zmans7GwNGzbM2hcYGKiBAwdq/fr1kqT09HQVFRV5lImMjFRcXJxVJjU1VU6n0wpWktSvXz85nU6rTHkFBQXKy8vz+AEAALgQr4arffv26dVXX1VsbKw+//xzPfDAA5o6dar+9re/SZKys7MlSeHh4R6fCw8Pt45lZ2erZcuW6tChQ7VlwsLCKlw/LCzMKlPe7NmzrflZTqdTUVFRF3ezAACgWfBquHK73bruuus0a9YsXXvttZo4caImTJigV1991aOcw+Hw2DbGVNhXXvkylZWv7jxPPPGEXC6X9ZOVlVXT2wIAAM2YV8NVp06d1KNHD499V155pQ4ePChJioiIkKQKvUs5OTlWb1ZERIQKCwuVm5tbbZlvv/22wvW/++67Cr1ipQIDA9WuXTuPHwAAgAvxargaMGCAvvnmG499u3btUrdu3SRJ0dHRioiIUHJysnW8sLBQq1atUv/+/SVJ8fHxatGihUeZo0ePavv27VaZhIQEuVwubdy40SqTlpYml8tllQEAALBDgDcv/tBDD6l///6aNWuWxo4dq40bN+r111/X66+/LunsUF5SUpJmzZql2NhYxcbGatasWWrdurXGjRsnSXI6nbrvvvv0yCOPKDQ0VCEhIXr00UfVs2dPDR06VNLZ3rARI0ZowoQJWrhwoSTp/vvv16hRo2r0pCAAAEBNeTVc9enTR0uXLtUTTzyhZ555RtHR0Zo7d67uvPNOq8z06dOVn5+vBx98ULm5uerbt6+WL1+u4OBgq8ycOXMUEBCgsWPHKj8/X0OGDNHixYvl7+9vlXnnnXc0depU66nCMWPGaP78+Q13swAAoFnw6jpXvoR1rgA0VsUlbi1YuVeb9p9Qn+4hmjT4UgX4e/0FHECj4I3vb6/2XAEALt6ClXs1N2WXjKR1e45JkqYNjfVupYBmjD9tAMDHbdp/QqVDEObcNgDvIVwBgI/r0z1EpSv2Oc5tA/AehgUBwMdNGnypJHnMuQLgPYQrAPBxAf5+zLECGhGGBQEAAGxEuAIAALAR4QoAAMBGhCsAAAAbEa4AAABsRLgCAACwEeEKAADARoQrAAAAGxGuAAAAbES4AgAAsBHhCgAAwEaEKwAAABsRrgAAAGxEuAIAALAR4QoAAMBGhCsAAAAbEa4AAABsRLgCAACwEeEKAADARoQrAAAAGxGuAAAAbES4AgAAsBHhCgAAwEaEKwAAABsRrgAAAGxEuAIAALAR4QoAAMBGhCsAAAAbEa4AAABsRLgCAACwEeEKAADARoQrAAAAGwV4uwIA0NgVl7i1YOVebdp/Qn26h2jS4EsV4M/fpgAqR7gCgAtYsHKv5qbskpG0bs8xSdK0obHerRSARos/vQDgAjbtPyFz7ndzbhsAqkK4AoAL6NM9RI5zvzvObQNAVRgWBIALmDT4UknymHMFAFUhXAHABQT4+zHHCkCNMSwIAABgI8IVAACAjQhXAAAANiJcAQAA2IhwBQAAYCPCFQAAgI0IVwAAADaqU7hyuVw6caLi6x9OnDihvLy8i64UAACLine truncated
"image/png": "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 truncated
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
@@ -441,7 +478,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"That makes it clear that there is a strong linear relation between number of backtracks and run time (with a couple of outliers).\n",
"This plot makes it clear that there is a strong linear relation between number of backtracks and run time. (Very roughly, one microsecond per backtrack.)\n",
"\n",
"One weakness of a scatter plot as a visualization tool is that there can be lots of points plotted on top of each other, and you can't tell the density of such points. A histogram portrays density better (but on only one attribute at a time):"
]
@@ -463,7 +500,7 @@
},
{
"data": {
"image/png": "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 truncated
"image/png": "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 truncated
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
@@ -504,15 +541,16 @@
" - If a check fails, back up a level in the search.\n",
"\n",
"\n",
"The [Python program](http://norvig.com/sudoku.html) was written for clarity and brevity. The [Java program](Sudoku.java) for efficiency. It uses these tricks:\n",
"The [Python program](http://norvig.com/sudoku.html) was written for clarity and brevity. The [Java program](Sudoku.java) for efficiency. The Java program uses these tricks:\n",
"- Primitive Java data types are used:\n",
" - A grid is an `int[81]` array, not a hash table. \n",
" - A square is an int (like `8`), not a string (like `'A9'`).\n",
" - A set of possible digits is an int bitset (like `0b110010100`) not a string of digits (like `'9853'`). \n",
" - A set of possible digits is bitset represented by an int (like `0b110010100`, aka 404), not a string of digits (like `'9853'`). \n",
"- Multiple puzzles are solved in parallel, in different threads.\n",
"- Rather than allocating (and then garbage collecting) a new copy of the grid at each step in the depth-first search, instead we pre-allocate a `gridpool` array of grids once and for all (at the start of each thread), and then every recursive call to `search` re-uses `gridpool[level]`. \n",
"- Rather than allocating (and then garbage collecting) a new copy of the grid at each step in the depth-first search, instead we pre-allocate a `gridpool` array of grids once and for all (at the start of each thread), and then every recursive call to `search` re-uses `gridpool[level]`, copying data into that array. \n",
"- The specific Sudoku strategy of [naked pairs](https://www.learn-sudoku.com/naked-pairs.html) is (optionally) implemented.\n",
"- The Java compiler produces code that is inherently faster than the Python interpreter.\n",
"- To warm up Java's JIT (just-in-time) code cache, I solve a few puzzles before reporting any timing results. \n",
" "
]
},
@@ -520,7 +558,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Verifying Correctness\n",
"# Is the Program Correct?\n",
"\n",
"How do we know this program (or any program) is correct? Traditionally, there are four kinds of evidence:\n",
"- A large number of example input/output pairs that give the right answer.\n",
@@ -534,25 +572,8 @@
" - Each unit in the solution contains all nine digits.\n",
" - Squares in the puzzle that are filled with a digit keep that same digit in the solution.\n",
"- I have looked at some example solutions and double-checked some with an [online Sudoku](https://sudokuspoiler.azurewebsites.net/) program. [LGTM](https://www.dictionary.com/e/acronyms/lgtm/).\n",
"- The `-u` option runs a suite of unit tests.\n",
"- I have looked at the code carefully, but I am nowhere near a proof of correctness.\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Unit tests pass.\n"
]
}
],
"source": [
"!java Sudoku -u "
"- I have looked at the code carefully, but I am nowhere near a proof of correctness.\n",
"- It would be nice to have a suite of unit tests."
]
},
{
@@ -564,7 +585,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 12,
"metadata": {},
"outputs": [
{
@@ -718,21 +739,30 @@
"\n",
"There are a few things I didn't get a chance to explore; maybe you can:\n",
"\n",
"- Would the program be even faster in Golang or C++ or Rust? Or with some more optimizations?\n",
"- Are there more algorithmic optimizations to try?\n",
"- Would the program be even faster if translated to Golang or C++ or Rust?\n",
"- Could you use a satisfiability prover such as DPLL to solve puzzles, as [[**tdoku**](https://github.com/t-dillon/tdoku) does?\n",
"- The depth-first search makes a choice to *fill* some square with a digit. What if instead the choice was to *eliminate* a digit from the square? At first glance it seems that would be slower, because more choices would be required, but would constraint propagation make it work well?\n",
"- On each recursive call, the depth-first search copies the current grid into a new grid (re-using the one in `gridpool[level]`). That requires copying an array of 81 ints. Would it be faster to instead make changes directly to the current grid, and then undo the changes when failure is detected? You would need to keep track of the changes made so that they can be undone. My guess is that this would make the code more complex and not much faster (if any), but you might want to try it.\n",
"- In the theory of constraint propagation, [shaving](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.175.7143&rep=rep1&type=pdf) means guessing a value for some variable, detecting a contradiction, and then keeping track of the fact that the value is not possible. But in our program, when we guess wrong we don't keep track of anything. Can the program be made faster by incorporating shaving?\n",
"- Can you create an adversarial puzzle, where the program guesses wrong the maximal number of times? In other words, if you draw a tree of choices, what's a puzzle with a tree that has the solution in the bottom-right corner? How much time and how many backtracks does that puzzle take to solve?\n",
"- What [other Sudoku strategies](https://bestofsudoku.com/sudoku-strategy) can be implemented? Can you find a suite of strategies that will solve all the puzzles with no search? \n",
"- Can you develop a system to rank the difficulty of puzzles, based on the complexity of the strategies needed to solve it?\n",
"- What [other Sudoku strategies](https://bestofsudoku.com/sudoku-strategy) can be implemented? Can you find a suite of strategies that will solve all the puzzles with no backtracking search? \n",
"- Can you develop a system to rank the difficulty of puzzles, based on the complexity of the strategies needed to solve it?\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"One final word: I got around to writing this up after a friend mentioned to me \"*Hey, a while back didn't you do a Sudoku program that could solve like **a hundred puzzles a second** or something?*\" I felt like Robert Wagner when he had to explain to Dr. Evil that \"*a million dollars isn't exactly a lot of money these days.*\" \n",
"\n",
"One final word: I got around to writing this up after a friend mentioned to me \"*Hey, a while back didn't you do a Sudoku program that could solve like **a dozen puzzles a second** or something?*\" I felt like Robert Wagner when he had to explain to Dr. Evil that \"*a million dollars isn't exactly a lot of money these days.*\" So this is for all of you who were cryogenically frozen in 1967 (and for all of you who weren't).\n",
"<img src=\"https://upload.wikimedia.org/wikipedia/en/1/16/Drevil_million_dollars.jpg\" height=\"249\" width=\"201\">\n",
"\n",
"|&nbsp;|&nbsp;|\n",
"|---|---|\n",
"|![](https://upload.wikimedia.org/wikipedia/en/1/16/Drevil_million_dollars.jpg)|![](Number_2.webp)|\n",
"|\"*One **dozen** puzzles per second!*|*A dozen isn't exactly a lot of throughput these days...*|\n",
"</center>"
"*One **hundred** puzzles per second!*\n",
"\n",
"<img src=\"Number_2.png\" height=\"249\" width=\"201\">\n",
"\n",
"*A hundred isn't exactly a lot of throughput these days...*"
]
}
],
+49441 -49441
View File
File diff suppressed because it is too large. Load diff
+24 -17
View File
@@ -3,9 +3,22 @@
notebooks = {
'New': [], # Gets updated automatically
'Large Language Models': [
("AlphaCode Automated Programming", 2022, 'AlphaCode.ipynb', "Analysis of AlphaCode's automated solution to a coding problem"),
('Advent of Code 2025: AI LLM Edition', 2025, 'Advent-2025-AI.ipynb', 'Using large language coding models to solve AoC puzzles'),
("Effectiveness of Language Models", 2019, 'Goldberg.ipynb', "A re-implementation in Python 3 of Yoav Goldberg's unreasonably effective character-level n-gram language model."),
('How to Do Things with Words: NLP in Python', 2018, 'How%20to%20Do%20Things%20with%20Words.ipynb', 'Spelling Correction, Secret Codes, Word Segmentation, and more'),
('The Languages of English, Math, and Programming', 2024, 'Triplets.ipynb', 'LLMs do better at producing a program to solve this puzzle than they do at solving it directly'),
("LLMs, Theory of Mind, and Cheryl's Birthday", 2024, 'CherylMind.ipynb', "Do LLMs have enough theory of mind to solve the Cheryl's Birthday puzzle?"),
("One Letter Off", 2023, 'OneLetterOff.ipynb', "Word game; use of large language model word embeddings to generate clues."),
('Project Euler #1–100 by a Human', 2026, 'Euler.ipynb', 'Solutions to the first 100 Project Euler math/programming problems'),
('Project Euler #1–100 by Fable LLM', 2026, 'Euler-Fable.ipynb', 'Solutions to Project Euler math/programming problems by Claude Fable LLM'),
('Project Euler #1–100 by Opus LLM', 2026, 'Euler-Opus.ipynb', 'Solutions to Project Euler math/programming problems by Claude Opus LLM'),
('Project Euler #1–100 by Kimi LLM', 2026, 'Euler-Kimi.ipynb', 'Solutions to Project Euler math/programming problems by Kimi LLM'),
],
'Programming Examples': [
("AlphaCode Automated Programming", 2022, 'AlphaCode.ipynb', "Analysis of AlphaCode's automated solution to a coding problem"),
("Approximating Pi with a Fraction", 2026, 'RationalPi.ipynb', "Using continued fractions to find fractions (like 22/7) that are close to pi."),
('The Babylonian Number System', 2022, 'Babylonian%20digits.ipynb', 'Translating between Babylonian and traditional number systems.'),
("Beal's Conjecture Revisited", 2018, 'Beal.ipynb', "A search for counterexamples to Beal's Conjecture"),
@@ -14,7 +27,7 @@ notebooks = {
('Chaos with Triangles', 2019, 'Sierpinski.ipynb', 'A surprising appearance of the Sierpinski triangle in a random walk'),
("Conway's Game of Life", 2017, 'Life.ipynb', 'The cellular automata zero-player game'),
('Generating and Solving Mazes', 2020, 'Maze.ipynb', 'Make a maze by generating a random tree superimposed on a grid and solve it'),
('The Languages of English, Math, and Programming', 2024, 'Triplets.ipynb', 'LLMs do better at producing a program to solve this puzzle than they do at solving it directly'),
('Largest prime factor', 2026, 'Euler3.ipynb', 'For beginner programmers: How to find the largest prime factor of a number. Project Euler #3.'),
("Mel's Konane Board", 2021, 'Konane.ipynb', 'Solving the game of Konane (Hawaiian checkers).'),
('Photo Focal Lengths', 2020, 'PhotoFocalLengths.ipynb', 'Generate charts of what focal lengths were used on a photo trip'),
('Pickleball Tournament', 2018, 'Pickleball.ipynb', 'Scheduling a doubles tournament fairly and efficiently'),
@@ -22,7 +35,7 @@ notebooks = {
('Tracking Polls: Electoral Votes', 2020, 'Electoral%20Votes.ipynb',
'How many electoral votes would Trump get if he wins the state where he has positive net approval?')],
'Advent of Code and Project Euler': [
'Advent of Code': [
('Advent of Code 2025: AI LLM Edition', 2025, 'Advent-2025-AI.ipynb', 'Using large language coding models to solve AoC puzzles'),
('Advent of Code 2025', 2025, 'Advent-2025.ipynb', 'Puzzle site with a coding puzzle for 12 days of Christmas, December 2025'),
('Advent of Code 2024', 2024, 'Advent-2024.ipynb', 'Puzzle site with a coding puzzle each day of Advent, December 2024'),
@@ -34,15 +47,9 @@ notebooks = {
('Advent of Code 2017', 2017, 'Advent-2017.ipynb', 'Puzzle site with a coding puzzle each day of Advent, December 2017'),
('Advent of Code 2016', 2016, 'Advent-2016.ipynb', 'Puzzle site with a coding puzzle each day of Advent, December 2016'),
('Advent of Code Utilities', 2022, 'AdventUtils.ipynb', 'Utility functions for Advent of Code puzzles'),
('Project Euler #1–100 by a Human', 2026, 'Euler.ipynb', 'Solutions to the first 100 Project Euler math/programming problems'),
('Project Euler #1–100 by Fable LLM', 2026, 'Euler-Fable.ipynb', 'Solutions to Project Euler math/programming problems by Claude Fable LLM'),
('Project Euler #1–100 by Opus LLM', 2026, 'Euler-Opus.ipynb', 'Solutions to Project Euler math/programming problems by Claude Opus LLM'),
('Project Euler #1–100 by Kimi LLM', 2026, 'Euler-Kimi.ipynb', 'Solutions to Project Euler math/programming problems by Kimi LLM'),
('Largest prime factor', 2026, 'Euler3.ipynb', 'For beginner programmers: How to find the largest prime factor of a number. Project Euler #3.'),
],
'Probability and Uncertainty': [
("Effectiveness of Language Models", 2019, 'Goldberg.ipynb', "A re-implementation in Python 3 of Yoav Goldberg's unreasonably effective character-level n-gram language model."),
('A Concrete Introduction to Probability', 2018, 'Probability.ipynb', 'Code and examples of the basic principles of Probability Theory'),
('Probability, Paradox, and the Reasonable Person Principle', 2016, 'ProbabilityParadox.ipynb', 'Some classic paradoxes in Probability Theory, and how to think about disagreements'),
('Estimating Probabilities with Simulations', 2020, 'ProbabilitySimulation.ipynb', 'When the sample space is too complex, simulations can estimate probabilities'),
@@ -61,22 +68,24 @@ notebooks = {
("Euler's Sum of Powers Conjecture", 2018, "Euler's%20Conjecture.ipynb", 'Solving a 200-year-old puzzle by finding integers that satisfy a<sup>5</sup> + b<sup>5</sup> + c<sup>5</sup> + d<sup>5</sup> = e<sup>5</sup>'),
('Four 4s, Five 5s, and Countdowns', 2020, 'Countdown.ipynb', 'Solving the equation 10 _ 9 _ 8 _ 7 _ 6 _ 5 _ 4 _ 3 _ 2 _ 1 = 2016. Originally from an Alex Bellos puzzle'),
('How to Count Things', 2020, 'How%20To%20Count%20Things.ipynb', 'Combinatorial math: how to count how many things there are, when there are a lot of them'),
('KenKen (Sudoku-like Puzzle)', 2021, 'KenKen.ipynb', 'A Sudoku-like puzzle, but with arithmetic.'),
('Number Bracelets Game', 2024, 'NumberBracelets.ipynb', 'A game involving numbered beads on a circular bracelet.'),
('Pairing Socks', 2019, 'Socks.ipynb', 'What is the probability that you will be able to pair up socks as you randomly pull them out of the dryer?'),
('Sicherman Dice', 2018, 'Sicherman%20Dice.ipynb', 'Find a pair of dice that is like a regular pair of dice, only different'),
("Sol Golomb's Rectangle Puzzle", 2014, 'Golomb-Puzzle.ipynb', 'A Puzzle involving placing rectangles of different sizes inside a square'),
("Stubborn number endings", 2024, 'Stubborn.ipynb', 'Any number ending in 5 has a square that also ends in 5. What other endings are like this?'),
('Star Battle (Sudoku-like Puzzle)', 2021, 'StarBattle.ipynb', 'Fill-in-the-grid puzzle similar to Sudoku'),
('Sudoku', 2006, 'Sudoku.ipynb', 'Classic fill-in-the-grid puzzle'),
('Sudoku: 200,000 puzzles/second in Java', 2021, 'SudokuJava.ipynb', 'A version of the Sudoku solver using parallel threads and other optimizations'),
('Square Sum Puzzle', 2020, 'SquareSum.ipynb', 'Place the numbers from 1 to n in a chain (or a circle) such that adjacent pairs sum to a perfect square'),
('Square Sum Chain Puzzle', 2020, 'SquareSum.ipynb', 'Place the numbers from 1 to n in a chain (or a circle) such that adjacent pairs sum to a perfect square'),
("When is Cheryl's Birthday?", 2020, 'Cheryl.ipynb', "Solving the *Cheryl's Birthday* logic puzzle"),
('When Cheryl Met Eve: A Birthday Story', 2015, 'Cheryl-and-Eve.ipynb', "Inventing new puzzles in the Style of Cheryl's Birthday"),
("LLMs, Theory of Mind, and Cheryl's Birthday", 2024, 'CherylMind.ipynb', "Do LLMs have enough theory of mind to solve the Cheryl's Birthday puzzle?"),
('xkcd 1313: Regex Golf', 2015, 'xkcd1313.ipynb', 'Find the smallest regular expression; inspired by Randall Munroe'),
('xkcd 1313: Regex Golf (Part 2: Infinite Problems)', 2015, 'xkcd1313-part2.ipynb', 'Regex Golf: better, faster, funner (with Stefan Pochmann)')],
'Sudoku and Similar Puzzles': [
('Sudoku', 2006, 'Sudoku.ipynb', 'Classic fill-in-the-grid puzzle solved in Python'),
('Sudoku: 400,000 puzzles/second in Java', 2021, 'SudokuJava.ipynb', 'A Java version of the Sudoku solver using parallel threads and other optimizations'),
('KenKen (Sudoku-like Puzzle)', 2021, 'KenKen.ipynb', 'A Sudoku-like puzzle, but with arithmetic.'),
('Star Battle (Sudoku-like Puzzle)', 2021, 'StarBattle.ipynb', 'Fill-in-the-grid puzzle similar to Sudoku'),
],
'Word Puzzles': [
('Boggle / Inverse Boggle', 2020, 'Boggle.ipynb', 'Find all the words on a Boggle board; then find a board with a lot of words'),
('Chemical Element Spelling', 2020, 'ElementSpelling.ipynb', 'Spelling words using the chemical element symbols, like CoIn'),
@@ -84,9 +93,7 @@ notebooks = {
('Equilength Numbers: FOUR = 4', 2020, 'equilength-numbers.ipynb', 'What number names have the same letter count as the number they name (such as FOUR)?'),
('Gesture Typing', 2017, 'Gesture%20Typing.ipynb', 'What word has the longest path on a gesture-typing smartphone keyboard?'),
('Ghost: A Word game', 2017, 'Ghost.ipynb', 'The word game Ghost (add letters, try to avoid making a word)'),
('How to Do Things with Words: NLP in Python', 2018, 'How%20to%20Do%20Things%20with%20Words.ipynb', 'Spelling Correction, Secret Codes, Word Segmentation, and more'),
("Let's Code About Bike Locks", 2015, 'Fred%20Buns.ipynb', 'A tale of a bicycle combination lock that uses letters instead of digits. Inspired by Bike Snob NYC'),
("One Letter Off", 2023, 'OneLetterOff.ipynb', "Word game; use of a large language model to generate clues."),
('Scrabble: Refactoring a Crossword Game Program', 2017, 'Scrabble.ipynb', 'Refactoring the Scrabble / Word with Friends game from Udacity 212'),
('Spelling Bee', 2020, 'SpellingBee.ipynb', 'Find the highest-scoring board for the NY Times Spelling Bee puzzle'),
('Translating English into Propositional Logic', 2017, 'PropositionalLogic.ipynb', 'Automatically convert informal English sentences into formal Propositional Logic'),