Merge branch 'master' into fix-randall-munroe
This commit is contained in:
commit
2a4141a4b6
183
README.md
183
README.md
@ -1,104 +1,123 @@
|
||||
|
||||
|
||||
<div align="right" style="text-align:right"><i>Peter Norvig<br><a href="https://github.com/norvig/pytudes/blob/master/LICENSE">MIT License</a><br>2015-2020</i></div>
|
||||
|
||||
# pytudes
|
||||
|
||||
"An *étude* (a French word meaning *study*) is an instrumental musical composition, usually short, of considerable difficulty, and designed to provide practice material for perfecting a particular musical skill." — [Wikipedia](https://en.wikipedia.org/wiki/%C3%89tude)
|
||||
|
||||
This project contains **pytudes**—Python programs, usually short, for perfecting programming skills.
|
||||
This project contains **pytudes**—Python programs, usually short, for perfecting particular programming skills.
|
||||
Some programs are in Jupyter (`.ipynb`) notebooks, some in `.py` files. For each notebook you can:
|
||||
- Click on [co](https://colab.research.google.com) to **run** the file on Colab
|
||||
- Click on [dn](https://deepnote.com) to **run** the notebook on DeepNote
|
||||
- Click on [my](https://mybinder.org) to **run** the notebook on MyBinder
|
||||
- Click on [nb](https://nbviewer.jupyter.org/) to **view** the notebook on NBViewer
|
||||
- Click on the title to **view** the notebook on github.
|
||||
- Hover over the title to **view** a description.
|
||||
|
||||
Some are in Jupyter (IPython) notebooks, some in `.py` files. You can view the files here on github, or click the **NB** link to view them on [nbviewer](http://nbviewer.jupyter.org/) (which sometimes works better). If you want to *run* the notebooks, not just view them, you can clone the project, or run all the notebooks online by clicking this button: [](https://mybinder.org/v2/gh/norvig/pytudes/master), or click the **DN** link to run each individual notebook on [deepnote](https://beta.deepnote.org/).
|
||||
|
||||
# Index of Jupyter (IPython) Notebooks
|
||||
|
||||
|
||||
|Run|Year|Programming Examples|
|
||||
|---|----|---|
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Advent-2018.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FAdvent-2018.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FAdvent-2018.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Advent-2018.ipynb) | 2018 | <b><a href="ipynb/Advent-2018.ipynb" title="Puzzle site with a coding puzzle each day for Advent 2018 ">Advent of Code 2018</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Advent%202017.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FAdvent%202017.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FAdvent%202017.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Advent%202017.ipynb) | 2017 | <b><a href="ipynb/Advent%202017.ipynb" title="Puzzle site with a coding puzzle each day for Advent 2017">Advent of Code 2017</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Advent%20of%20Code.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FAdvent%20of%20Code.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FAdvent%20of%20Code.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Advent%20of%20Code.ipynb) | 2016 | <b><a href="ipynb/Advent%20of%20Code.ipynb" title="Puzzle site with a coding puzzle each day for Advent 2016*">Advent of Code 2016</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Beal.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FBeal.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FBeal.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Beal.ipynb) | 2018 | <b><a href="ipynb/Beal.ipynb" title="A search for counterexamples to Beal's Conjecture">Beal's Conjecture Revisited</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Bike%20Speed%20versus%20Grade.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FBike%20Speed%20versus%20Grade.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FBike%20Speed%20versus%20Grade.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Bike%20Speed%20versus%20Grade.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/Bike%20Speed%20versus%20Grade.ipynb" title="How fast can I bike as the route gets steeper?">Bike Speed Versus Grade</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Cant-Stop.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCant-Stop.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FCant-Stop.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Cant-Stop.ipynb) | 2018 | <b><a href="ipynb/Cant-Stop.ipynb" title="Optimal play in a dice board game">Can't Stop</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Sierpinski.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FSierpinski.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FSierpinski.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Sierpinski.ipynb) | 2019 | <b><a href="ipynb/Sierpinski.ipynb" title="A surprising appearance of the Sierpinski triangle in a random walk between vertexes">Chaos with Triangles</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Life.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FLife.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FLife.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Life.ipynb) | 2017 | <b><a href="ipynb/Life.ipynb" title="The cellular automata zero-player game">Conway's Game of Life</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Dice%20Baseball.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FDice%20Baseball.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FDice%20Baseball.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Dice%20Baseball.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/Dice%20Baseball.ipynb" title="Simulating baseball games">Dice Baseball</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Maze.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FMaze.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FMaze.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Maze.ipynb) | <b><u>2020</b></u> | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/PhotoFocalLengths.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FPhotoFocalLengths.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FPhotoFocalLengths.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/PhotoFocalLengths.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/PhotoFocalLengths.ipynb" title="Generate charts of what focal lengths were used on a photo trip.">Photo Focal Lengths</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Pickleball.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FPickleball.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FPickleball.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Pickleball.ipynb) | 2018 | <b><a href="ipynb/Pickleball.ipynb" title="Scheduling a doubles tournament fairly and efficiently">Pickleball Tournament</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Project%20Euler%20Utils.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FProject%20Euler%20Utils.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FProject%20Euler%20Utils.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Project%20Euler%20Utils.ipynb) | 2017 | <b><a href="ipynb/Project%20Euler%20Utils.ipynb" title="My utility functions for the Project Euler problems, including `Primes` and `Factors`">Project Euler Utilities</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Orderable%20Cards.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FOrderable%20Cards.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FOrderable%20Cards.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Orderable%20Cards.ipynb) | 2018 | <b><a href="ipynb/Orderable%20Cards.ipynb" title="Can you get your hand of cards into a nice order with just one move?">Properly Ordered Card Hands</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Electoral%20Votes.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FElectoral%20Votes.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FElectoral%20Votes.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Electoral%20Votes.ipynb) | <b><u>2020</b></u> | <b><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 Trump: Electoral Votes</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/WWW.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FWWW.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FWWW.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/WWW.ipynb) | 2019 | <b><a href="ipynb/WWW.ipynb" title="Computing the probability of winning the NBA title, for my home town Warriors, or any other team">WWW: Who Will Win (NBA Title)?</a></b> |
|
||||
|
||||
|Programming Examples|
|
||||
|---|
|
||||
|[Advent of Code 2018](ipynb/Advent-2018.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Advent-2018.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FAdvent-2018.ipynb)**<br>*Puzzle site with a coding puzzle each day for Advent 2018 .*|
|
||||
|[Advent of Code 2017](ipynb/Advent%202017.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Advent%202017.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FAdvent%25202017.ipynb)**<br>*Puzzle site with a coding puzzle each day for Advent 2017.*|
|
||||
|[Advent of Code 2016](ipynb/Advent%20of%20Code.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Advent%20of%20Code.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FAdvent%2520of%2520Code.ipynb)**<br>*Puzzle site with a coding puzzle each day for Advent 2016*.|
|
||||
|[Project Euler Utilities](ipynb/Project%20Euler%20Utils.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Project%20Euler%20Utils.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FProject%2520Euler%2520Utils.ipynb)**<br>*My utility functions for the Project Euler problems, including `Primes` and `Factors`.*|
|
||||
|[Translating English Sentences into Propositional Logic Statements](ipynb/PropositionalLogic.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/PropositionalLogic.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FPropositionalLogic.ipynb)**<br>*Automatically converting informal English sentences into formal Propositional Logic.*|
|
||||
|[Beal's Conjecture Revisited](ipynb/Beal.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Beal.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FBeal.ipynb)**<br>*A search for counterexamples to Beal's Conjecture*|
|
||||
|[WWW: Who Will Win (NBA Title)?](ipynb/WWW.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/WWW.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FWWW.ipynb)**<br>*Computing the probability of winning the NBA title, for my home town Warriors, or any other team.*|
|
||||
|[Pickleball Tournament](ipynb/Pickleball.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Pickleball.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FPickleball.ipynb)**<br>*Scheduling a doubles tournament fairly and efficiently.*|
|
||||
|[Dice Baseball](ipynb/Dice%20Baseball.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Dice%2520Baseball.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FDice%2520Baseball.ipynb)**<br>*Simulating baseball games.*|
|
||||
|[Conway's Game of Life](ipynb/Life.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Life.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FLife.ipynb)**<br>*The cellular automata zero-player game.*|
|
||||
|[A Chaos Game with Triangles](ipynb/Sierpinski.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Sierpinski.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FSierpinski.ipynb)**<br>*A surprising appearance of the Sierpinski triangle in a random walk between vertexes.*|
|
||||
|[Generating Mazes](ipynb/Maze.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Maze.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FMaze.ipynb)**<br>*Make a maze by generating a random tree superimposed on a grid.*|
|
||||
|[Weighing Twelve Balls](ipynb/TwelveBalls.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/TwelveBalls.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FTwelveBalls.ipynb)**<br>*A puzzle where you are given some billiard balls and a balance scale, and asked to find the one ball that is heavier or lighter, in a limited number of weighings.*|
|
||||
|[Can't Stop](ipynb/Cant-Stop.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Cant-Stop.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCant-Stop.ipynb)**<br>*Optimal play in a dice board game.*|
|
||||
|[Bike Speed Versus Grade](ipynb/Bike%20Speed%20versus%20Grade.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Bike%20Speed%20versus%20Grade.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FBike%2520Speed%2520versus%2520Grade.ipynb)**<br>*How fast can I bike as the route gets steeper?*|
|
||||
|[Properly Ordered Card Hands](ipynb/Orderable%20Cards.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Orderable%20Cards.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FOrderable%2520Cards.ipynb)**<br>*Can you get your hand of cards into a nice order with just one move?*|
|
||||
|[Tracking Trump: Electoral Votes](ipynb/Electoral%20Votes.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Electoral%20Votes.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FElectoral%20Votes.ipynb)**<br>*How many electoral votes would Trump get if he wins the state where he has positive net approval?*|
|
||||
|Run|Year|Logic and Number Puzzles|
|
||||
|---|----|---|
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Cryptarithmetic.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCryptarithmetic.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FCryptarithmetic.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Cryptarithmetic.ipynb) | 2014 | <b><a href="ipynb/Cryptarithmetic.ipynb" title="Substitute digits for letters and make NUM + BER = PLAY">Cryptarithmetic</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Countdown.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCountdown.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FCountdown.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Countdown.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/Countdown.ipynb" title="Solving the equation 10 _ 9 _ 8 _ 7 _ 6 _ 5 _ 4 _ 3 _ 2 _ 1 = 2016. From an Alex Bellos puzzle">Four 4s, Five 5s, Equilength Numbers, and Countdown to 2016</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/flipping.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2Fflipping.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2Fflipping.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/flipping.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/flipping.ipynb" title="Can you go through a deck of cards, guessing higher or lower correctly for each card?">Flipping Cards: A Guessing Game</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/NightKing.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FNightKing.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FNightKing.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/NightKing.ipynb) | 2019 | <b><a href="ipynb/NightKing.ipynb" title="Investigasting a battle between the army of the dead and the army of the living">How Many Soldiers to Beat the Night King?</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Fred%20Buns.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FFred%20Buns.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FFred%20Buns.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Fred%20Buns.ipynb) | 2015 | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Socks.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FSocks.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FSocks.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Socks.ipynb) | 2019 | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Mean%20Misanthrope%20Density.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FMean%20Misanthrope%20Density.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FMean%20Misanthrope%20Density.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Mean%20Misanthrope%20Density.ipynb) | 2017 | <b><a href="ipynb/Mean%20Misanthrope%20Density.ipynb" title="How crowded will this neighborhood be, if nobody wants to live next door to anyone else?">The Puzzle of the Misanthropic Neighbors</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Riddler%20Battle%20Royale.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FRiddler%20Battle%20Royale.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FRiddler%20Battle%20Royale.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Riddler%20Battle%20Royale.ipynb) | 2017 | <b><a href="ipynb/Riddler%20Battle%20Royale.ipynb" title="A puzzle involving allocating your troops and going up against an opponent">Riddler: Battle Royale</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/RiddlerLottery.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FRiddlerLottery.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FRiddlerLottery.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/RiddlerLottery.ipynb) | 2019 | <b><a href="ipynb/RiddlerLottery.ipynb" title="Can you find what lottery number tickets these five friends picked?">Riddler Lottery</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/TourDe538.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FTourDe538.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FTourDe538.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/TourDe538.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/TourDe538.ipynb" title="Solve a puzzle involving the best pace for a bicycle race.">Riddler: Tour de 538</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Sicherman%20Dice.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FSicherman%20Dice.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FSicherman%20Dice.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Sicherman%20Dice.ipynb) | 2018 | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Golomb-Puzzle.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FGolomb-Puzzle.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FGolomb-Puzzle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Golomb-Puzzle.ipynb) | 2014 | <b><a href="ipynb/Golomb-Puzzle.ipynb" title="A Puzzle involving placing rectangles of different sizes inside a square">Sol Golomb's Rectangle Puzzle</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Cheryl.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCheryl.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FCheryl.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Cheryl.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/Cheryl.ipynb" title="Solving the *Cheryl's Birthday* logic puzzle">When is Cheryl's Birthday? (new: Mad Cheryl)</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Cheryl-and-Eve.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCheryl-and-Eve.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FCheryl-and-Eve.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Cheryl-and-Eve.ipynb) | 2015 | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/xkcd1313.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2Fxkcd1313.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2Fxkcd1313.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/xkcd1313.ipynb) | 2015 | <b><a href="ipynb/xkcd1313.ipynb" title="Find the smallest regular expression; inspired by Randall Munroe">xkcd 1313: Regex Golf</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/xkcd1313-part2.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2Fxkcd1313-part2.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2Fxkcd1313-part2.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/xkcd1313-part2.ipynb) | 2015 | <b><a href="ipynb/xkcd1313-part2.ipynb" title="Regex Golf: better, faster, funner. With Stefan Pochmann.">xkcd 1313: Regex Golf (Part 2: Infinite Problems)</a></b> |
|
||||
|
||||
|Logic and Number Puzzles|
|
||||
|---|
|
||||
|[When is Cheryl's Birthday?](ipynb/Cheryl.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Cheryl.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCheryl.ipynb)**<br>*Solving the "Cheryl's Birthday" logic puzzle.*|
|
||||
|[When Cheryl Met Eve: A Birthday Story](ipynb/Cheryl-and-Eve.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Cheryl-and-Eve.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCheryl-and-Eve.ipynb)**<br>*Inventing new puzzles in the Style of Cheryl's Birthday.*|
|
||||
|[How Many Soldiers Do You Need to Beat the Night King?](ipynb/NightKing.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/NightKing.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FNightKing.ipynb)**<br>*Investigasting a battle between the army of the dead and the army of the living.*|
|
||||
|[The Devil and the Coin Flip Game](ipynb/Coin%20Flip.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Coin%20Flip.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCoin%2520Flip.ipynb)**<br>*How to beat the Devil at his own game.*|
|
||||
|[The Puzzle of the Misanthropic Neighbors](ipynb/Mean%20Misanthrope%20Density.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Mean%20Misanthrope%20Density.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FMean%2520Misanthrope%2520Density.ipynb)**<br>*How crowded will this neighborhood be, if nobody wants to live next door to anyone else?*|
|
||||
|[Four 4s, Five 5s, and Countdown to 2016](ipynb/Countdown.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Countdown.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCountdown.ipynb)**<br>*Solving the equation 10 _ 9 _ 8 _ 7 _ 6 _ 5 _ 4 _ 3 _ 2 _ 1 = 2016. From an Alex Bellos puzzle.*|
|
||||
|[Sicherman Dice](ipynb/Sicherman%20Dice.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Sicherman%20Dice.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FSicherman%2520Dice.ipynb)**<br>*Find a pair of dice that is like a regular pair of dice, only different.*|
|
||||
|[Sol Golomb's Rectangle Puzzle](ipynb/Golomb-Puzzle.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Golomb-Puzzle.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FGolomb-Puzzle.ipynb)**<br>*A Puzzle involving placing rectangles of different sizes inside a square.*|
|
||||
|[Cryptarithmetic](ipynb/Cryptarithmetic.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Cryptarithmetic.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCryptarithmetic.ipynb)**<br>*Substitute digits for letters and make NUM + BER = PLAY.*|
|
||||
|[The Riddler: Battle Royale](ipynb/Riddler%20Battle%20Royale.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Riddler%20Battle%20Royale.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FRiddler%2520Battle%2520Royale.ipynb)**<br>*A puzzle involving allocating your troops and going up against an opponent.*|
|
||||
|Run|Year|Word Puzzles|
|
||||
|---|----|---|
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Boggle.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FBoggle.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FBoggle.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Boggle.ipynb) | <b><u>2020</b></u> | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Scrabble.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FScrabble.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FScrabble.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Scrabble.ipynb) | 2017 | <b><a href="ipynb/Scrabble.ipynb" title="Refactoring the Scrabble / Word with Friends game from Udacity 212">Crossword Game : Refactoring a Scrabble Program</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/ElementSpelling.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FElementSpelling.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FElementSpelling.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/ElementSpelling.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/ElementSpelling.ipynb" title="Spelling words using the chemical element symbols, like CoIn">Chemical Element Spelling</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Gesture%20Typing.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FGesture%20Typing.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FGesture%20Typing.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Gesture%20Typing.ipynb) | 2017 | <b><a href="ipynb/Gesture%20Typing.ipynb" title="What word has the longest path on a gesture-typing smartphone keyboard?">Gesture Typing</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Ghost.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FGhost.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FGhost.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Ghost.ipynb) | 2017 | <b><a href="ipynb/Ghost.ipynb" title="The word game Ghost (add letters, try to avoid making a word)">Ghost: A Word game</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FHow%20to%20Do%20Things%20with%20Words.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FHow%20to%20Do%20Things%20with%20Words.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) | 2018 | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/SpellingBee.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FSpellingBee.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FSpellingBee.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/SpellingBee.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/SpellingBee.ipynb" title="Find the highest-scoring board for the NY Times Spelling Bee puzzle">Spelling Bee</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/PropositionalLogic.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FPropositionalLogic.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FPropositionalLogic.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/PropositionalLogic.ipynb) | 2017 | <b><a href="ipynb/PropositionalLogic.ipynb" title="Automatically convert informal English sentences into formal Propositional Logic">Translating English into Propositional Logic</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/pal3.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2Fpal3.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2Fpal3.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/pal3.ipynb) | 2017 | <b><a href="ipynb/pal3.ipynb" title="Searching for a long Panama-style palindrome, this time letter-by-letter">World's Longest Palindrome</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Portmantout.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FPortmantout.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FPortmantout.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Portmantout.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/Portmantout.ipynb" title="Find a 1word that squishes together a bunch of words">World's Shortest Portmantout Word</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/xkcd-Name-Dominoes.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2Fxkcd-Name-Dominoes.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2Fxkcd-Name-Dominoes.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/xkcd-Name-Dominoes.ipynb) | 2018 | <b><a href="ipynb/xkcd-Name-Dominoes.ipynb" title="Lay out dominoes legally; the dominoes have people names, not numbers">xkcd 1970: Name Dominoes</a></b> |
|
||||
|
||||
|Word Games|
|
||||
|---|
|
||||
|[Portmantout Words](ipynb/Portmantout.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Portmantout.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FPortmantout.ipynb)**<br>*Find a long word that squishes together a bunch of words.*|
|
||||
|[xkcd 1970: Name Dominoes](ipynb/xkcd-Name-Dominoes.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/xkcd-Name-Dominoes.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2Fxkcd-Name-Dominoes.ipynb)**<br>*Lay out dominoes legally; the dominoes have people names, not numbers.*|
|
||||
|[Ghost](ipynb/Ghost.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Ghost.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FGhost.ipynb)**<br>*The word game Ghost (add letters, try to avoid making a word).*|
|
||||
|[World's Longest Palindrome](ipynb/pal3.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/pal3.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2Fpal3.ipynb)**<br>*Searching for a long Panama-style palindrome, this time letter-by-letter.*|
|
||||
|[Refactoring a Crossword Game Program](ipynb/Scrabble.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Scrabble.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FScrabble.ipynb)**<br>*Refactoring the Scrabble / Word with Friends game from Udacity 212.*|
|
||||
|[xkcd 1313: Regex Golf](ipynb/xkcd1313.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/xkcd1313.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2Fxkcd1313.ipynb)**<br>*Find the smallest regular expression; inspired by Randall Munroe.*|
|
||||
|[xkcd 1313: Regex Golf (Part 2: Infinite Problems)](ipynb/xkcd1313-part2.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/xkcd1313-part2.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2Fxkcd1313-part2.ipynb)**<br>*Regex Golf: better, faster, funner. With Stefan Pochmann.*|
|
||||
|[Let's Code About Bike Locks](ipynb/Fred%20Buns.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Fred%20Buns.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FFred%2520Buns.ipynb)**<br>*A tale of a bicycle combination lock that uses letters instead of digits. Inspired by Bike Snob NYC.*|
|
||||
|[Gesture Typing](ipynb/Gesture%20Typing.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Gesture%20Typing.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FGesture%2520Typing.ipynb)**<br>*What word has the longest path on a gesture-typing smartphone keyboard?*|
|
||||
|[How to Do Things with Words, or Statistical Natural Language Processing in Python](ipynb/How%20to%20Do%20Things%20with%20Words.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/How%20to%20Do%20Things%20with%20Words.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FHow%2520to%2520Do%2520Things%2520with%2520Words.ipynb)**<br>*Spelling Correction, Secret Codes, Word Segmentation, and more: grab your bag of words.*|
|
||||
|Run|Year|Math Concepts: Probability, Uncertainty, Counting, etc.|
|
||||
|---|----|---|
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Probability.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FProbability.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FProbability.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Probability.ipynb) | 2018 | <b><a href="ipynb/Probability.ipynb" title="Code and examples of the basic principles of Probability Theory">A Concrete Introduction to Probability</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/ProbabilityParadox.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FProbabilityParadox.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FProbabilityParadox.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/ProbabilityParadox.ipynb) | 2016 | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/ProbabilitySimulation.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FProbabilitySimulation.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FProbabilitySimulation.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/ProbabilitySimulation.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/ProbabilitySimulation.ipynb" title="When the sample space is too complex, simulations can estimate probabilities">Estimating Probabilities with Simulations</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Coin%20Flip.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FCoin%20Flip.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FCoin%20Flip.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Coin%20Flip.ipynb) | 2019 | <b><a href="ipynb/Coin%20Flip.ipynb" title="How to beat the Devil at his own game">The Devil and the Coin Flip Game</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Economics.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FEconomics.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FEconomics.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Economics.ipynb) | 2018 | <b><a href="ipynb/Economics.ipynb" title="A simulation of a simple economic game">Economics Simulation</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Euler's%20Conjecture.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FEuler's%20Conjecture.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FEuler's%20Conjecture.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Euler's%20Conjecture.ipynb) | 2018 | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/How%20To%20Count%20Things.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FHow%20To%20Count%20Things.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FHow%20To%20Count%20Things.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/How%20To%20Count%20Things.ipynb) | <b><u>2020</b></u> | <b><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></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/TwelveBalls.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FTwelveBalls.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FTwelveBalls.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/TwelveBalls.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/TwelveBalls.ipynb" title="A puzzle where you are given some billiard balls and a balance scale, and asked to find the one ball that is heavier or lighter, in a limited number of weighings">Weighing Twelve Balls</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Differentiation.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FDifferentiation.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FDifferentiation.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Differentiation.ipynb) | 2017 | <b><a href="ipynb/Differentiation.ipynb" title="A computer algebra system that manipulates expressions, including symbolic differentiation">Symbolic Algebra, Simplification, and Differentiation</a></b> |
|
||||
|
||||
|Math Concepts|
|
||||
|---|
|
||||
|[A Concrete Introduction to Probability](ipynb/Probability.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Probability.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FProbability.ipynb)**<br>*Code and examples of the basic principles of Probability Theory.*|
|
||||
|[Probability, Paradox, and the Reasonable Person Principle](ipynb/ProbabilityParadox.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/ProbabilityParadox.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FProbabilityParadox.ipynb)**<br>*Some classic paradoxes in Probability Theory, and how to think about disagreements.*|
|
||||
|[Symbolic Algebra, Simplification, and Differentiation](ipynb/Differentiation.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Differentiation.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FDifferentiation.ipynb)**<br>*A computer algebra system that manipulates expressions, including symbolic differentiation.*|
|
||||
|[Economics Simulation](ipynb/Economics.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Economics.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FEconomics.ipynb)**<br>*A simulation of a simple economic game.*|
|
||||
|[How to Count Things](ipynb/How%20To%20Count%20Things.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/How%20To%20Count%20Things.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FHow%2520To%2520Count%2520Things.ipynb)**<br>*Combinatorial math: how to count how many things there are, when there are a lot of them.*|
|
||||
|[Pairing Socks](ipynb/Socks.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Socks.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FSocks.ipynb)**<br>*What is the probability that you will be able to pair up socks as you randomly pull them out of the dryer?*|
|
||||
|[Euler's Sum of Powers Conjecture](ipynb/Euler's%20Conjecture.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Euler's%20Conjecture.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FEuler's%2520Conjecture.ipynb)**<br>*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>.*|
|
||||
|
||||
|Computer Science Algorithms and Concepts|
|
||||
|---|
|
||||
|[BASIC Interpreter](ipynb/BASIC.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/BASIC.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FBASIC.ipynb)**<br>*How to write an interpreter for the BASIC programming language.*|
|
||||
|[Bad Grade, Good Experience](ipynb/Snobol.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Snobol.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FSnobol.ipynb)**<br>*As a student, did you ever get a bad grade on a programming assignment? (Snobol, Concordance)*|
|
||||
|[The Convex Hull Problem](ipynb/Convex%20Hull.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Convex%20Hull.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FConvex%2520Hull.ipynb)**<br>*A classic Computer Science Algorithm.*|
|
||||
|[The Traveling Salesperson Problem](ipynb/TSP.ipynb) **[NB](http:/nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/TSP.ipynb)**, **[DN](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FTSP.ipynb)**<br>*Another of the classics.*|
|
||||
|Run|Year|Computer Science Algorithms and Concepts|
|
||||
|---|----|---|
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Snobol.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FSnobol.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FSnobol.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Snobol.ipynb) | 2017 | <b><a href="ipynb/Snobol.ipynb" title="As a student, did you ever get a bad grade on a programming assignment? (Snobol, Concordance)">Bad Grade, Good Experience</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/BASIC.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FBASIC.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FBASIC.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/BASIC.ipynb) | 2017 | <b><a href="ipynb/BASIC.ipynb" title="How to write an interpreter for the BASIC programming language">BASIC Interpreter</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/Convex%20Hull.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FConvex%20Hull.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FConvex%20Hull.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Convex%20Hull.ipynb) | 2017 | <b><a href="ipynb/Convex%20Hull.ipynb" title="A classic Computer Science Algorithm">The Convex Hull Problem</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/StableMatching.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FStableMatching.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FStableMatching.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/StableMatching.ipynb) | <b><u>2020</b></u> | <b><a href="ipynb/StableMatching.ipynb" title="What is the best way to pair up two groups with each other, obeying preferences?">The Stable Matching Problem</a></b> |
|
||||
| [co](https://colab.research.google.com/github/norvig/pytudes/blob/master/ipynb/TSP.ipynb) [dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2FTSP.ipynb) [my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2FTSP.ipynb) [nb](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/TSP.ipynb) | 2018 | <b><a href="ipynb/TSP.ipynb" title="Another of the classics">The Traveling Salesperson Problem</a></b> |
|
||||
|
||||
# Index of Python Files
|
||||
|
||||
| **File** | **Description** | **Documentation**|
|
||||
|:--------|:-------------------|----|
|
||||
|[SET.py](https://github.com/norvig/pytudes/blob/master/py/SET.py)|Analyze the card game [SET](http://www.setgame.com/set).|[SET.html](http://norvig.com/SET.html)|
|
||||
|[beal.py](https://github.com/norvig/pytudes/blob/master/py/beal.py)|Search for counterexamples to Beal's Conjecture|[beal.html](http://norvig.com/beal.html)
|
||||
|[docex.py](https://github.com/norvig/pytudes/blob/master/py/docex.py)|A framework for running unit tests, similar to `doctest`.|
|
||||
|[ibol.py](https://github.com/norvig/pytudes/blob/master/py/ibol.py)|An Exercise in Species Barcoding|[ibol.html](http://norvig.com/ibol.html)
|
||||
|[lettercount.py](https://github.com/norvig/pytudes/blob/master/py/lettercount.py)|Convert Google Ngram Counts to Letter Counts|[mayzner.html](http://norvig.com/mayzner.html)
|
||||
|[lis.py](https://github.com/norvig/pytudes/blob/master/py/lis.py)|Lisp Interpreter written in Python|[lispy.html](http://norvig.com/lispy.html)
|
||||
|[lispy.py](https://github.com/norvig/pytudes/blob/master/py/lispy.py)|Even Better Lisp Interpreter written in Python|[lispy2.html](http://norvig.com/lispy2.html)
|
||||
|[lispytest.py](https://github.com/norvig/pytudes/blob/master/py/lispytest.py)|Tests for Lisp Interpreters|
|
||||
|[pal.py](https://github.com/norvig/pytudes/blob/master/py/pal.py)|Find long palindromes|[palindrome.html](http://norvig.com/palindrome.html)
|
||||
|[pal2.py](https://github.com/norvig/pytudes/blob/master/py/pal2.py)|Find longer palindromes|[palindrome.html](http://norvig.com/palindrome.html)
|
||||
|[pal3.py](https://github.com/norvig/pytudes/blob/master/py/pal3.py)|Find even longer palindromes|[palindrome.html](http://norvig.com/palindrome.html)
|
||||
|[py2html.py](https://github.com/norvig/pytudes/blob/master/py/py2html.py)|Pretty-printer to format Python files as html|
|
||||
|[spell.py](https://github.com/norvig/pytudes/blob/master/py/spell.py)|Spelling corrector|[spell-correct.html](http://norvig.com/spell-correct.html)
|
||||
|[sudoku.py](https://github.com/norvig/pytudes/blob/master/py/sudoku.py)|Program to solve sudoku puzzles|[sudoku.html](http://norvig.com/sudoku.html)
|
||||
|[testaccum.py](https://github.com/norvig/pytudes/blob/master/py/testaccum.py)|Tests for my failed Python `accumulation display` proposal|[pyacc.html](http://norvig.com/pyacc.html)
|
||||
|[yaptu.py](https://github.com/norvig/pytudes/blob/master/py/yaptu.py)|Yet Another Python Templating Utility|
|
||||
| File | Description | Documentation |
|
||||
|:--|:----|----|
|
||||
|[beal.py](/blob/master/py/beal.py)|*Search for counterexamples to Beal's Conjecture*|[documentation](http://norvig.com/beal.html)|
|
||||
|[docex.py](/blob/master/py/docex.py)|*A framework for running unit tests, similar to `doctest`*||
|
||||
|[ibol.py](/blob/master/py/ibol.py)|*An Exercise in Species Barcoding*|[documentation](http://norvig.com/ibol.html)|
|
||||
|[lettercount.py](/blob/master/py/lettercount.py)|*Convert Google Ngram Counts to Letter Counts*|[documentation](http://norvig.com/mayzner.html)|
|
||||
|[lis.py](/blob/master/py/lis.py)|*Lisp Interpreter written in Python*|[documentation](http://norvig.com/lispy.html)|
|
||||
|[lispy.py](/blob/master/py/lispy.py)|*Even Better Lisp Interpreter written in Python*|[documentation](http://norvig.com/lispy2.html)|
|
||||
|[lispytest.py](/blob/master/py/lispytest.py)|*Tests for Lisp Interpreters*||
|
||||
|[pal.py](/blob/master/py/pal.py)|*Find long palindromes*|[documentation](http://norvig.com/palindrome.html)|
|
||||
|[pal2.py](/blob/master/py/pal2.py)|*Find longer palindromes*|[documentation](http://norvig.com/palindrome.html)|
|
||||
|[pal3.py](/blob/master/py/pal3.py)|*Find even longer palindromes*|[documentation](http://norvig.com/palindrome.html)|
|
||||
|[pytudes.py](/blob/master/py/pytudes.py)|*Pre-process text to generate this README.md file.*||
|
||||
|[py2html.py](/blob/master/py/py2html.py)|*Pretty-printer to format Python files as html*||
|
||||
|[SET.py](/blob/master/py/SET.py)|*Analyze the card game SET*|[documentation](http://norvig.com/SET.html)|
|
||||
|[spell.py](/blob/master/py/spell.py)|*Spelling corrector*|[documentation](http://norvig.com/spell-correct.html)|
|
||||
|[sudoku.py](/blob/master/py/sudoku.py)|*Program to solve sudoku puzzles*|[documentation](http://norvig.com/sudoku.html)|
|
||||
|[testaccum.py](/blob/master/py/testaccum.py)|*Tests for my failed Python `accumulation display` proposal*|[documentation](http://norvig.com/pyacc.html)|
|
||||
|[yaptu.py](/blob/master/py/yaptu.py)|*Yet Another Python Templating Utility*||
|
||||
|
||||
# Etudes for Programmers
|
||||
|
||||
I got the idea for the "etudes" part of the name from
|
||||
I got the idea for the *"etudes"* part of the name from
|
||||
this [1978 book](https://books.google.com/books/about/Etudes_for_programmers.html?id=u89WAAAAMAAJ)
|
||||
by [Charles Wetherell](http://demin.ws/blog/english/2012/08/25/interview-with-charles-wetherell/)
|
||||
that was very influential to me when I was first learning to program.
|
||||
by [Charles Wetherell](http://demin.ws/blog/english/2012/08/25/interview-with-charles-wetherell)
|
||||
that was very influential to me when I was first learning to program. I still have my copy.
|
||||
|
||||

|
||||
|
File diff suppressed because one or more lines are too long
584
ipynb/Boggle.ipynb
Normal file
584
ipynb/Boggle.ipynb
Normal file
@ -0,0 +1,584 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div style=\"text-align: right\">Peter Norvig<br>January 2020</div>\n",
|
||||
"\n",
|
||||
"# Boggle\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"The word game **Boggle** ([rules here](https://howdoyouplayit.com/boggle-rules-play-boggle/)) is a fun game to play with friends, but not so fun to play against a computer. It is all too simple for a computer to find *all* the valid words in a Boggle board (such as the one shown above) in less than a second. We'll see how to do that as a straightforward programming exercise. Then we'll consider a more challenging problem:\n",
|
||||
"\n",
|
||||
"# Inverse Boggle\n",
|
||||
"\n",
|
||||
"The goal of Inverse Boggle is to find an arrangement of letters on a board that scores the most points. It is called Inverse Boggle because, instead of \"give me a board and I'll find the words\" it is \"give me the word list and I'll find a board with lots of words.\" \n",
|
||||
"\n",
|
||||
"It is not feasible to try all possible boards ($26^{5 \\times 5} \\approx 10^{35}$ possible $5 \\times 5$ boards), so we will use **hill-climbing**, a heuristic search technique that does not guarantee finding an optimal solution.\n",
|
||||
"\n",
|
||||
"# The Boggle Board\n",
|
||||
"\n",
|
||||
"I'll represent a board as a dict where the keys are `(x, y)` coordinates of squares, and the values are individual letters (except that `QU` occupies a single square). A board will also hold:\n",
|
||||
"- `board.squares`: a list of squares in row-major order.\n",
|
||||
"- `board.neighbors`: a dict of `{square: neighbors}`. \n",
|
||||
"- `board.format`: a string format method to print the board nicely using `__repr__`.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import random\n",
|
||||
"import itertools\n",
|
||||
"\n",
|
||||
"class Board(dict):\n",
|
||||
" \"\"\"A board is a dict of {(x, y): L}.\"\"\"\n",
|
||||
" def __init__(self, letters=None):\n",
|
||||
" letters = [('QU' if L == 'Q' else L) for L in letters if 'A' <= L <= 'Z']\n",
|
||||
" n = int(len(letters) ** 0.5)\n",
|
||||
" self.squares = [(x, y) for y in range(n) for x in range(n)]\n",
|
||||
" self.neighbors = {s: neighbors(s, n) for s in self.squares}\n",
|
||||
" self.format = ('\\n'.join(['{:2s}' * n] * n)).format\n",
|
||||
" self.update(zip(self.squares, letters))\n",
|
||||
" \n",
|
||||
" def __repr__(self): \n",
|
||||
" return self.format(*(self[s].capitalize() for s in self.squares))\n",
|
||||
" \n",
|
||||
"def neighbors(square, n) -> list:\n",
|
||||
" \"\"\"All the squares that neighbor a square.\"\"\"\n",
|
||||
" (x, y) = square\n",
|
||||
" return [(x+dx, y+dy) for dx in (-1, 0, 1) for dy in (-1, 0, 1)\n",
|
||||
" if 0 <= x+dx < n and 0 <= y+dy < n and not (dx == dy == 0)]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"P U Z Z L \n",
|
||||
"W O R D E \n",
|
||||
"B O G G L \n",
|
||||
"S E A R C \n",
|
||||
"F I N D H "
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"board = Board('PUZZL WORDE BOGGL SEARC FINDH')\n",
|
||||
"board"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# The word list and scoring\n",
|
||||
"\n",
|
||||
"We'll read in a word list, convert to uppercase, and keep all the words that are three letters or more. The variable `WORDS` holds the set of valid words. The function `total_score` computes the total score of a set of words. It does not take a board as an argument, and so it does not check if the words can actually be made on the board. It does check that the words are in the `WORDS` list of valid words."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" 172820 enable1.txt\r\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"! [ -e enable1.txt ] || curl -O http://norvig.com/ngrams/enable1.txt\n",
|
||||
"! wc -w enable1.txt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"172724"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def total_score(found, scores=[0, 0, 0, 1, 1, 2, 3, 5] + [11] * 99) -> int:\n",
|
||||
" \"\"\"The total score for the words found, according to the rules.\"\"\"\n",
|
||||
" return sum([scores[len(w)] for w in found & WORDS])\n",
|
||||
"\n",
|
||||
"WORDS = {w for w in open('enable1.txt').read().upper().split() if len(w) >= 3}\n",
|
||||
"len(WORDS)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Finding all the words in a board\n",
|
||||
"\n",
|
||||
"The strategy for finding words:\n",
|
||||
"- A word could start at any of the squares on the board, so consider each one.\n",
|
||||
"- At each square, form a **path** consisting of just that square (for example, the top left square forms the path `[(0, 0)]`) and a **prefix string** consisting of the letters visited in the path; for this one-square path on `board` (the one with `PUZZLE` in it), the prefix string would be `'P'`. On a 5 by 5 board we would have 25 initial paths of length one.\n",
|
||||
"- Now continue each path:\n",
|
||||
" - If the prefix string is a word, record it: add it to the set `found`.\n",
|
||||
" - If the prefix string is a prefix of some word in the dictionary, continue the path by visiting each neighboring square that has not already been visited in the path. For example, in `board`, the path `[(0, 0)]` with prefix `'P'` would be continued to three neighbors:\n",
|
||||
" - `[(0, 0), (1, 0)]`, `'PU'`: here `'PU'` is a prefix of a word so continue this path.\n",
|
||||
" - `[(0, 0), (0, 1)]`, `'PW'`: here `'PW'` is not a prefix of any word so do **not** continue this path.\n",
|
||||
" - `[(0, 0), (1, 1)]`, `'PO'`: here `'PO'` is a prefix of a word so continue this path.\n",
|
||||
" - One continuation of `'PO'` is as follows:\n",
|
||||
" - `[(0, 0), (1, 1), (0, 1)]`, `'POW'`: here `'POW'` is both a word and a prefix, so record it and continue.\n",
|
||||
"- We can precompute the set of all `PREFIXES` of all words in the word list."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def word_prefixes(words) -> set:\n",
|
||||
" \"The set of all non-empty, non-full-word prefixes of each word in a word list.\"\n",
|
||||
" return {word[:i] for word in words for i in range(1, len(word))}\n",
|
||||
"\n",
|
||||
"PREFIXES = word_prefixes(WORDS) # Precompute this once.\n",
|
||||
"\n",
|
||||
"def find_words(board, words=WORDS, prefixes=PREFIXES):\n",
|
||||
" \"\"\"Find all words in a Boggle board by recursively continuing paths that are prefixes of words.\"\"\"\n",
|
||||
" found = set() # Accumulate the words found in the set `found`\n",
|
||||
" def continue_path(path, prefix):\n",
|
||||
" if prefix in words:\n",
|
||||
" found.add(prefix)\n",
|
||||
" if prefix in prefixes:\n",
|
||||
" for s in board.neighbors[path[-1]]:\n",
|
||||
" if s not in path:\n",
|
||||
" continue_path(path + [s], prefix + board[s])\n",
|
||||
" # Body of find_words: Start paths from each of the squares on the board\n",
|
||||
" for s in board.squares:\n",
|
||||
" continue_path([s], board[s])\n",
|
||||
" return found"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"How many words can we find? And how long does it take?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 4.39 ms, sys: 43 µs, total: 4.43 ms\n",
|
||||
"Wall time: 4.5 ms\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"252"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%time len(find_words(board))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Can we see the words and summarize them? "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"252 words found:\n",
|
||||
"AGE AGED AGES AGGRO AGGROS AGO AIN AIS AND ANE ANES ANI ANIS ANISE ARC ARCH ARGLE ARGLED\n",
|
||||
"BEAD BEAGLE BEAN BEAR BEARD BEG BEGAN BEGGAR BEGGED BEGROAN BEN BEND BOA BOAR BOARD BOG\n",
|
||||
"BOGAN BOGGED BOGGLE BOGGLED BOO BOOR BOOS BOP BORDEL BOS BOURG BOW CRAG CRAGGED CRANE\n",
|
||||
"CRANES DAG DAGGLE DAGGLED DAGO DAGOES DAGOS DAIS DARN DEGAGE DEL DRAG DRAGGED DRAGGLE\n",
|
||||
"DRAGGLED DRAIN DROOP DROP EAGLE EAR EARL EARN EDGE EDGES EFS EGAD EGG EGGAR EGGED EGO EGOS\n",
|
||||
"ELD END ENDARCH ENRAGE ENRAGED EOSIN FEAR FEN FENAGLE FENAGLED FEND FIAR FIE FIEND FIN\n",
|
||||
"FINAGLE FINAGLED FIND FINE FINES GAD GAE GAEN GAES GAG GAGE GAGED GAGES GAIN GAN GANE\n",
|
||||
"GANEF GANEFS GAR GARGLE GARGLED GARNI GEAR GED GEL GELD GEN GLED GOA GOAD GOB GOBO GOBOES\n",
|
||||
"GOBOS GOBS GOES GOO GOOP GOOS GOOSE GOR GORGE GORGED GOURD GOURDE GRAD GRAIN GRAN GRAND\n",
|
||||
"GROAN GROG GROUP GROW IFS INARCH LED LEDGE LEDGES LEG LEZ NAE NAG NAGGED NAIF NAIFS NAOS\n",
|
||||
"NARC NARD NEAR NEB NEBS NEIF NEIFS OAR OBE OBES OBOE OBOES OES ORGAN ORGANISE ORZO OSE OUR\n",
|
||||
"POOR POROSE POUR POW PUR PURGE PURGED PURGES PUZZLE PUZZLED RAD RAG RAGE RAGED RAGES\n",
|
||||
"RAGGED RAGGLE RAIN RAISE RAN RAND RANI RANIS ROAD ROAN ROAR ROB ROBE ROBES ROBS ROE ROES\n",
|
||||
"ROOSE ROSE ROSIN ROUP ROW SEA SEAR SEARCH SEG SEGGAR SEGO SEI SEIF SEN SEND SIN SINE SOAR\n",
|
||||
"SOB SOGGED SORD SORGO SOW UPO URD URGE URGED URGES WOAD WOE WOES WOG WOO WOOS WOP WORD WOS\n",
|
||||
"WURZEL ZED ZOO ZOOS\n",
|
||||
"Score of 449 for 252 words on board:\n",
|
||||
"P U Z Z L \n",
|
||||
"W O R D E \n",
|
||||
"B O G G L \n",
|
||||
"S E A R C \n",
|
||||
"F I N D H \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import textwrap\n",
|
||||
"\n",
|
||||
"def report(board, verbose=True):\n",
|
||||
" found = find_words(board, WORDS, PREFIXES)\n",
|
||||
" if verbose:\n",
|
||||
" print(f'{len(found)} words found:') \n",
|
||||
" print(textwrap.fill(' '.join(sorted(found)), width=90))\n",
|
||||
" print(f'Score of {total_score(found)} for {len(found)} words on board:\\n{board}')\n",
|
||||
" \n",
|
||||
"report(board)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's test that the `Q` works, and that a 4x4 board works:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"125 words found:\n",
|
||||
"ANE ANES ANEW ANSWER ANSWERS AWE AWES AWN AWNS AWRY ENOSIS ENS EON EONS ERS ESES ESS ESSES\n",
|
||||
"ION IONS ITS NAE NAW NEIST NENE NEON NEONS NESS NESSES NEST NESTS NEW NEWNESS NEWS NOES\n",
|
||||
"NOESIS NOISE NOISES NOS NOSE NOSES NOSIER OES ONE ONENESS ONERY ONES ONS OSE OSES OSIER\n",
|
||||
"OSIERS QUA QUEAN QUEANS QUEST QUESTION QUESTIONER QUESTIONERS QUESTIONS QUESTS REI REIS\n",
|
||||
"RENEST RENESTS RES RESIST REST RESTS REWAN REWAX SEA SEI SEIS SEISE SEISES SEN SENE SENSE\n",
|
||||
"SENSES SER SERS SESSION SEW SEWAN SEWANS SEWN SEWS SIS SISES SIT SITS SNAW SNAWS SON SONE\n",
|
||||
"SONES SONS SOS STIES SWAN SWANS TIE TIER TIERS TIES TIS WAE WAENESS WAES WAN WANE WANES\n",
|
||||
"WANS WAX WAXY WEN WENS WEST WESTS WREN WRENS WREST WRESTS WRY\n",
|
||||
"Score of 245 for 125 words on board:\n",
|
||||
"QuE S T \n",
|
||||
"A N S I \n",
|
||||
"X W E O \n",
|
||||
"Y R S N \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"report(Board('QEST ANSI XWEO YRSN'))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Inverse Boggle\n",
|
||||
"\n",
|
||||
"I'll tackle the Inverse Boggle problem with a **hill-climbing** approach:\n",
|
||||
"- Start with some board.\n",
|
||||
"- Make a random change to some letter on the board.\n",
|
||||
"- Evaluate the score of the new board; if it is the best score so far, keep it.\n",
|
||||
"- If not, revert the change.\n",
|
||||
"- Repeat for a set number of iterations"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def boggle_hill_climbing(board, words=WORDS, prefixes=PREFIXES, repeat=500):\n",
|
||||
" \"\"\"Solve Inverse Boggle by hill-climbing: find a high-scoring board by\n",
|
||||
" starting with one and changing it.\"\"\"\n",
|
||||
" board = Board(board[s] for s in board.squares) # Copy board, so we don't mutate original\n",
|
||||
" best_score = total_score(find_words(board, words, prefixes))\n",
|
||||
" for _ in range(repeat):\n",
|
||||
" s, old_letter = mutate_boggle(board)\n",
|
||||
" new_score = total_score(find_words(board, words, prefixes))\n",
|
||||
" if new_score >= best_score:\n",
|
||||
" best_score = new_score\n",
|
||||
" else:\n",
|
||||
" board[s] = old_letter # Change back\n",
|
||||
" return board\n",
|
||||
"\n",
|
||||
"# The distribution of letters in the 16-cube version of the game\n",
|
||||
"LETTERS = 3 * 'AABCDEEEGHIILMNOOPRSTUY' + 'AADEFFIJKKLLNNQRSSTTUVVWWXZ'\n",
|
||||
"\n",
|
||||
"def mutate_boggle(board, letters=LETTERS):\n",
|
||||
" \"\"\"Make a random change to one letter in the board\"\"\"\n",
|
||||
" s = random.choice(board.squares)\n",
|
||||
" old_letter = board[s]\n",
|
||||
" board[s] = random.choice(letters)\n",
|
||||
" return s, old_letter\n",
|
||||
"\n",
|
||||
"def random_board(n=5, letters=LETTERS) -> Board:\n",
|
||||
" \"\"\"Return a random Boggle board.\"\"\"\n",
|
||||
" return Board(random.sample(letters, n * n))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's generate a random board and see if we can improve it:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"177 words found:\n",
|
||||
"ABIOTIC ABO ALE ALOOF ALT ALTO ATT ATTIC AUK BABE BABOO BABOOL BABU BAKE BAL BALE BALK BAT\n",
|
||||
"BATT BAUBEE BEE BEEP BIO BIOTIC BIPOD BOA BOAT BOD BOLA BOLE BOLT BOO BOOT BOP BOT BOTA\n",
|
||||
"BOTT BOTTLE BOY BUB BUBAL BUBALE BUBO BUOY CITOLA CITOLE DIOL DIT DITA DITTO DOPY DOT\n",
|
||||
"DOTTLE EAT EAU ELK FIB FOB FOOL FOOT FOOTBOY FOOTLE FOP HEP HIP HOB HOBO HOE HOOD HOOF\n",
|
||||
"HOOP HOOT HOP HOPE HOY HYP HYPO IODIC IOTA KAB KAE KALE KAT KEA KELOID KLOOF KUE LAB LAKE\n",
|
||||
"LAT LATI LEA LEAK LEK LEKU LOB LOBO LOO LOOF LOOP LOOPY LOOT LOT LOTA LOTI LOTIC LOTTO\n",
|
||||
"LOUP LOUPE OAK OAT OBI OBOE OBOL OBOLE ODIC OFT OLE OLEA OOH OOT OOTID OPE OTIC OTTO OUPH\n",
|
||||
"OUPHE PEE PEH PHI PIBAL POD POH POI POOD POOF POOH POOL POOP POT POTBOY POTTLE POTTO PUB\n",
|
||||
"PUKE TAB TABOO TABU TAE TAEL TAKE TALE TALK TAO TAU TAUPE TIC TIT TITLE TOD TOIT TOLA TOLE\n",
|
||||
"TOO TOOL TOOT TOOTLE TOP TOPI TOT TOTAL TOUPEE UKE UPO YIP YOB YOU\n",
|
||||
"Score of 234 for 177 words on board:\n",
|
||||
"C I T L E \n",
|
||||
"D T O A K \n",
|
||||
"F O B U B \n",
|
||||
"P I O P E \n",
|
||||
"I Y H E U \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"board2 = random_board(5)\n",
|
||||
"report(board2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Score of 5782 for 1577 words on board:\n",
|
||||
"V I S E N \n",
|
||||
"D E T T S \n",
|
||||
"R A L A R \n",
|
||||
"S I M E C \n",
|
||||
"E D N S H \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"report(boggle_hill_climbing(board2), False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Impressive! We got roughly a ten-fold improvement in score after 500 repetitions.\n",
|
||||
"We can do the same with our original `board`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"252 words found:\n",
|
||||
"AGE AGED AGES AGGRO AGGROS AGO AIN AIS AND ANE ANES ANI ANIS ANISE ARC ARCH ARGLE ARGLED\n",
|
||||
"BEAD BEAGLE BEAN BEAR BEARD BEG BEGAN BEGGAR BEGGED BEGROAN BEN BEND BOA BOAR BOARD BOG\n",
|
||||
"BOGAN BOGGED BOGGLE BOGGLED BOO BOOR BOOS BOP BORDEL BOS BOURG BOW CRAG CRAGGED CRANE\n",
|
||||
"CRANES DAG DAGGLE DAGGLED DAGO DAGOES DAGOS DAIS DARN DEGAGE DEL DRAG DRAGGED DRAGGLE\n",
|
||||
"DRAGGLED DRAIN DROOP DROP EAGLE EAR EARL EARN EDGE EDGES EFS EGAD EGG EGGAR EGGED EGO EGOS\n",
|
||||
"ELD END ENDARCH ENRAGE ENRAGED EOSIN FEAR FEN FENAGLE FENAGLED FEND FIAR FIE FIEND FIN\n",
|
||||
"FINAGLE FINAGLED FIND FINE FINES GAD GAE GAEN GAES GAG GAGE GAGED GAGES GAIN GAN GANE\n",
|
||||
"GANEF GANEFS GAR GARGLE GARGLED GARNI GEAR GED GEL GELD GEN GLED GOA GOAD GOB GOBO GOBOES\n",
|
||||
"GOBOS GOBS GOES GOO GOOP GOOS GOOSE GOR GORGE GORGED GOURD GOURDE GRAD GRAIN GRAN GRAND\n",
|
||||
"GROAN GROG GROUP GROW IFS INARCH LED LEDGE LEDGES LEG LEZ NAE NAG NAGGED NAIF NAIFS NAOS\n",
|
||||
"NARC NARD NEAR NEB NEBS NEIF NEIFS OAR OBE OBES OBOE OBOES OES ORGAN ORGANISE ORZO OSE OUR\n",
|
||||
"POOR POROSE POUR POW PUR PURGE PURGED PURGES PUZZLE PUZZLED RAD RAG RAGE RAGED RAGES\n",
|
||||
"RAGGED RAGGLE RAIN RAISE RAN RAND RANI RANIS ROAD ROAN ROAR ROB ROBE ROBES ROBS ROE ROES\n",
|
||||
"ROOSE ROSE ROSIN ROUP ROW SEA SEAR SEARCH SEG SEGGAR SEGO SEI SEIF SEN SEND SIN SINE SOAR\n",
|
||||
"SOB SOGGED SORD SORGO SOW UPO URD URGE URGED URGES WOAD WOE WOES WOG WOO WOOS WOP WORD WOS\n",
|
||||
"WURZEL ZED ZOO ZOOS\n",
|
||||
"Score of 449 for 252 words on board:\n",
|
||||
"P U Z Z L \n",
|
||||
"W O R D E \n",
|
||||
"B O G G L \n",
|
||||
"S E A R C \n",
|
||||
"F I N D H \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"report(board)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Score of 5690 for 1511 words on board:\n",
|
||||
"L P C A P \n",
|
||||
"N A R O M \n",
|
||||
"D E T I L \n",
|
||||
"S E S E N \n",
|
||||
"R T N R G \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"report(boggle_hill_climbing(board), False) "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Again, roughly a ten-fold improvement. \n",
|
||||
"\n",
|
||||
"Now, let's start from a very high-scoring board, identified by Justin Boyan in [his Ph.D. thesis](https://www.ri.cmu.edu/publications/learning-evaluation-functions-for-global-optimization/), and see if we can improve it:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Score of 10112 for 2290 words on board:\n",
|
||||
"R S T C S \n",
|
||||
"D E I A E \n",
|
||||
"G N L R P \n",
|
||||
"E A T E S \n",
|
||||
"M S S I D \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"boyan = Board('RSTCS DEIAE GNLRP EATES MSSID')\n",
|
||||
"report(boyan, False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Score of 10112 for 2290 words on board:\n",
|
||||
"R S T C S \n",
|
||||
"D E I A E \n",
|
||||
"G N L R P \n",
|
||||
"E A T E S \n",
|
||||
"M S S I D \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"report(boggle_hill_climbing(boyan), False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Sadly, we were not able to make an improvement in 500 repetitions. But that certainly is no guarantee that `boyan` is the best possible board. Here are some things to try to find a better board; maybe you can implement some of them, or try some ideas of your own:\n",
|
||||
"\n",
|
||||
"- **Genetic algorithms**: We used **mutation** of a single board, but we could also consider **crossover** where we keep a pool of boards and take the first half of one board and combine it with the second half of another.\n",
|
||||
"- **Swaps**: We changed one letter at a time. But maybe there is no change of one letter that will improve a board, but there is a change involving two squares, either swapping them or mutating both of them.\n",
|
||||
"- **Incremental score calculation**: We modified just one square and then tried to find all the words from scratch. Would it be faster to keep track of which squares contributed to which words, and only re-do the calculations for the one changed square? This would probably involve infixes of words rather than prefixes. Perhaps by keeping track of what each square contributes, we can make a better choice of which square to mutate.\n",
|
||||
"- **Random restarts**: When is it best to continue searching from the current board, versus starting over from a new board?"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -4,25 +4,25 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div align=\"right\"><i>Peter Norvig<br>April 2015<br>Python 3: Feb 2019</i></div>\n",
|
||||
"<div align=\"right\" style=\"text-align: right\"><i>Peter Norvig<br>April 2015<br>Python 3: Feb 2019<br>Steve's bus: Apr 2020<br>Mad Cheryl: May 2020</i></div>\n",
|
||||
"\n",
|
||||
"# When is Cheryl's Birthday?\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"[This puzzle](https://www.google.com/webhp?#q=cheryl%27s%20birthday) has been making the rounds:\n",
|
||||
"**[This puzzle](https://www.google.com/webhp?#q=cheryl%27s%20birthday)** has been making the rounds:\n",
|
||||
"\n",
|
||||
"> 1. Albert and Bernard became friends with Cheryl, and want to know when her birthday is. Cheryl gave them a list of 10 possible dates:\n",
|
||||
" May 15 May 16 May 19\n",
|
||||
" June 17 June 18\n",
|
||||
" July 14 July 16\n",
|
||||
" August 14 August 15 August 17\n",
|
||||
"> 2. Cheryl then tells Albert and Bernard separately the month and the day of the birthday respectively.\n",
|
||||
"> 2. **Cheryl** then privately tells Albert the month and Bernard the day of her birthday.\n",
|
||||
"> 3. **Albert**: \"I don't know when Cheryl's birthday is, and I know that Bernard does not know.\"\n",
|
||||
"> 4. **Bernard**: \"At first I don't know when Cheryl's birthday is, but I know now.\"\n",
|
||||
"> 5. **Albert**: \"Then I also know when Cheryl's birthday is.\"\n",
|
||||
"> 6. So when is Cheryl's birthday?\n",
|
||||
"\n",
|
||||
"Let's work through this puzzle statement by statement.\n",
|
||||
"Let's work through the puzzle line by line.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@ -54,9 +54,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def Month(date): return date.split()[0]\n",
|
||||
"\n",
|
||||
"def Day(date): return date.split()[1]"
|
||||
"def month(date): return date.split()[0]\n",
|
||||
"def day(date): return date.split()[1]"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -76,7 +75,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"Month('May 15')"
|
||||
"month('May 15')"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -96,7 +95,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"Day('May 15')"
|
||||
"day('May 15')"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -105,9 +104,19 @@
|
||||
"source": [
|
||||
"## 2. Cheryl then tells Albert and Bernard separately the month and the day of the birthday respectively.\n",
|
||||
"\n",
|
||||
"Now we have to think about what we're doing. We'll use a *set of dates* to represent a *belief set*: a person who has the belief set `{'August 15', 'May 15'}` *believes* that Cheryl's birthday is one of those two days. A person *knows* the birthdate when they get down to a belief set with only one possibility. \n",
|
||||
"Now we have to think really carefully about what we're doing. The puzzle is tricky because we're dealing with two types of uncertainty:\n",
|
||||
"\n",
|
||||
"We can define the idea of Cheryl **telling** someone a component of her birthdate, and while we're at it, the idea of **knowing** a birthdate:"
|
||||
"1. Albert and Bernard are uncertain about the birthdate. *(Cheryl knows something they don't know.)*\n",
|
||||
"2. We, the puzzle solvers don't know what Albert and Bernard were told. *(They know something we don't know.)*\n",
|
||||
"\n",
|
||||
"If Cheryl tells Albert \"May\", then he believes the birthdate could be either May 15, May 16, or May 19. We'll call `{'May 15', 'May 16', 'May 19'}` his **belief state** about the birthdate. We will say that a person **knows** the birthdate when they get down to a belief state with exactly one possibility. The type 2 uncertainty is that we don't know that Albert was told \"May\", so we have uncertainty about his belief state. But we do know some statements about his belief state, and our task is to use those statements to solve the puzzle. \n",
|
||||
"\n",
|
||||
"The way we will deal with our uncertainty as puzzle solvers is by considering each of the ten dates one at a time and reasoning as follows: \n",
|
||||
"- If this date were Cheryl's true birthdate, then we would know what Albert and Bernard were told: we would eliminate the type 2 uncertainty, and we could figure out their belief states. \n",
|
||||
"- From that we could figure out if the statements are true (given this date). \n",
|
||||
"- If the puzzle is correct and we don't make mistakes, then there will be only one date that makes all the statements true; that's Cheryl's birthday.\n",
|
||||
"\n",
|
||||
"We can define the idea of Cheryl having **told** someone a component of her birthdate, and the idea of **knowing** a birthdate as follows:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -116,14 +125,14 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BeliefSet = set\n",
|
||||
"BeliefState = set\n",
|
||||
"\n",
|
||||
"def tell(part, dates=dates) -> BeliefSet:\n",
|
||||
" \"Cheryl tells a part of her birthdate to someone; return a set of possible dates.\"\n",
|
||||
"def told(part: str) -> BeliefState:\n",
|
||||
" \"\"\"Cheryl told a part of her birthdate to someone; return a belief state of possible dates.\"\"\"\n",
|
||||
" return {date for date in dates if part in date}\n",
|
||||
"\n",
|
||||
"def know(beliefs) -> bool:\n",
|
||||
" \"A person `knows` the answer if their belief set has only one possibility.\"\n",
|
||||
"def know(beliefs: BeliefState) -> bool:\n",
|
||||
" \"\"\"A person `knows` the answer if their belief state has only one possibility.\"\"\"\n",
|
||||
" return len(beliefs) == 1"
|
||||
]
|
||||
},
|
||||
@ -131,7 +140,11 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"For example: If Cheryl tells Albert that her birthday is in May, he would know there is a set of three possible birthdates:"
|
||||
"(Note: one thing I dislike about my code is that `told` uses the global `dates`. Later we will see that `cheryls_birthday` does the same. For that to be an acceptable design choice, we need to think of `dates` as a constant, not a variable.)\n",
|
||||
"\n",
|
||||
"Let's see what happens as we consider the date `'May 15'`:\n",
|
||||
"\n",
|
||||
"Cheryl tells Albert `'May'` and Bernard `'15'`, giving them these belief states: "
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -151,14 +164,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tell('May')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And if she tells Bernard that her birthday is on the 15th, he would know there are two possibilities:"
|
||||
"told('May')"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -178,14 +184,14 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tell('15')"
|
||||
"told('15')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"With two possibilities, Bernard does not know the birthdate:"
|
||||
"We can then check whether Albert and Bernard's statements are true, given this date. The first part of Albert's first statement is \"I don't know when Cheryl's birthday is.\" We can verify that that part of the statement is true (given that Albert was told \"May\"):"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -196,7 +202,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"False"
|
||||
"True"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
@ -205,20 +211,25 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"know(tell('15'))"
|
||||
"not know(told('May'))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Overall Strategy\n",
|
||||
"If the rest of the statements worked out to be true, then `'May 15'` would be a solution to the puzzle. If not, it must be one of the other dates."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Overall Strategy\n",
|
||||
"\n",
|
||||
"If Cheryl tells Albert `'May'` then *he* knows there are three possibilities, but *we* (the puzzle solvers) don't know that, because we don't know what Cheryl said. \n",
|
||||
"Here is the main function, `cheryls_birthday`, which computes the subset of dates in the global variable `dates` that satisfy statements 3 through 5. We will define a **statement** as a boolean function that takes a single date as input and returns true if the statement would be true in the condition that the given date is Cheryl's actual birthday. So a statement only has to consider one date at a time. But the code within a statement may have to consider the belief states of Albert and Bernard, to determine if the statement is true.\n",
|
||||
"\n",
|
||||
"So what can we do? We can consider *all* of the possible dates, one at a time. For example, first consider `'May 15'`. Cheryl tells Albert `'May'` and Bernard `'15'`, giving them the lists of possible birthdates shown above. We can then check whether statements 3 through 5 are true in this scenario. If they are, then `'May 15'` is a solution to the puzzle. Repeat the process for each of the other possible dates. If all goes well, there should be exactly one date for which all the statements are true. \n",
|
||||
"\n",
|
||||
"Here is the main function, `cheryls_birthday`, which takes a set of possible dates, and returns the subset of dates that satisfy statements 3 through 5. The function `satisfy` is similar to the builtin function `filter`: `satisfy` takes a collection of items (here a set of dates) and returns the subset that satisfies all the predicates:"
|
||||
"The function `satisfy` takes a collection of dates and some statement(s) and returns the subset of dates that satisfies all the statements."
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -227,16 +238,14 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def cheryls_birthday(dates=dates) -> BeliefSet:\n",
|
||||
" \"Return a subset of the dates for which all three statements are true.\"\n",
|
||||
" return satisfy(dates, statement3, statement4, statement5)\n",
|
||||
"def cheryls_birthday() -> BeliefState:\n",
|
||||
" \"\"\"Return a subset of the global `dates` for which all three statements are true.\"\"\"\n",
|
||||
" return satisfy(dates, albert1, bernard1, albert2)\n",
|
||||
"\n",
|
||||
"def satisfy(items, *predicates):\n",
|
||||
" \"Return the subset of items that satisfy all the predicates.\"\n",
|
||||
" return {item for item in items\n",
|
||||
" if all(pred(item) for pred in predicates)}\n",
|
||||
"\n",
|
||||
"## TO DO: define statement3, statement4, statement5"
|
||||
"def satisfy(some_dates, *statements) -> BeliefState:\n",
|
||||
" \"\"\"Return the subset of dates that satisfy all the statements.\"\"\"\n",
|
||||
" return {date for date in some_dates\n",
|
||||
" if all(statement(date) for statement in statements)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -250,9 +259,9 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The function `statement3` corresponds to the third statement in the problem. It takes as input a single possible birthdate (not a set) and returns `True` if Albert's statement is true for that birthdate. How do we go from Albert's English statement to a Python function? Let's paraphrase it in a form that uses the concepts we have defined:\n",
|
||||
"The function `albert1` takes as input a single possible birthdate and returns `True` if Albert's statement is true for that birthdate. How do we go from Albert's English statement to a Python function? Let's paraphrase it in a form that uses the concepts we have defined:\n",
|
||||
"\n",
|
||||
"> **Albert**: After Cheryl told me the month of her birthdate, I didn't know her birthday. But for *any* of the possible dates, if Bernard was told the day of that date, he would not know Cheryl's birthday.\n",
|
||||
"> **Albert**: After Cheryl **told** me the **month** of her birthdate, my **belief state** was such that I didn't **know** her birthday. And I know that Bernard does not know; in other words he could not make the statement that he knows. How do I know that? I can see that for all the possible dates in my **belief state**, if Bernard was **told** the **day** of that date, he would **not know** Cheryl's birthday.\n",
|
||||
"\n",
|
||||
"That I can translate directly into code:"
|
||||
]
|
||||
@ -263,18 +272,19 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def statement3(date) -> bool:\n",
|
||||
" \"Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too.\"\n",
|
||||
" dates = tell(Month(date))\n",
|
||||
" return (not know(dates) \n",
|
||||
" and all(not know(tell(Day(d))) for d in dates))"
|
||||
"def albert1(date) -> bool:\n",
|
||||
" \"Albert: I don't know when Cheryl's birthday is, and I know that Bernard does not know.\"\n",
|
||||
" albert_beliefs = told(month(date))\n",
|
||||
" return not know(albert_beliefs) and not satisfy(albert_beliefs, bernard_knows)\n",
|
||||
"\n",
|
||||
"def bernard_knows(date) -> bool: return know(told(day(date))) "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We haven't solved the puzzle yet, but let's take a peek and see which dates satisfy statement 3:"
|
||||
"We haven't solved the puzzle yet, but let's take a peek and see which dates satisfy Albert's statement:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -294,7 +304,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"satisfy(dates, statement3)"
|
||||
"satisfy(dates, albert1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -305,7 +315,7 @@
|
||||
"\n",
|
||||
"Again, a paraphrase:\n",
|
||||
"\n",
|
||||
"> **Bernard:** At first Cheryl told me the day, and I didn't know. Then, out of the possible dates, I considered just the dates for which Albert's statement 3 is true, and now I know."
|
||||
"> **Bernard:** At first Cheryl **told** me the **day**, and I didn't **know**. After I heard Albert's **statement**, I updated my **belief state**, and now I **know**."
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -314,17 +324,18 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def statement4(date):\n",
|
||||
"def bernard1(date) -> bool:\n",
|
||||
" \"Bernard: At first I don't know when Cheryl's birthday is, but I know now.\"\n",
|
||||
" dates = tell(Day(date))\n",
|
||||
" return (not know(dates) and know(satisfy(dates, statement3)))"
|
||||
" at_first_beliefs = told(day(date))\n",
|
||||
" after_beliefs = satisfy(at_first_beliefs, albert1)\n",
|
||||
" return not know(at_first_beliefs) and know(after_beliefs)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's see which dates satisfy both statement 3 and statement 4:"
|
||||
"Let's see which dates satisfy both Albert and Bernard's statements:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -344,18 +355,26 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"satisfy(dates, statement3, statement4)"
|
||||
"satisfy(dates, albert1, bernard1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Wait a minute—I thought that Bernard **knew**?! Why are there three possible dates? Bernard does indeed know; it is just that we, the puzzle solvers, don't know. That's because Bernard knows something we don't know: the day. If Bernard was told `'15'` then he would know `'August 15'`; if he was told `'17'` he would know `'August 17'`, and if he was told `'16'` he would know `'July 16'`. *We* don't know because we don't know which of these is the case.\n",
|
||||
"Wait a minute—I thought that Bernard **knew**?! Why are there three possible dates? \n",
|
||||
"\n",
|
||||
"Bernard does indeed know; it is just that we, the puzzle solvers, don't know. That's because Bernard knows something we don't know: the day. If Bernard was told `'15'` then he would know `'August 15'`; if he was told `'17'` he would know `'August 17'`, and if he was told `'16'` he would know `'July 16'`. *We* don't know because we don't know which of these is the case. We'll need more information (coming in statement `albert2`) before *we* know.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 5. Albert: Then I also know when Cheryl's birthday is.\n",
|
||||
"\n",
|
||||
"Albert is saying that after hearing the month and Bernard's statement 4, he now knows Cheryl's birthday:"
|
||||
"Albert is saying that after hearing the month and Bernard's statement, he now knows Cheryl's birthday:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -364,9 +383,10 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def statement5(date):\n",
|
||||
"def albert2(date) -> bool:\n",
|
||||
" \"Albert: Then I also know when Cheryl's birthday is.\" \n",
|
||||
" return know(satisfy(tell(Month(date)), statement4))"
|
||||
" then = satisfy(told(month(date)), bernard1)\n",
|
||||
" return know(then)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -423,6 +443,97 @@
|
||||
"source": [
|
||||
"know(cheryls_birthday())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"___\n",
|
||||
"\n",
|
||||
"# New Puzzle: Steve's Bus\n",
|
||||
"\n",
|
||||
"Here's [another puzzle](https://www.reddit.com/r/riddles/comments/fw7h42/a_riddle_i_couldnt_solve/) that seems to have a very similar format:\n",
|
||||
"\n",
|
||||
"> 1. Steve tells Alice the hour of his bus departure and he tells Annie at which minute it leaves. He also tells them both that the bus leaves between 06:00 and 10:00.\n",
|
||||
"> 2. Alice and Annie consult the timetable and find the following services between those two time: 06:32 06:43 06:50 07:17 07:46 08:19 08:32 09:17 09:19 09:50.\n",
|
||||
"> 3. Alice then says “I don’t know when Steve’s bus leaves but I am sure that neither does Annie”\n",
|
||||
"> 4. Annie Replies “I didn’t know his bus, but now I do”\n",
|
||||
"> 5. Alice responds “Now I do as well!”\n",
|
||||
"> 6. When is Steve’s bus?\n",
|
||||
"\n",
|
||||
"Upon closer inspection, not only is it a similar format, it is **exactly** the same puzzle, except that months are changed to hours and days to minutes. If we rewrite the times in the same format as `dates`, we can solve the problem without writing a single line of new code:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'08 32'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 17,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"times = '06:32 06:43 06:50 07:17 07:46 08:19 08:32 09:17 09:19 09:50'.split()\n",
|
||||
"dates = [time.replace(':', ' ') for time in times]\n",
|
||||
"\n",
|
||||
"cheryls_birthday()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Steve took the 8:32 bus."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Another New Puzzle: Evil Mad Scientist Cheryl\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Again, we can solve this problem just by changing the format of `dates`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'C 3'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"pads = 'A2 A3 A6 B4 B5 C1 C3 D1 D2 D4'.split()\n",
|
||||
"dates = [L+' '+N for L,N in pads]\n",
|
||||
"\n",
|
||||
"cheryls_birthday()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Mad scientist Cheryl was refering to pad C3. (But may I point out that this Cheryl is not actually a mad scientist, just a [mad engineer](https://www.evilmadscientist.com/2015/evil-mad-engineers/). A true mad scientist would kill 25 people and use the other 25 as a control group.)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@ -441,7 +552,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.0"
|
||||
"version": "3.7.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -27,7 +27,7 @@
|
||||
"source": [
|
||||
"Pound a bunch of nails into a board, then stretch a rubber band around them and let the rubber band snap taut, like this:\n",
|
||||
"\n",
|
||||
"<img src=\"http://www.personal.kent.edu/~rmuhamma/Compgeometry/MyCG/Gifs-CompGeometry/ch2.gif\">\n",
|
||||
"<img src=\"convexhull.jpg\">\n",
|
||||
"\n",
|
||||
"The rubber band has traced out the *convex hull* of the set of nails. It turns out this is an important problem with applications in computer graphics, robot motion planning, geographical information systems, ethology, and other areas.\n",
|
||||
"More formally, we say that:\n",
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -12,12 +12,12 @@
|
||||
" gives state-by-state, month-by-month presidential approval poll data. Within the web page there is some Javascript from which\n",
|
||||
" we can extract the data we need. It looks like this:\n",
|
||||
"\n",
|
||||
" var mc_state_trend = [[\"Demographic\",\"January 1, 2017\",\"February 1, 2017\", ...]\n",
|
||||
" var mc_state_trend = [[\"Demographic\",\"Jan-17\",\"\",\"Feb-17\",\"\", ...]\n",
|
||||
" [\"Alabama\",\"62\",\"26\",\"65\",\"29\", ...], \n",
|
||||
" ... ]\n",
|
||||
" \n",
|
||||
"The first row is a header (each date is a day at which polls were aggregated).\n",
|
||||
"The subsequent rows each start with the state name, followed by the approval and disapproval percentages for each date. That is, if there are 34 dates, there will by 68 numbers. The row shown above is saying that on January 1, 2017, 62% of Alabamans approved and 26% disapproved; then on February 1, 2017, 65% approved and 29% disapproved, and so on. Our job is to extract this data and find ways to visualize and understand it.\n",
|
||||
"The first row is a header (each date is a month at which polls were aggregated).\n",
|
||||
"The subsequent rows each start with the state name, followed by the approval and disapproval percentages for each date. That is, if there are 34 dates, there will by 68 numbers. The row shown above is saying that in January, 2017, 62% of Alabamans approved and 26% disapproved; then in February, 2017, 65% approved and 29% disapproved, and so on. Our job is to extract this data and find ways to visualize and understand it.\n",
|
||||
"\n",
|
||||
"First fetch the page and save it locally:"
|
||||
]
|
||||
@ -57,7 +57,9 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Additional data: the variable `state_data` contains the [electoral votes by state](https://www.britannica.com/topic/United-States-Electoral-College-Votes-by-State-1787124) and the [partisan lean by state](https://github.com/fivethirtyeight/data/tree/master/partisan-lean) (how much more Republican (plus) or Democratic (minus) leaning the state is compared to the country as a whole, across recent elections). The variable `net_usa` has the [country-wide net presidential approval](https://projects.fivethirtyeight.com/trump-approval-ratings/) by month."
|
||||
"Additional data: the variable `state_data` contains the [electoral votes by state](https://www.britannica.com/topic/United-States-Electoral-College-Votes-by-State-1787124) and the [partisan lean by state](https://github.com/fivethirtyeight/data/tree/master/partisan-lean) (how much more Republican (plus) or Democratic (minus) leaning the state is compared to the country as a whole, across recent elections). \n",
|
||||
"\n",
|
||||
"The variable `net_usa` has the [country-wide net presidential approval](https://projects.fivethirtyeight.com/trump-approval-ratings/) by month."
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -67,7 +69,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# From https://github.com/fivethirtyeight/data/tree/master/partisan+lean\n",
|
||||
"# a dict of {\"state name\": (electoral_votes, partisan_lean)}\n",
|
||||
"# A dict of {\"state name\": (electoral_votes, partisan_lean)}\n",
|
||||
"state_data = { \n",
|
||||
" \"Alabama\": (9, +27), \"Alaska\": (3, +15), \"Arizona\": (11, +9), \n",
|
||||
" \"Arkansas\": (6, +24), \"California\": (55, -24), \"Colorado\": (9, -1), \n",
|
||||
@ -88,20 +90,20 @@
|
||||
" \"West Virginia\": (5, +30), \"Wisconsin\": (10, +1), \"Wyoming\": (3, +47)}\n",
|
||||
"\n",
|
||||
"# From https://projects.fivethirtyeight.com/trump-approval-ratings/\n",
|
||||
"# A dict of {'date': country-wide-net-approval}\n",
|
||||
"# A dict of {'date': country-wide-net-approval}, taken from 1st of month.\n",
|
||||
"net_usa = {\n",
|
||||
" 'January 2017': 10, 'January 2018': -18, 'January 2019': -12, \n",
|
||||
" 'February 2017': 0, 'February 2018': -15, 'February 2019': -16, \n",
|
||||
" 'March 2017': -6, 'March 2018': -14, 'March 2019': -11, \n",
|
||||
" 'April 2017': -13, 'April 2018': -13, 'April 2019': -11, \n",
|
||||
" 'May 2017': -11, 'May 2018': -12, 'May 2019': -12, \n",
|
||||
" 'June 2017': -16, 'June 2018': -11, 'June 2019': -12, \n",
|
||||
" 'July 2017': -15, 'July 2018': -10, 'July 2019': -11, \n",
|
||||
" 'August 2017': -19, 'August 2018': -12, 'August 2019': -10, \n",
|
||||
" 'September 2017': -20, 'September 2018': -14, 'September 2019': -13, \n",
|
||||
" 'October 2017': -17, 'October 2018': -11, 'October 2019': -13, \n",
|
||||
" 'November 2017': -19, 'November 2018': -11, 'November 2019': -13,\n",
|
||||
" 'December 2017': -18, 'December 2018': -10, 'December 2019': -12,\n",
|
||||
" 'Jan-17': 10, 'Jan-18': -18, 'Jan-19': -12, 'Jan-20': -11,\n",
|
||||
" 'Feb-17': 0, 'Feb-18': -15, 'Feb-19': -16, 'Feb-20': -10,\n",
|
||||
" 'Mar-17': -6, 'Mar-18': -14, 'Mar-19': -11, \n",
|
||||
" 'Apr-17': -13, 'Apr-18': -13, 'Apr-19': -11, \n",
|
||||
" 'May-17': -11, 'May-18': -12, 'May-19': -12, \n",
|
||||
" 'Jun-17': -16, 'Jun-18': -11, 'Jun-19': -12, \n",
|
||||
" 'Jul-17': -15, 'Jul-18': -10, 'Jul-19': -11, \n",
|
||||
" 'Aug-17': -19, 'Aug-18': -12, 'Aug-19': -10, \n",
|
||||
" 'Sep-17': -20, 'Sep-18': -14, 'Sep-19': -13, \n",
|
||||
" 'Oct-17': -17, 'Oct-18': -11, 'Oct-19': -13, \n",
|
||||
" 'Nov-17': -19, 'Nov-18': -11, 'Nov-19': -13,\n",
|
||||
" 'Dec-17': -18, 'Dec-18': -10, 'Dec-19': -12,\n",
|
||||
" }"
|
||||
]
|
||||
},
|
||||
@ -146,7 +148,7 @@
|
||||
"\n",
|
||||
"def margin(states, date=now) -> int:\n",
|
||||
" \"What's the least swing that would lead to a majority?\"\n",
|
||||
" return min(swing for swing in range(-50, 50) if EV(states, date, swing) >= 270)\n",
|
||||
" return min(swing for swing in range(-50, 50) if EV(states, date, swing+0.1) >= 270)\n",
|
||||
"\n",
|
||||
"def net(state, date=now) -> int: return state.approvals[date] - state.disapprovals[date]\n",
|
||||
"def undecided(state, date=now) -> int: return 100 - state.approvals[date] - state.disapprovals[date]\n",
|
||||
@ -164,24 +166,25 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def labels(xlab, ylab): plt.xlabel(xlab); plt.ylabel(ylab); plt.legend()\n",
|
||||
"\n",
|
||||
"def grid(): plt.minorticks_on(); plt.grid(which='minor', ls=':', alpha=0.7)\n",
|
||||
" \n",
|
||||
"def header(head) -> str: return head + '\\n' + '-'.join('|' * head.count('|'))\n",
|
||||
"\n",
|
||||
"def markdown(fn) -> callable: return lambda *args: display(Markdown('\\n'.join(fn(*args))))\n",
|
||||
"\n",
|
||||
"def parp(state, date=now) -> int: return net(state, date) - state.lean "
|
||||
"def parp(state, date=now) -> int: return net(state, date) - state.lean\n",
|
||||
"\n",
|
||||
"def grid(dates, xlab, ylab): \n",
|
||||
" plt.minorticks_on(); plt.grid(which='minor', axis='y', ls=':', alpha=0.7)\n",
|
||||
" plt.xticks(range(len(dates)), dates, rotation=90)\n",
|
||||
" plt.xlabel(xlab); plt.ylabel(ylab); plt.legend()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -192,16 +195,16 @@
|
||||
" N = len(dates)\n",
|
||||
" err = [[EV(states, date) - EV(states, date, -swing) for date in dates],\n",
|
||||
" [EV(states, date, +swing) - EV(states, date) for date in dates]]\n",
|
||||
" grid()\n",
|
||||
" plt.plot(range(N), [270] * N, color='darkorange', label=\"270 EVs\", lw=2)\n",
|
||||
" plt.errorbar(range(N), [EV(states, date) for date in dates], fmt='D-',\n",
|
||||
" yerr=err, ecolor='grey', capsize=7, label='Trump EVs ±3% swing', lw=2)\n",
|
||||
" labels('Months into term', 'Electoral Votes')"
|
||||
" grid(dates, 'Date', 'Electoral Vptes')\n",
|
||||
" #labels('Date', 'Electoral Votes')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@ -210,11 +213,23 @@
|
||||
" plt.rcParams[\"figure.figsize\"] = [10, 7]\n",
|
||||
" plt.style.use('fivethirtyeight')\n",
|
||||
" N = len(dates)\n",
|
||||
" grid()\n",
|
||||
" plt.plot(range(N), [0] * N, label='Net zero', color='darkorange')\n",
|
||||
" plt.plot(range(N), [-margin(states, date) for date in dates], 'D-', label='Margin to 270')\n",
|
||||
" plt.plot(range(N), [net_usa[date] for date in dates], 'go-', label='Country-wide Net')\n",
|
||||
" labels('Months into term', 'Net popularity')"
|
||||
" grid(dates, 'Date', 'Net popularity')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def show_swings(swings=range(10)):\n",
|
||||
" print('Swing EV Range')\n",
|
||||
" for swing in swings:\n",
|
||||
" s = swing + 0.5\n",
|
||||
" print(f'±{s:3.1f}% {EV(states, swing=-s):3} to {EV(states, swing=s):3}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -226,7 +241,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"@markdown\n",
|
||||
"def show_states(states=states, d=now, ref='January 2017'):\n",
|
||||
"def show_states(states=states, d=now, ref='Jan-17'):\n",
|
||||
" \"A table of states, sorted by net approval, with electoral votes.\"\n",
|
||||
" total = 0\n",
|
||||
" yield header(f'|State|Net|Move|EV|ΣEV|+|−|?|𝝈|')\n",
|
||||
@ -246,7 +261,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"@markdown\n",
|
||||
"def show_parp(states=states, dates=(now, 'January 2019', 'January 2018', 'January 2017')):\n",
|
||||
"def show_parp(states=states, dates=(now, 'Jan-19', 'Jan-18', 'Jan-17')):\n",
|
||||
" \"A table of states, sorted by Popularity Above Replacement President.\"\n",
|
||||
" def year(date): return '' if date == now else \"'\" + date[-2:]\n",
|
||||
" fields = [f\"PARP{year(date)}|(Net)\" for date in dates]\n",
|
||||
@ -260,7 +275,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Tests** (I really should have more)."
|
||||
"**Tests** (I really should have more):"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
272
ipynb/ElementSpelling.ipynb
Normal file
272
ipynb/ElementSpelling.ipynb
Normal file
@ -0,0 +1,272 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div style=\"text-align: right\"><i>Peter Norvig<br>March 2020</i></div>\n",
|
||||
"\n",
|
||||
"# Elemental Spelling\n",
|
||||
"\n",
|
||||
"Here's a problem: \n",
|
||||
"\n",
|
||||
"> Given a word, decide if it can be spelled using only the symbols in the **[periodic table](https://en.wikipedia.org/wiki/Periodic_table)** of elements. For example, the word \"bananas\" can be spelled with \"BaNaNaS\" (Barium-Sodium-Sodium-Sulfur). Note that there can be multiple possible spellings for a word—\"coin\" could be \"CoIn\" (Cobalt-Indium) or \"COIN\" (Carbon-Oxygen-Iodine-Nitrogen). \n",
|
||||
"\n",
|
||||
"Here is a sketch of a recursive algorithm to solve the problem. A word is **spellable** if any of the following are true:\n",
|
||||
"- The word is the empty word.\n",
|
||||
"- The first 2 letters of the word (capitalized) form an element symbol, and the rest of the word is spellable.\n",
|
||||
"- The first 1 letter of the word (capitalized) forms an element symbol, and the rest of the word is spellable.\n",
|
||||
"\n",
|
||||
"The input to `spellable` should be a string and the output is a boolean. Here is the code:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def spellable(word: str) -> bool:\n",
|
||||
" \"\"\"Can we spell `word` using the `symbols` of the elements?\"\"\"\n",
|
||||
" return (word == ''\n",
|
||||
" or word[:2].capitalize() in symbols and spellable(word[2:])\n",
|
||||
" or word[:1].capitalize() in symbols and spellable(word[1:]))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"I felt a bit bad about repeating a line of code above—violating [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)—but using a subfunction or `any/for` would add complexity. Here are the 118 currently defined `symbols`. (Note that the symbols are all capitalized, so I capitalize `[word[:2]` and `word[:1]` in `spellable` to make sure they match.)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"symbols = set( # Elements in the periodic table\n",
|
||||
" 'Ac Al Am Sb Ar As At Ba Bk Be Bi Bh B Br Cd Ca Cf C Ce Cs Cl Cr Co Cn Cu Cm Ds Db '\n",
|
||||
" 'Dy Es Er Eu Fm Fl F Fr Gd Ga Ge Au Hf Hs He Ho H In I Ir Fe Kr La Lr Pb Li Lv Lu '\n",
|
||||
" 'Mg Mn Mt Md Hg Mo Mc Nd Ne Np Ni Nh Nb N No Og Os O Pd P Pt Pu Po K Pr Pm Pa Ra Rn '\n",
|
||||
" 'Re Rh Rg Rb Ru Rf Sm Sc Sg Se Si Ag Na Sr S Ta Tc Te Ts Tb Tl Th Tm Sn Ti W U V Xe '\n",
|
||||
" 'Yb Y Zn Zr'.split())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can now| test the function (on `'Bananas'` and `'hello'`):"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"True"
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"spellable('Bananas')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"False"
|
||||
]
|
||||
},
|
||||
"execution_count": 25,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"spellable('hello')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"That was easy. \n",
|
||||
"\n",
|
||||
"But maybe you'd like to see the actual spelling:`'BaNaNaS'`. The function `spelling` does that. The general idea is the same, except:\n",
|
||||
" - We use the subfunction `first_rest_spelling` rather than repeating code.\n",
|
||||
" - Both `spelling` and `first_rest_spelling` return either a string (the spelling) or `None` if no spelling is possible.\n",
|
||||
" - There might be multiple possible spellings; only one is returned.\n",
|
||||
" - We use `lru_cache` to avoid repeated computation and thereby speed up the function."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 26,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from functools import lru_cache\n",
|
||||
"\n",
|
||||
"@lru_cache()\n",
|
||||
"def spelling(word):\n",
|
||||
" \"The spelling for `word` using `symbols` of the elements; or None if fail.\"\n",
|
||||
" return '' if word == '' else first_rest_spelling(word, 2) or first_rest_spelling(word, 1)\n",
|
||||
"\n",
|
||||
"def first_rest_spelling(word, k):\n",
|
||||
" \"Resulting spelling from taking off first k characters of word; or None if fail.\"\n",
|
||||
" first, rest = word[:k].capitalize(), word[k:]\n",
|
||||
" if first in symbols and spelling(rest) is not None:\n",
|
||||
" return first + spelling(rest)\n",
|
||||
" else:\n",
|
||||
" return None"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'BaNaNaS'"
|
||||
]
|
||||
},
|
||||
"execution_count": 27,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"spelling('bananas')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Testing\n",
|
||||
"\n",
|
||||
"Here I define `bad`, a list of words that are **not** spellable, and `good`, a list of words that **are**, and make some assertions:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"bad = 'hello world failure not an alternative'.split() # Unspellable words\n",
|
||||
"\n",
|
||||
"good = '''howdy sphere falure is notan option bananas \n",
|
||||
" carbon iron silver silicon copper arsenic tin xenon bismuth\n",
|
||||
" attention copernicus inconspicuous hyperbolic orbits functions\n",
|
||||
" wonky nutso officious psychic unprofessional bilateralism \n",
|
||||
" whippersnappers vichyssois bobbysocks alterabilities capabilities\n",
|
||||
" biostatistical physics floccinaucinihilipilification'''.split() # Spellable words\n",
|
||||
"\n",
|
||||
"assert len(symbols) == 118\n",
|
||||
"assert not any(spellable(w) or spelling(w) for w in bad) \n",
|
||||
"assert all(spellable(w) and spelling(w) for w in good)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And here are the actual spellings for the good words:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'AlTeRaBiLiTiEs',\n",
|
||||
" 'ArSeNiC',\n",
|
||||
" 'AtTeNTiON',\n",
|
||||
" 'BOBBYSOCKS',\n",
|
||||
" 'BaNaNaS',\n",
|
||||
" 'BiLaTeRaLiSm',\n",
|
||||
" 'BiOsTaTiSTiCAl',\n",
|
||||
" 'BiSmUTh',\n",
|
||||
" 'CaPaBiLiTiEs',\n",
|
||||
" 'CaRbON',\n",
|
||||
" 'CoPErNiCuS',\n",
|
||||
" 'CoPPEr',\n",
|
||||
" 'FAlURe',\n",
|
||||
" 'FUNCTiONS',\n",
|
||||
" 'FlOCCInAuCInIHILiPILiFICaTiON',\n",
|
||||
" 'HYPErBOLiC',\n",
|
||||
" 'HoWDy',\n",
|
||||
" 'IS',\n",
|
||||
" 'InCoNSPICuOUS',\n",
|
||||
" 'IrON',\n",
|
||||
" 'NUTsO',\n",
|
||||
" 'NoTaN',\n",
|
||||
" 'OFFICIOUS',\n",
|
||||
" 'OPtION',\n",
|
||||
" 'ORbITs',\n",
|
||||
" 'PHYSiCs',\n",
|
||||
" 'PSYCHIC',\n",
|
||||
" 'SPHeRe',\n",
|
||||
" 'SiLiCoN',\n",
|
||||
" 'SiLvEr',\n",
|
||||
" 'TiN',\n",
|
||||
" 'UNPrOFeSSiONAl',\n",
|
||||
" 'VICHYSSOIS',\n",
|
||||
" 'WHIPPErSNaPPErS',\n",
|
||||
" 'WONKY',\n",
|
||||
" 'XeNoN'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"{spelling(w) for w in good}"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
File diff suppressed because one or more lines are too long
510
ipynb/Maze.ipynb
510
ipynb/Maze.ipynb
File diff suppressed because one or more lines are too long
331
ipynb/PhotoFocalLengths.ipynb
Normal file
331
ipynb/PhotoFocalLengths.ipynb
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -2249,7 +2249,7 @@
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"A table and a plot will give a feel for the `util` function. Notice the characteristics concave-down shape of the plot."
|
||||
"A table and a plot will give a feel for the `util` function. Notice the characterisitc concave-down shape of the plot."
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -3094,7 +3094,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.5.3"
|
||||
"version": "3.7.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
1063
ipynb/ProbabilitySimulation.ipynb
Normal file
1063
ipynb/ProbabilitySimulation.ipynb
Normal file
File diff suppressed because one or more lines are too long
524
ipynb/RiddlerLottery.ipynb
Normal file
524
ipynb/RiddlerLottery.ipynb
Normal file
@ -0,0 +1,524 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"<div style=\"text-align:right\">Peter Norvig<br>Nov 2019</div>\n",
|
||||
"\n",
|
||||
"# Riddler Lottery\n",
|
||||
"\n",
|
||||
"The 538 Riddler [poses](https://fivethirtyeight.com/features/can-you-decode-the-riddler-lottery/) this problem:\n",
|
||||
"\n",
|
||||
"> Five friends with a lot in common are playing the [Riddler Lottery](https://fivethirtyeight.com/features/can-you-decode-the-riddler-lottery/), in which each must choose exactly five numbers from 1 to 70. After they all picked their numbers,\n",
|
||||
"- The first friend notices that no number was selected by two or more friends. \n",
|
||||
"- The second friend observes that all 25 selected numbers are composite (i.e., not prime). \n",
|
||||
"- The third friend points out that each selected number has at least two distinct prime factors. \n",
|
||||
"- The fourth friend excitedly remarks that the product of selected numbers on each ticket is exactly the same. \n",
|
||||
"- The fifth friend is left speechless. (You can tell why all these people are friends.)\n",
|
||||
"\n",
|
||||
"> 1. What is the product of the selected numbers on each ticket?\n",
|
||||
"2. How many _different_ ways could the friends have selected five numbers each so that all their statements are true?\n",
|
||||
"\n",
|
||||
"# Preliminary Analysis\n",
|
||||
"\n",
|
||||
"The fourth friend's statement was a bit unclear, but I take it to mean that each friend multiplied together their own five numbers, and they all got the same product. To be concrete, here's an example of a solution in a simplified version of the problem where each friend only selects two tickets, not five:\n",
|
||||
"\n",
|
||||
" Friend Selection Product Factors\n",
|
||||
" 1 ( 6, 60) 360 [2, 3] + [2, 2, 3, 5]\n",
|
||||
" 2 (10, 36) 360 [2, 5] + [2, 2, 3, 3]\n",
|
||||
" 3 (12, 30) 360 [2, 2, 3] + [2, 3, 5]\n",
|
||||
" 4 (15, 24) 360 [3, 5] + [2, 2, 2, 3]\n",
|
||||
" 5 (18, 20) 360 [2, 3, 3] + [2, 2, 5]\n",
|
||||
"\n",
|
||||
"And here's a list of the key concepts:\n",
|
||||
"\n",
|
||||
"- **number**: An integer from 1 to 70, e.g. the int `42`.\n",
|
||||
"- **factors**: Every positive integer has a unique prime factorization, e.g. `factors(12) == [2, 2, 3]`: two distinct primes factors. But `factors(8) == [2, 2, 2]`: one distinct prime factor.\n",
|
||||
"- **selection**: A collection of 5 numbers, e.g. the sorted tuple `(12, 15, 20, 28, 30)`.\n",
|
||||
"- **product**: The result of multiplying together the 5 numbers in a selection, e.g. the int `3024000`.\n",
|
||||
"- **candidate**: A candidate solution is a set of 5 selections e.g. `{( 6, 60), (10, 36), (12, 30), (15, 24), (18, 20)}` in my simplified version where each selection has only two numbers.\n",
|
||||
"- **solution**: A solution is a candidate that satisifes each of the four friends' statements.\n",
|
||||
"\n",
|
||||
"Can I use brute force and enumerate all the possible candidates? \n",
|
||||
"\n",
|
||||
"There are (70 choose 5) × (65 choose 5) × (60 choose 5) × (55 choose 5) × (50 choose 5) / 5! or [about](https://www.wolframalpha.com/input/?i=%2870+choose+5%29+*+%2865+choose+5%29+*+%2860+choose+5%29+*+%2855+choose+5%29+*+%2850+choose+5%29+%2F+5%21) $10^{31}$ candidates, so no. We'll have to be more clever."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Valid Numbers\n",
|
||||
"\n",
|
||||
"There will be fewer candidates to consider if we can reduce the number of valid numbers to select from. The __third__ friend stated that the numbers all have at least two distinct prime factors. So let's find the numbers that have that property. (The numbers we are dealing with are small, so don't worry about the inefficiency of my function `factors`.)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from itertools import combinations\n",
|
||||
"from collections import Counter, defaultdict"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"41 {6, 10, 12, 14, 15, 18, 20, 21, 22, 24, 26, 28, 30, 33, 34, 35, 36, 38, 39, 40, 42, 44, 45, 46, 48, 50, 51, 52, 54, 55, 56, 57, 58, 60, 62, 63, 65, 66, 68, 69, 70}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def factors(n) -> list:\n",
|
||||
" \"List of prime factors that multiply together to give n.\"\n",
|
||||
" return ([] if n == 1\n",
|
||||
" else next([p] + factors(n // p) \n",
|
||||
" for p in range(2, n + 1) if n % p == 0))\n",
|
||||
"\n",
|
||||
"distinct = set\n",
|
||||
"\n",
|
||||
"numbers = {n for n in range(1, 71) if len(distinct(factors(n))) >= 2}\n",
|
||||
"\n",
|
||||
"print(len(numbers), numbers)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Great; we got it down from 70 to 41 possible numbers.\n",
|
||||
"\n",
|
||||
"Now the __fourth__ friend's statement says that each friend picks five numbers that have the same product. \n",
|
||||
"In my simplified version where the friends pick two numbers each, they all picked a selection with the product 360. The prime factorization of 360 is `[2, 2, 2, 3, 3, 5]`; that means that all five friends had to find a different way of allocating these factors to their numbers. \n",
|
||||
"\n",
|
||||
"For each number that is selected by any friend, and for each prime factor $p$ of that number, it must be the case that there are at least four other valid numbers that also have $p$ as a factor; otherwise the product couldn't be the same for all the friends. So let's count in how many numbers each prime appears:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Counter({2: 29,\n",
|
||||
" 3: 20,\n",
|
||||
" 5: 12,\n",
|
||||
" 7: 8,\n",
|
||||
" 11: 5,\n",
|
||||
" 13: 4,\n",
|
||||
" 17: 3,\n",
|
||||
" 19: 2,\n",
|
||||
" 23: 2,\n",
|
||||
" 29: 1,\n",
|
||||
" 31: 1})"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"prime_counts = Counter(p for n in numbers for p in distinct(factors(n)))\n",
|
||||
"\n",
|
||||
"prime_counts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This says that the prime factor 2 appears in 29 valid numbers and the prime factor 31 appears in only 1 valid number. Only factors that appear in at least 5 valid numbers can be part of a solution, so that's `{2, 3, 5, 7, 11}`.\n",
|
||||
"\n",
|
||||
"Let's update the set of valid numbers to contain only numbers $n$ such that every prime factor $p$ of $n$ appears in at least 5 valid numbers:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"28 {6, 10, 12, 14, 15, 18, 20, 21, 22, 24, 28, 30, 33, 35, 36, 40, 42, 44, 45, 48, 50, 54, 55, 56, 60, 63, 66, 70}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"numbers = {n for n in numbers if all(prime_counts[p] >= 5 for p in distinct(factors(n)))}\n",
|
||||
"\n",
|
||||
"print(len(numbers), numbers)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"There are now only 28 valid numbers; a nice reduction from 70 to 41 to 28.\n",
|
||||
"\n",
|
||||
"# Valid Selections\n",
|
||||
"\n",
|
||||
"Now let's switch attention from individual numbers to selections of five numbers. There are (28 choose 5) = 98,280 possible selections; a manageable number. But that means there are (98,280 choose 5) = $\\approx 10^{23}$ candidate solutions; an unmanageable number.\n",
|
||||
"\n",
|
||||
"My first thought to reduce the number of candidates is to say that we should only consider candidates where all five selections in the candidate have the same product. To do that, we can group selections by product.\n",
|
||||
"\n",
|
||||
"We'll make `products` be a `dict` where each key is the product of the five numbers in a selection, and the corresponding value is a list of all the selections of five numbers with that product:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def multimap(items, key) -> dict:\n",
|
||||
" \"A dict of {key(item): [item, ...]}\"\n",
|
||||
" result = defaultdict(list)\n",
|
||||
" for x in items:\n",
|
||||
" result[key(x)].append(x)\n",
|
||||
" return result\n",
|
||||
"\n",
|
||||
"def product(nums) -> int: \n",
|
||||
" \"Multiply nums together (similar to sum(nums)).\"\n",
|
||||
" result = 1\n",
|
||||
" for num in nums:\n",
|
||||
" result *= num\n",
|
||||
" return result\n",
|
||||
"\n",
|
||||
"products = multimap(combinations(numbers, 5), key=product)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Here is an entry in the `products` dict:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[(6, 10, 12, 14, 30),\n",
|
||||
" (6, 10, 12, 15, 28),\n",
|
||||
" (6, 10, 12, 20, 21),\n",
|
||||
" (6, 10, 14, 15, 24),\n",
|
||||
" (6, 10, 14, 18, 20),\n",
|
||||
" (6, 12, 14, 15, 20)]"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"products[6 * 12 * 14 * 15 * 20]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This says there are 6 selections whose product is 6 * 12 * 14 * 15 * 20 = 302,400; if there were a way to choose 5 of these 6 with all distinct numbers, that would be a solution. Sadly, there is no such way. Since every one of the selections contains a 6; we can't even choose two disjoint selections, let alone five.\n",
|
||||
"\n",
|
||||
"Let's see how many different products there are, and how many have at least five selections:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(4042, 2407)"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"len(products), len([n for n in products if len(products[n]) >= 5])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"It seems reasonable to go through all the products and see if one of the 29,506 with 5 or more selections can come up with 5 disjoint selections. The function `k_disjoint(k, selections)` finds all ways to choose `k` different elements of `selections` such that there is no shared number among any selection. The function keeps track of a `partial_solution`—a set of previously-found selections—as it recursively searches for a complete solution. Any new selection must be disjoint from all the selections in `partial_solution`. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def k_disjoint(k, selections, start=0, partial_solution=set()) -> list:\n",
|
||||
" \"All ways of picking k elements of selections that have all disjoint members.\"\n",
|
||||
" if len(partial_solution) == k:\n",
|
||||
" yield partial_solution\n",
|
||||
" elif len(partial_solution) + (len(selections) - start) >= k:\n",
|
||||
" for i in range(start, len(selections)):\n",
|
||||
" selection = selections[i]\n",
|
||||
" if all(is_disjoint(selection, s) for s in partial_solution):\n",
|
||||
" yield from k_disjoint(k, selections, i + 1, partial_solution | {selection})\n",
|
||||
" \n",
|
||||
"def is_disjoint(A, B) -> bool: return not any(a in B for a in A)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Here's an example of how `k_disjoint` works. Out of the following six selections (which in this simplified example have only two numbers each, not five), there are two ways to pick five selections without having a duplicate number:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{(6, 60), (10, 36), (12, 30), (15, 24), (18, 20)},\n",
|
||||
" {(6, 60), (10, 35), (12, 30), (15, 24), (18, 20)}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"selections = [(6, 60), (10, 36), (12, 30), (15, 24), (18, 20), (10, 35)]\n",
|
||||
"list(k_disjoint(5, selections))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"But for the six selections whose product is 302,400, there are no disjoint solutions:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[]"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"selections = [(6, 10, 12, 14, 30),\n",
|
||||
" (6, 10, 12, 15, 28),\n",
|
||||
" (6, 10, 12, 20, 21),\n",
|
||||
" (6, 10, 14, 15, 24),\n",
|
||||
" (6, 10, 14, 18, 20),\n",
|
||||
" (6, 12, 14, 15, 20)]\n",
|
||||
"list(k_disjoint(5, selections))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now we're ready to solve the problem.\n",
|
||||
"\n",
|
||||
"# 1. What is the product of the selected numbers on each ticket?\n",
|
||||
"\n",
|
||||
"That is, find the number `N` in `products` that can form 5 disjoint selections."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"The product is 19,958,400; factors are [2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 5, 5, 7, 11]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"N = next(n for n in products if any(k_disjoint(5, products[n])))\n",
|
||||
"\n",
|
||||
"print(f'The product is {N:,d}; factors are {factors(N)}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 2. How many different ways could the friends have selected five numbers?\n",
|
||||
"\n",
|
||||
"I'll compute all of the results for `k_disjoint`, and see how many there are:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"There are 12,781 different ways.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"different_ways = list(k_disjoint(5, products[N]))\n",
|
||||
"print(f'There are {len(different_ways):,d} different ways.')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"That's too many to look at all of them, but I can peek at every thousandth one:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{(6, 15, 56, 60, 66),\n",
|
||||
" (10, 14, 48, 54, 55),\n",
|
||||
" (12, 18, 42, 44, 50),\n",
|
||||
" (20, 21, 33, 36, 40),\n",
|
||||
" (22, 24, 28, 30, 45)},\n",
|
||||
" {(6, 18, 55, 56, 60),\n",
|
||||
" (10, 21, 30, 48, 66),\n",
|
||||
" (12, 22, 28, 50, 54),\n",
|
||||
" (14, 20, 36, 44, 45),\n",
|
||||
" (15, 24, 33, 40, 42)},\n",
|
||||
" {(6, 20, 54, 55, 56),\n",
|
||||
" (10, 21, 36, 44, 60),\n",
|
||||
" (12, 28, 33, 40, 45),\n",
|
||||
" (14, 18, 24, 50, 66),\n",
|
||||
" (15, 22, 30, 42, 48)},\n",
|
||||
" {(6, 21, 48, 50, 66),\n",
|
||||
" (10, 24, 33, 45, 56),\n",
|
||||
" (12, 18, 28, 55, 60),\n",
|
||||
" (14, 20, 30, 44, 54),\n",
|
||||
" (15, 22, 36, 40, 42)},\n",
|
||||
" {(6, 22, 50, 54, 56),\n",
|
||||
" (10, 14, 36, 60, 66),\n",
|
||||
" (12, 18, 40, 42, 55),\n",
|
||||
" (15, 21, 30, 44, 48),\n",
|
||||
" (20, 24, 28, 33, 45)},\n",
|
||||
" {(6, 24, 42, 50, 66),\n",
|
||||
" (10, 18, 33, 56, 60),\n",
|
||||
" (12, 28, 30, 36, 55),\n",
|
||||
" (14, 15, 40, 44, 54),\n",
|
||||
" (20, 21, 22, 45, 48)},\n",
|
||||
" {(6, 28, 30, 60, 66),\n",
|
||||
" (10, 15, 44, 54, 56),\n",
|
||||
" (12, 22, 36, 42, 50),\n",
|
||||
" (14, 24, 33, 40, 45),\n",
|
||||
" (18, 20, 21, 48, 55)},\n",
|
||||
" {(6, 28, 40, 45, 66),\n",
|
||||
" (10, 30, 33, 42, 48),\n",
|
||||
" (12, 21, 24, 55, 60),\n",
|
||||
" (14, 18, 36, 44, 50),\n",
|
||||
" (15, 20, 22, 54, 56)},\n",
|
||||
" {(6, 28, 44, 50, 54),\n",
|
||||
" (10, 21, 33, 48, 60),\n",
|
||||
" (12, 14, 40, 45, 66),\n",
|
||||
" (15, 22, 30, 36, 56),\n",
|
||||
" (18, 20, 24, 42, 55)},\n",
|
||||
" {(6, 30, 36, 55, 56),\n",
|
||||
" (10, 15, 42, 48, 66),\n",
|
||||
" (12, 28, 33, 40, 45),\n",
|
||||
" (14, 20, 22, 54, 60),\n",
|
||||
" (18, 21, 24, 44, 50)},\n",
|
||||
" {(6, 30, 44, 45, 56),\n",
|
||||
" (10, 15, 42, 48, 66),\n",
|
||||
" (12, 14, 40, 54, 55),\n",
|
||||
" (18, 24, 28, 33, 50),\n",
|
||||
" (20, 21, 22, 36, 60)},\n",
|
||||
" {(6, 33, 40, 45, 56),\n",
|
||||
" (10, 12, 42, 60, 66),\n",
|
||||
" (14, 22, 24, 50, 54),\n",
|
||||
" (15, 28, 30, 36, 44),\n",
|
||||
" (18, 20, 21, 48, 55)},\n",
|
||||
" {(6, 36, 40, 42, 55),\n",
|
||||
" (10, 20, 33, 54, 56),\n",
|
||||
" (12, 18, 28, 50, 66),\n",
|
||||
" (14, 15, 44, 45, 48),\n",
|
||||
" (21, 22, 24, 30, 60)}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"different_ways[::1000]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
26920
ipynb/Scrabble.ipynb
26920
ipynb/Scrabble.ipynb
File diff suppressed because one or more lines are too long
1532
ipynb/SpellingBee.ipynb
Normal file
1532
ipynb/SpellingBee.ipynb
Normal file
File diff suppressed because it is too large
Load Diff
639
ipynb/StableMatching.ipynb
Normal file
639
ipynb/StableMatching.ipynb
Normal file
File diff suppressed because one or more lines are too long
477
ipynb/TourDe538.ipynb
Normal file
477
ipynb/TourDe538.ipynb
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,19 @@
|
||||
#Ride Date Title mi ft
|
||||
# 2020 Eddington rides only
|
||||
Ride Sun, 1/26/2020 Los Gatos / Bay 5:18:15 67.98 mi 1,286 ft
|
||||
Ride Fri, 1/31/2020 Bay Trail / Guadalupe / Geocaching 5:21:39 69.39 mi 1,125 ft
|
||||
# 2019 Eddington rides only
|
||||
Ride Sun, 6/2/2019 The Sequoia 6:40:43 77.51 mi 6,467 ft
|
||||
Ride Sat, 6/8/2019 Morning Ride 2:53:08 34.42 mi 1,824 ft
|
||||
Ride Sat, 6/15/2019 Morning Ride 2:29:38 29.98 mi 1,785 ft
|
||||
Ride Fri, 7/5/2019 Crystal Springs 4:30:55 64.05 mi 1,965 ft
|
||||
Ride Sun, 8/18/2019 Afternoon Ride 2:06:52 27.14 mi 331 ft
|
||||
Ride Sat, 8/24/2019 Tour de Fox 4:43:39 68.66 mi 2,097 ft
|
||||
Ride Sat, 8/31/2019 San Gregorio via OLH 5:13:21 65.31 mi 4,026 ft
|
||||
Ride Fri, 9/6/2019 San Mateo / Bay Trail 4:59:29 68.01 mi 1,158 ft
|
||||
Ride Sat, 11/16/2019 Canada / Crystal Dam / Sawyer Camp / Bay Trail 5:18:46 66.08 mi 1,972 ft
|
||||
# 2012 to 2018 rides over 25 miles
|
||||
Ride Fri, 1/9/2012 Otago Rail Trail 8:00:00 101.00 mi 2,200 ft
|
||||
Ride Thu, 6/14/2012 Coyote Creek Century with Juliet 8:08:15 100.07 mi 1,513 ft
|
||||
Ride Sat, 5/13/2017 Morgan Hill iCare Classic 7:27:21 100.05 mi 4,596 ft
|
||||
Ride Sat, 5/12/2018 ICare Classic, Morgan Hill 6:47:46 91.29 mi 4,160 ft
|
||||
|
Can't render this file because it has a wrong number of fields in line 3.
|
@ -1,64 +1,56 @@
|
||||
Alma Mountain Charlie, 3.12, 875, 31:49
|
||||
Alpine Westridge, 0.76, 99, 3:26, 3:29, 3:43
|
||||
Alpine last kicker, 0.39, 114, 3:11, 3:36, 5:19
|
||||
Arastradero, 1.19, 193, 6:41, 7:49, 7:55
|
||||
Bring it On Home, 10.81, 237, 37:19
|
||||
Cabrillo Hmy S, 0.46, 153, 4:31
|
||||
Canada South Interval, 1.42, 54, 4:50, 5:08, 5:20
|
||||
Canada to College, 1.37, 119, 5:55, 6:23, 6:35
|
||||
Canon, 0.90, 295, 11:05, 11:56
|
||||
Canon to No Cycling, 0.75, 198, 5:14, 5:41
|
||||
El Camino, 0.50, 15, 2:11, 2:19, 2:22
|
||||
Foothill Arastradero, 1.13, 37, 3:31, 3:44, 3:51
|
||||
Foothill Homestead, 1.22, 126, 5:31, 6:36, 6:53
|
||||
Foothill Magdalena, 1.66, 44, 5:51, 6:21, 6:29
|
||||
Foothill Miramonte, 1.64, 39, 4:55, 5:29, 5:43
|
||||
Haskins, 1.51, 566, 17:48, 18:43, 18:48
|
||||
Highway 9, 0.56, 260, 8:33
|
||||
Highway 9 Mantalvo, 0.45, 35, 1:36, 1:48, 1:53
|
||||
Huddart, 0.92, 385, 10:15, 11:18, 12:55
|
||||
Joaquin, 0.33, 253, 6:19, 7:27
|
||||
Kaboom Portola Rd, 0.67, 102, 3:14, 3:26, 3:33
|
||||
Kings half way, 2.89, 820, 27:26, 29:45, 32:43
|
||||
Kings to Skeggs, 1.10, 273, 9:08, 10:39, 10:54
|
||||
Limantour Spit, 0.47, 303, 5:20
|
||||
Limantour steepest, 0.20, 159, 5:41
|
||||
Lobitas Creek, 0.96, 430, 12:15, 12:07
|
||||
Lower Redwood Gulch, 1.03, 474, 13:00
|
||||
Mapache, 0.24, 111, 2:52, 2:56
|
||||
Mt Eden climb, 1.02, 272, 8:32, 9:43, 11:59
|
||||
Mt Eden to Archery, 0.54, 180, 5:20, 5:39, 6:12
|
||||
Old La Honda, 2.98, 1255, 28:49, 34:04, 36:45
|
||||
Old La Honda Mile 1, 0.99, 370, 8:02, 9:36, 9:51
|
||||
Page Mill to Moody, 0.21, 112, 2:50, 2:55
|
||||
Page Mill to Ventana, 0.47, 196, 4:54, 5:32, 6:05
|
||||
Peach Hill, 0.60, 248, 7:30, 7:50
|
||||
Pescadero Bean Hollow, 2.77, 51, 8:07, 10:48
|
||||
Pomponio Creek, 0.38, 122, 2:47, 2:56, 3:16
|
||||
Red Hill KOM, 0.34, 161, 4:15
|
||||
Redwood Gulch hits, 0.18, 151, 3:36
|
||||
Redwood Gulch wall, 0.43, 258, 6:39
|
||||
Sand Gill Sharon-top, 0.85, 136, 4:04, 4:16, 4:24
|
||||
Sand Hill 280 to horse, 0.49, 95, 2:34, 2:44, 2:51
|
||||
Sand Hill Alpine to 280, 1.67, 180, 7:02, 7:40, 8:02
|
||||
Stair Step, 0.32, 175, 5:25
|
||||
Stanford Ave, 0.63, 85, 3:14, 3:32, 3:49
|
||||
Stevens Canyon, 2.85, 127, 12:51, 13:46, 18:12
|
||||
Stevens Country Park, 1.22, 112, 5:43, 5:59, 7:00
|
||||
Stirrup Wall, 0.17, 125, 3:47, 4:33
|
||||
Stirrup to Moon, 0.36, 159, 3:41, 6:07
|
||||
Summit Rd Climb, 0.60, 275, 8:00
|
||||
Tepa, 0.58, 248, 6:34, 7:14, 8:21
|
||||
The Boneyard, 1.48, 135, 6:05, 6:36, 7:00
|
||||
Try not to fall back, 0.71, 410, 12:45
|
||||
Tunitas Knoll to Star, 1.55, 339, 12:28, 12:56, 13:51
|
||||
Tunitas flattens, 0.42, 166, 3:16, 3:25, 3:51
|
||||
Tunitas lower climb, 1.30, 421, 13:34, 16:00, 17:33
|
||||
Tunitas steep, 1.20, 599, 16:16, 17:48, 20:44
|
||||
Vickrey Fruitvale, 0.99, 68, 3:41, 3:58, 4:27
|
||||
West Alpine switchback, 0.78, 322, 8:56, 10:52, 11:56
|
||||
Westridge, 0.68, 385, 8:28, 9:52, 10:19
|
||||
Westridge 3min, 0.37, 240, 4:41, 5:35, 6:31
|
||||
Westridge Hill 1, 0.51, 104, 3:19, 3:58
|
||||
Westridge Hill 2, 0.51, 166, 4:42, 5:10, 5:21
|
||||
Woodside Climb, 1.71, 295, 8:05, 9:00, 9:35
|
||||
Alma Mountain Charlie, 3.12, 875, 31:49
|
||||
Alpine Westridge, 0.76, 99, 3:26, 3:29, 3:43
|
||||
Alpine last kicker, 0.39, 114, 3:11, 3:36, 5:19
|
||||
Arastradero, 1.19, 193, 6:41, 7:49, 7:55
|
||||
Cabrillo Hmy S, 0.46, 153, 4:31
|
||||
Canada to College, 1.37, 119, 5:55, 6:23, 6:35
|
||||
Canon, 0.90, 295, 11:04, 11:55
|
||||
Canon to No Cycling, 0.75, 198, 5:14, 5:41
|
||||
Foothill Homestead, 1.22, 126, 5:31, 6:36, 6:53
|
||||
Haskins, 1.51, 566, 17:47, 18:43, 18:47
|
||||
Highway 9, 0.56, 260, 8:33
|
||||
Highway 9 Mantalvo, 0.45, 35, 1:36, 1:48, 1:53
|
||||
Huddart, 0.92, 385, 10:15, 11:18, 12:55
|
||||
Joaquin, 0.33, 253, 6:19, 7:27
|
||||
Kaboom Portola Rd, 0.67, 102, 3:14, 3:26, 3:33
|
||||
Kings half way, 2.89, 820, 27:26, 29:45, 32:43
|
||||
Kings to Skeggs, 1.10, 273, 9:07, 10:39, 10:53
|
||||
Limantour Spit, 0.47, 303, 5:19
|
||||
Limantour steepest, 0.20, 159, 5:41
|
||||
Lobitas Creek, 0.96, 430, 12:15, 12:07
|
||||
Lower Redwood Gulch, 1.03, 474, 13:00
|
||||
Mapache, 0.24, 111, 2:52, 2:55
|
||||
Mt Eden climb, 1.02, 272, 8:32, 9:43, 11:59
|
||||
Mt Eden to Archery, 0.54, 180, 5:19, 5:39, 6:12
|
||||
Old La Honda, 2.98, 1255, 28:49, 34:03, 36:44
|
||||
Old La Honda Mile 1, 0.99, 370, 8:02, 9:36, 9:51
|
||||
Page Mill to Moody, 0.21, 112, 2:50, 2:55
|
||||
Page Mill to Ventana, 0.47, 196, 4:54, 5:32, 6:05
|
||||
Peach Hill, 0.60, 248, 7:30, 7:50
|
||||
Pomponio Creek, 0.38, 122, 2:47, 2:55, 3:16
|
||||
Red Hill KOM, 0.34, 161, 4:15
|
||||
Redwood Gulch hits, 0.18, 151, 3:36
|
||||
Redwood Gulch wall, 0.43, 258, 6:39
|
||||
Sand Gill Sharon-top, 0.85, 136, 4:03, 4:16, 4:24
|
||||
Sand Hill 280 to horse, 0.49, 95, 2:34, 2:44, 2:51
|
||||
Sand Hill Alpine to 280, 1.67, 180, 7:02, 7:39, 8:02
|
||||
Stair Step, 0.32, 175, 5:25
|
||||
Stanford Ave, 0.63, 85, 3:14, 3:32, 3:49
|
||||
Stevens Country Park, 1.22, 112, 5:42, 5:58, 7:00
|
||||
Stirrup Wall, 0.17, 125, 3:47, 4:33
|
||||
Stirrup to Moon, 0.36, 159, 3:41, 6:07
|
||||
Summit Rd Climb, 0.60, 275, 8:00
|
||||
Tepa, 0.58, 248, 6:34, 7:14, 8:21
|
||||
The Boneyard, 1.48, 135, 6:05, 6:36, 7:00
|
||||
Try not to fall back, 0.71, 410, 12:45
|
||||
Tunitas Knoll to Star, 1.55, 339, 12:28, 12:56, 13:51
|
||||
Tunitas flattens, 0.42, 166, 3:16, 3:25, 3:51
|
||||
Tunitas lower climb, 1.30, 421, 13:34, 16:00, 17:33
|
||||
Tunitas steep, 1.20, 599, 16:15, 17:47, 20:44
|
||||
Vickrey Fruitvale, 0.99, 68, 3:41, 3:58, 4:26
|
||||
West Alpine switchback, 0.78, 322, 8:56, 10:52, 11:55
|
||||
Westridge, 0.68, 385, 8:28, 9:52, 10:18
|
||||
Westridge 3min, 0.37, 240, 4:41, 5:35, 6:31
|
||||
Westridge Hill 1, 0.51, 104, 3:19, 3:58
|
||||
Westridge Hill 2, 0.51, 166, 4:42, 5:10, 5:20
|
||||
Woodside Climb, 1.71, 295, 8:05, 9:00, 9:35
|
|
BIN
ipynb/convexhull.jpg
Normal file
BIN
ipynb/convexhull.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
813
ipynb/evs.html
813
ipynb/evs.html
File diff suppressed because one or more lines are too long
559
ipynb/flipping.ipynb
Normal file
559
ipynb/flipping.ipynb
Normal file
File diff suppressed because one or more lines are too long
1
ipynb/natalie.txt
Normal file
1
ipynb/natalie.txt
Normal file
File diff suppressed because one or more lines are too long
@ -257,7 +257,7 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The main point of the `Panama` class is to allow the `.search()` method to search for a long palindrome. The [overall strategy](http://norvig.com/pal-alg.html#v3) is explained elsewhere. The search\n",
|
||||
"adds a lett1er at a time to both left and right side, backtracking when necessary. The state of the search is represented by the four fields `.left, .L, .R, .right`, for example:\n",
|
||||
"adds a letter at a time to both left and right side, backtracking when necessary. The state of the search is represented by the four fields `.left, .L, .R, .right`, for example:\n",
|
||||
" \n",
|
||||
" A man, a plan, a cam, a yak, a yam, a canal, Panama\n",
|
||||
" \n",
|
||||
@ -272,7 +272,7 @@
|
||||
"\n",
|
||||
" cat(left + [L]) == cat([R] + right)[::-1]\n",
|
||||
" \n",
|
||||
"The `search` method would be more straightforward if we could write it is a recursive function. But Python does not perform well when recursing to 100 thousand levels deep. So instead I manually manage a *stack* of *commands* that tell the search what to do and to undo. A command can be a `DoCommand`, which consists of a list of letters describing all the possible actions we could take at this point. Actions include letters that could be added (only the letters that make a legitimate prefix of a known phrase in `.L` and a legitimate suffix of a known phrase in `.R`). Actions can also include the character `','`, which is used to dignal that `.L` is a complete word and should be moved ontot he `.left` list, or `';'` to signal the same for `.R/.right`. A command can also be a `UndoCommand`, which consists of a single character; one that was previously chosen to be done. So the list of characters in a `DoCommand` constitutes a choice point; we first choose one, and continue deeper from there, but we put on the command stack an `UndoCommand` to reverse the effects of the action, and put back the remaining characters to try instead, if the first character doesn't work out. Note that we pop characters off the end of a `DoCommand`, so the last character is the first one tried.\n",
|
||||
"The `search` method would be more straightforward if we could write it is a recursive function. But Python does not perform well when recursing to 100 thousand levels deep. So instead I manually manage a *stack* of *commands* that tell the search what to do and to undo. A command can be a `DoCommand`, which consists of a list of letters describing all the possible actions we could take at this point. Actions include letters that could be added (only the letters that make a legitimate prefix of a known phrase in `.L` and a legitimate suffix of a known phrase in `.R`). Actions can also include the character `','`, which is used to signal that `.L` is a complete word and should be moved onto the `.left` list, or `';'` to signal the same for `.R/.right`. A command can also be a `UndoCommand`, which consists of a single character; one that was previously chosen to be done. So the list of characters in a `DoCommand` constitutes a choice point; we first choose one, and continue deeper from there, but we put on the command stack an `UndoCommand` to reverse the effects of the action, and put back the remaining characters to try instead, if the first character doesn't work out. Note that we pop characters off the end of a `DoCommand`, so the last character is the first one tried.\n",
|
||||
"\n",
|
||||
"Let's see how it works:"
|
||||
]
|
||||
@ -452,7 +452,7 @@
|
||||
"- I'm not sure when to end a word and when to try to continue to a longer word; experimentation here would be useful.\n",
|
||||
"- The program is deterministic, and thus always finds the same plaindrome. that's boring; can some randomness be introduced?\n",
|
||||
"- Can we make more interesting phrases? Include determiners other than \"a\" and \"an\"; include adjectives, etc.\n",
|
||||
"- The counts of prefixes and suffixes include all the phrases in the dictionary. But we are not allowed to repeat a phrase, so can we modify the code to subtract one from the counts every time a phrase is used (and add onr back in when we backtrack over the phrase)?\n",
|
||||
"- The counts of prefixes and suffixes include all the phrases in the dictionary. But we are not allowed to repeat a phrase, so can we modify the code to subtract one from the counts every time a phrase is used (and add one back in when we backtrack over the phrase)?\n",
|
||||
"\n",
|
||||
"Perhaps you can find other areas to explore.\n",
|
||||
"\n",
|
||||
|
1956
ipynb/poker.ipynb
Normal file
1956
ipynb/poker.ipynb
Normal file
File diff suppressed because one or more lines are too long
139
ipynb/portman.py
Normal file
139
ipynb/portman.py
Normal file
@ -0,0 +1,139 @@
|
||||
# Generate a portmantout word
|
||||
# Peter Norvig
|
||||
# See https://github.com/norvig/pytudes/blob/master/ipynb/Portmantout.ipynb
|
||||
|
||||
from collections import defaultdict, Counter
|
||||
from typing import List, Tuple, Set, Dict, Any
|
||||
|
||||
Word = str
|
||||
class Wordset(set): """A set of words."""
|
||||
Step = Tuple[int, str] # An (overlap, word) pair.
|
||||
OVERLAP, WORD = 0, 1 # Indexes of the two parts of a Step.
|
||||
Path = List[Step] # A list of steps.
|
||||
Bridge = (int, Step,...) # An excess letter count and step(s), e.g. (1, (2, 'arrow')).
|
||||
EXCESS, STEPS = 0, slice(1, None) # Indexes of the two parts of a bridge.
|
||||
|
||||
W = Wordset(open('wordlist.asc').read().split())
|
||||
|
||||
def portman(P: Path) -> Word:
|
||||
"""Compute the portmantout string S from the path P."""
|
||||
return ''.join(word[overlap:] for (overlap, word) in P)
|
||||
|
||||
def natalie(W: Wordset, start=None) -> Path:
|
||||
"""Return a portmantout path containing all words in W."""
|
||||
precompute(W)
|
||||
word = start or first(W.unused)
|
||||
used(W, word)
|
||||
P = [(0, word)]
|
||||
while W.unused:
|
||||
steps = unused_step(W, word) or bridging_steps(W, word)
|
||||
for (overlap, word) in steps:
|
||||
P.append((overlap, word))
|
||||
used(W, word)
|
||||
return P
|
||||
|
||||
def unused_step(W: Wordset, prev_word: Word) -> List[Step]:
|
||||
"""Return [(overlap, unused_word)] or []."""
|
||||
for suf in suffixes(prev_word):
|
||||
for unused_word in W.startswith.get(suf, ()):
|
||||
overlap = len(suf)
|
||||
return [(overlap, unused_word)]
|
||||
return []
|
||||
|
||||
def bridging_steps(W: Wordset, prev_word: Word) -> List[Step]:
|
||||
"""The steps from the shortest bridge that bridges
|
||||
from a suffix of prev_word to a prefix of an unused word."""
|
||||
bridge = min(W.bridges[suf][pre]
|
||||
for suf in suffixes(prev_word) if suf in W.bridges
|
||||
for pre in W.bridges[suf] if W.startswith[pre])
|
||||
return bridge[STEPS]
|
||||
|
||||
def precompute(W):
|
||||
"""Precompute and cache data structures for W. The .subwords and .bridges
|
||||
data structures are static and only need to be computed once; .unused and
|
||||
.startswith are dynamic and must be recomputed on each call to `natalie`."""
|
||||
if not hasattr(W, 'subwords') or not hasattr(W, 'bridges'):
|
||||
W.subwords = subwords(W)
|
||||
W.bridges = build_bridges(W)
|
||||
W.unused = W - W.subwords
|
||||
W.startswith = compute_startswith(W.unused)
|
||||
|
||||
def used(W, word):
|
||||
"""Remove word from `W.unused` and, for each prefix, from `W.startswith[pre]`."""
|
||||
assert word in W, f'used "{word}", which is not in the word set'
|
||||
if word in W.unused:
|
||||
W.unused.remove(word)
|
||||
for pre in prefixes(word):
|
||||
W.startswith[pre].remove(word)
|
||||
if not W.startswith[pre]:
|
||||
del W.startswith[pre]
|
||||
|
||||
def first(iterable, default=None): return next(iter(iterable), default)
|
||||
|
||||
def multimap(pairs) -> Dict[Any, set]:
|
||||
"""Given (key, val) pairs, make a dict of {key: {val,...}}."""
|
||||
result = defaultdict(set)
|
||||
for key, val in pairs:
|
||||
result[key].add(val)
|
||||
return result
|
||||
|
||||
def compute_startswith(words) -> Dict[str, Set[Word]]:
|
||||
"""A dict mapping a prefix to all the words it starts:
|
||||
{'somet': {'something', 'sometimes'},...}."""
|
||||
return multimap((pre, w) for w in words for pre in prefixes(w))
|
||||
|
||||
def subwords(W: Wordset) -> Set[str]:
|
||||
"""All the words in W that are subparts of some other word."""
|
||||
return {subword for w in W for subword in subparts(w) & W}
|
||||
|
||||
def suffixes(word) -> List[str]:
|
||||
"""All non-empty proper suffixes of word, longest first."""
|
||||
return [word[i:] for i in range(1, len(word))]
|
||||
|
||||
def prefixes(word) -> List[str]:
|
||||
"""All non-empty proper prefixes of word."""
|
||||
return [word[:i] for i in range(1, len(word))]
|
||||
|
||||
def subparts(word) -> Set[str]:
|
||||
"""All non-empty proper substrings of word"""
|
||||
return {word[i:j]
|
||||
for i in range(len(word))
|
||||
for j in range(i + 1, len(word) + (i > 0))}
|
||||
|
||||
def splits(word) -> List[Tuple[int, str, str]]:
|
||||
"""A sequence of (excess, pre, suf) tuples."""
|
||||
return [(excess, word[:i], word[i+excess:])
|
||||
for excess in range(len(word) - 1)
|
||||
for i in range(1, len(word) - excess)]
|
||||
|
||||
def try_bridge(bridges, pre, suf, excess, word, step2=None):
|
||||
"""Store a new bridge if it has less excess than the previous bridges[pre][suf]."""
|
||||
if suf not in bridges[pre] or excess < bridges[pre][suf][EXCESS]:
|
||||
bridge = (excess, (len(pre), word))
|
||||
if step2: bridge += (step2,)
|
||||
bridges[pre][suf] = bridge
|
||||
|
||||
def build_bridges(W: Wordset, maxlen=5, end='qujvz'):
|
||||
"""A table of bridges[pre][suf] == (excess, (overlap, word)), e.g.
|
||||
bridges['ar']['c'] == (0, (2, 'arc'))."""
|
||||
bridges = defaultdict(dict)
|
||||
shortwords = [w for w in W if len(w) <= maxlen + (w[-1] in end)]
|
||||
shortstartswith = compute_startswith(shortwords)
|
||||
# One-word bridges
|
||||
for word in shortwords:
|
||||
for excess, pre, suf, in splits(word):
|
||||
try_bridge(bridges, pre, suf, excess, word)
|
||||
# Two-word bridges
|
||||
for word1 in shortwords:
|
||||
for suf in suffixes(word1):
|
||||
for word2 in shortstartswith[suf]:
|
||||
excess = len(word1) + len(word2) - len(suf) - 2
|
||||
A, B = word1[0], word2[-1]
|
||||
if A != B:
|
||||
step2 = (len(suf), word2)
|
||||
try_bridge(bridges, A, B, excess, word1, step2)
|
||||
return bridges
|
||||
|
||||
if __name__ == "__main__":
|
||||
W = Wordset(open('wordlist.asc').read().split())
|
||||
print(portman(natalie(W)))
|
File diff suppressed because one or more lines are too long
147
py/pytudes.py
Normal file
147
py/pytudes.py
Normal file
@ -0,0 +1,147 @@
|
||||
# Run "python pytudes.py" to create README.md for pytudes
|
||||
|
||||
|
||||
def nb(title, url, comment=''):
|
||||
"""Make a markdown table entry for a jupyter/ipython notebook."""
|
||||
urlb = f'/blob/master/ipynb/{url}'
|
||||
co = f'[co](https://colab.research.google.com/github/norvig/pytudes{urlb})'
|
||||
dn = f'[dn](https://beta.deepnote.org/launch?template=python_3.6&url=https%3A%2F%2Fgithub.com%2Fnorvig%2Fpytudes%2Fblob%2Fmaster%2Fipynb%2F{url}) '
|
||||
my = f'[my](https://mybinder.org/v2/gh/norvig/pytudes/master?filepath=ipynb%2F{url})'
|
||||
nb = f'[nb](https://nbviewer.jupyter.org/github/norvig/pytudes{urlb})'
|
||||
ti = f'<b><a href="ipynb/{url}" title="{comment}">{title}</a></b>'
|
||||
return f'| {co} {dn} {my} {nb} | {ti} |'
|
||||
|
||||
|
||||
def py(url, description, doc=''):
|
||||
"""Make a markdown table entry for a .py file."""
|
||||
if doc: doc = f'[documentation]({doc})'
|
||||
return f'|[{url}](/blob/master/py/{url})|*{description}*|{doc}|'
|
||||
|
||||
|
||||
body = f'''
|
||||
<div align="right" style="text-align: right"><i>Peter Norvig<br><a href="https://github.com/norvig/pytudes/blob/master/LICENSE">MIT License</a><br>2015-2020</i></div>
|
||||
|
||||
# pytudes
|
||||
|
||||
"An *étude* (a French word meaning *study*) is an instrumental musical composition, usually short, of considerable difficulty, and designed to provide practice material for perfecting a particular musical skill." — [Wikipedia](https://en.wikipedia.org/wiki/%C3%89tude)
|
||||
|
||||
This project contains **pytudes**—Python programs, usually short, for perfecting particular programming skills.
|
||||
Some programs are in Jupyter (`.ipynb`) notebooks, some in `.py` files. For each notebook you can:
|
||||
- Click on [co](https://colab.research.google.com) to **run** the file on Colab
|
||||
- Click on [dn](https://deepnote.com) to **run** the notebook on DeepNote
|
||||
- Click on [my](https://mybinder.org) to **run** the notebook on MyBinder
|
||||
- Click on [nb](https://nbviewer.jupyter.org/) to **view** the notebook on NBViewer
|
||||
- Click on the title to **view** the notebook on github.
|
||||
- Hover over the title to **view** a description.
|
||||
|
||||
|
||||
# Index of Jupyter (IPython) Notebooks
|
||||
|
||||
|
||||
|Run|Programming Examples|
|
||||
|---|--|
|
||||
{nb('Advent of Code 2018', 'Advent-2018.ipynb', 'Puzzle site with a coding puzzle each day for Advent 2018 ')}
|
||||
{nb('Advent of Code 2017', 'Advent%202017.ipynb', 'Puzzle site with a coding puzzle each day for Advent 2017')}
|
||||
{nb('Advent of Code 2016', 'Advent%20of%20Code.ipynb', 'Puzzle site with a coding puzzle each day for Advent 2016*')}
|
||||
{nb("Beal's Conjecture Revisited", 'Beal.ipynb', "A search for counterexamples to Beal's Conjecture")}
|
||||
{nb('Bike Speed Versus Grade', 'Bike%20Speed%20versus%20Grade.ipynb', 'How fast can I bike as the route gets steeper?')}
|
||||
{nb("Can't Stop", 'Cant-Stop.ipynb', 'Optimal play in a dice board game')}
|
||||
{nb('Chaos with Triangles', 'Sierpinski.ipynb', 'A surprising appearance of the Sierpinski triangle in a random walk between vertexes')}
|
||||
{nb("Conway's Game of Life", 'Life.ipynb', 'The cellular automata zero-player game')}
|
||||
{nb('Dice Baseball', 'Dice%20Baseball.ipynb', 'Simulating baseball games')}
|
||||
{nb('Generating Mazes', 'Maze.ipynb', 'Make a maze by generating a random tree superimposed on a grid')}
|
||||
{nb('Photo Focal Lengths', 'PhotoFocalLengths.ipynb', 'Generate charts of what focal lengths were used on a photo trip.')}
|
||||
{nb('Pickleball Tournament', 'Pickleball.ipynb', 'Scheduling a doubles tournament fairly and efficiently')}
|
||||
{nb('Project Euler Utilities', 'Project%20Euler%20Utils.ipynb', 'My utility functions for the Project Euler problems, including `Primes` and `Factors`')}
|
||||
{nb('Properly Ordered Card Hands', 'Orderable%20Cards.ipynb', 'Can you get your hand of cards into a nice order with just one move?')}
|
||||
{nb('Tracking Trump: Electoral Votes', 'Electoral%20Votes.ipynb', 'How many electoral votes would Trump get if he wins the state where he has positive net approval?')}
|
||||
{nb('Weighing Twelve Balls', 'TwelveBalls.ipynb', 'A puzzle where you are given some billiard balls and a balance scale, and asked to find the one ball that is heavier or lighter, in a limited number of weighings')}
|
||||
{nb('WWW: Who Will Win (NBA Title)?', 'WWW.ipynb', 'Computing the probability of winning the NBA title, for my home town Warriors, or any other team')}
|
||||
|
||||
|
||||
|Run | Logic and Number Puzzles|
|
||||
|--|---|
|
||||
{nb('Boggle / Inverse Boggle', 'Boggle.ipynb', 'Find all the words on a Boggle board; then find a board with a lot of words')}
|
||||
{nb('Chemical Element Spelling', 'ElementSpelling.ipynb', 'Spelling words using the chemical element symbols, like CoIn')}
|
||||
{nb('Cryptarithmetic', 'Cryptarithmetic.ipynb', 'Substitute digits for letters and make NUM + BER = PLAY')}
|
||||
{nb('Four 4s, Five 5s, and Countdown to 2016', 'Countdown.ipynb', 'Solving the equation 10 _ 9 _ 8 _ 7 _ 6 _ 5 _ 4 _ 3 _ 2 _ 1 = 2016. From an Alex Bellos puzzle')}
|
||||
{nb('The Devil and the Coin Flip Game', 'Coin%20Flip.ipynb', 'How to beat the Devil at his own game')}
|
||||
{nb('Flipping Cards: A Guessing Game', 'flipping.ipynb', 'Can you go through a deck of cards, guessing higher or lower correctly for each card?')}
|
||||
{nb('Gesture Typing', 'Gesture%20Typing.ipynb', 'What word has the longest path on a gesture-typing smartphone keyboard?')}
|
||||
{nb('Ghost', 'Ghost.ipynb', 'The word game Ghost (add letters, try to avoid making a word)')}
|
||||
{nb('How Many Soldiers Do You Need to Beat the Night King?', 'NightKing.ipynb', 'Investigasting a battle between the army of the dead and the army of the living')}
|
||||
{nb("Let's Code About Bike Locks", 'Fred%20Buns.ipynb', 'A tale of a bicycle combination lock that uses letters instead of digits. Inspired by Bike Snob NYC')}
|
||||
{nb('Pairing Socks', 'Socks.ipynb', 'What is the probability that you will be able to pair up socks as you randomly pull them out of the dryer?')}
|
||||
{nb('Portmantout Words', 'Portmantout.ipynb', 'Find a long word that squishes together a bunch of words')}
|
||||
{nb('The Puzzle of the Misanthropic Neighbors', 'Mean%20Misanthrope%20Density.ipynb', 'How crowded will this neighborhood be, if nobody wants to live next door to anyone else?')}
|
||||
{nb('Refactoring a Crossword Game Program', 'Scrabble.ipynb', 'Refactoring the Scrabble / Word with Friends game from Udacity 212')}
|
||||
{nb('Riddler: Battle Royale', 'Riddler%20Battle%20Royale.ipynb', 'A puzzle involving allocating your troops and going up against an opponent')}
|
||||
{nb('Riddler Lottery', 'RiddlerLottery.ipynb', 'Can you find what lottery number tickets these five friends picked?')}
|
||||
{nb('Riddler: Tour de 538', 'TourDe538.ipynb', 'Solve a puzzle involving the best pace for a bicycle race.')}
|
||||
{nb('Sicherman Dice', 'Sicherman%20Dice.ipynb', 'Find a pair of dice that is like a regular pair of dice, only different')}
|
||||
{nb("Sol Golomb's Rectangle Puzzle", 'Golomb-Puzzle.ipynb', 'A Puzzle involving placing rectangles of different sizes inside a square')}
|
||||
{nb('Spelling Bee', 'SpellingBee.ipynb', 'Find the highest-scoring board for the NY Times Spelling Bee puzzle')}
|
||||
{nb('Translating English Sentences into Propositional Logic Statements', 'PropositionalLogic.ipynb', 'Automatically convert informal English sentences into formal Propositional Logic')}
|
||||
{nb('How to Do Things with Words: NLP in Python', 'How%20to%20Do%20Things%20with%20Words.ipynb', 'Spelling Correction, Secret Codes, Word Segmentation, and more')}
|
||||
{nb('When Cheryl Met Eve: A Birthday Story', 'Cheryl-and-Eve.ipynb', "Inventing new puzzles in the Style of Cheryl's Birthday")}
|
||||
{nb("When is Cheryl's Birthday?", 'Cheryl.ipynb', "Solving the *Cheryl's Birthday* logic puzzle")}
|
||||
{nb("World's Longest Palindrome", 'pal3.ipynb', 'Searching for a long Panama-style palindrome, this time letter-by-letter')}
|
||||
{nb('xkcd 1313: Regex Golf (Part 2: Infinite Problems)', 'xkcd1313-part2.ipynb', 'Regex Golf: better, faster, funner. With Stefan Pochmann')}
|
||||
{nb('xkcd 1313: Regex Golf', 'xkcd1313.ipynb', 'Find the smallest regular expression; inspired by Randall Monroe')}
|
||||
{nb('xkcd 1970: Name Dominoes', 'xkcd-Name-Dominoes.ipynb', 'Lay out dominoes legally; the dominoes have people names, not numbers')}
|
||||
|
||||
|
||||
|Run|Math Concepts|
|
||||
|--|--|
|
||||
{nb('A Concrete Introduction to Probability', 'Probability.ipynb', 'Code and examples of the basic principles of Probability Theory')}
|
||||
{nb('Probability, Paradox, and the Reasonable Person Principle', 'ProbabilityParadox.ipynb', 'Some classic paradoxes in Probability Theory, and how to think about disagreements')}
|
||||
{nb('Estimating Probabilities with Simulations', 'ProbabilitySimulation.ipynb', 'When the sample space is too complex, simulations can estimate probabilities')}
|
||||
{nb('Economics Simulation', 'Economics.ipynb', 'A simulation of a simple economic game')}
|
||||
{nb("Euler's Sum of Powers Conjecture", "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>')}
|
||||
{nb('How to Count Things', 'How%20To%20Count%20Things.ipynb', 'Combinatorial math: how to count how many things there are, when there are a lot of them')}
|
||||
{nb('Symbolic Algebra, Simplification, and Differentiation', 'Differentiation.ipynb', 'A computer algebra system that manipulates expressions, including symbolic differentiation')}
|
||||
|
||||
|
||||
|Run|Computer Science Algorithms and Concepts|
|
||||
|--|--|
|
||||
{nb('Bad Grade, Good Experience', 'Snobol.ipynb', 'As a student, did you ever get a bad grade on a programming assignment? (Snobol, Concordance)')}
|
||||
{nb('BASIC Interpreter', 'BASIC.ipynb', 'How to write an interpreter for the BASIC programming language')}
|
||||
{nb('The Convex Hull Problem', 'Convex%20Hull.ipynb', 'A classic Computer Science Algorithm')}
|
||||
{nb('The Stable Matching Problem', 'StableMatching.ipynb', 'What is the best way to pair up two grpups with each other, obeying preferences?')}
|
||||
{nb('The Traveling Salesperson Problem', 'TSP.ipynb', 'Another of the classics')}
|
||||
|
||||
|
||||
# Index of Python Files
|
||||
|
||||
|
||||
| File | Description | Documentation |
|
||||
|:--------|:-------------------|----|
|
||||
{py('beal.py', "Search for counterexamples to Beal's Conjecture", 'http://norvig.com/beal.html')}
|
||||
{py('docex.py', 'A framework for running unit tests, similar to `doctest`')}
|
||||
{py('ibol.py', 'An Exercise in Species Barcoding', 'http://norvig.com/ibol.html')}
|
||||
{py('lettercount.py', 'Convert Google Ngram Counts to Letter Counts', 'http://norvig.com/mayzner.html')}
|
||||
{py('lis.py', 'Lisp Interpreter written in Python', 'http://norvig.com/lispy.html')}
|
||||
{py('lispy.py', 'Even Better Lisp Interpreter written in Python', 'http://norvig.com/lispy2.html')}
|
||||
{py('lispytest.py', 'Tests for Lisp Interpreters')}
|
||||
{py('pal.py', 'Find long palindromes', 'http://norvig.com/palindrome.html')}
|
||||
{py('pal2.py', 'Find longer palindromes', 'http://norvig.com/palindrome.html')}
|
||||
{py('pal3.py', 'Find even longer palindromes', 'http://norvig.com/palindrome.html')}
|
||||
{py('pytudes.py', 'Pre-process text to generate this README.md file.')}
|
||||
{py('py2html.py', 'Pretty-printer to format Python files as html')}
|
||||
{py('SET.py', 'Analyze the card game SET', 'http://norvig.com/SET.html')}
|
||||
{py('spell.py', 'Spelling corrector', 'http://norvig.com/spell-correct.html')}
|
||||
{py('sudoku.py', 'Program to solve sudoku puzzles', 'http://norvig.com/sudoku.html')}
|
||||
{py('testaccum.py', 'Tests for my failed Python `accumulation display` proposal', 'http://norvig.com/pyacc.html')}
|
||||
{py('yaptu.py', 'Yet Another Python Templating Utility')}
|
||||
# Etudes for Programmers
|
||||
I got the idea for the *"etudes"* part of the name from
|
||||
this [1978 book](https://books.google.com/books/about/Etudes_for_programmers.html?id=u89WAAAAMAAJ)
|
||||
by [Charles Wetherell](http://demin.ws/blog/english/2012/08/25/interview-with-charles-wetherell)
|
||||
that was very influential to me when I was first learning to program. I still have my copy.
|
||||
|
||||

|
||||
'''
|
||||
|
||||
output = 'README.md'
|
||||
with open(output, 'w') as out:
|
||||
print(f'Wrote {output}; {out.write(body)} characters')
|
2
tools.md
2
tools.md
@ -7,7 +7,7 @@
|
||||
- Deepnote
|
||||
- [Epiphany](https://epiphany.pub/) "a new blogging experience"
|
||||
- [Iodide](https://github.com/iodide-project/iodide) Run notebook in the browser
|
||||
- {Pyodide](https://github.com/iodide-project/pyodide) Run Python in the browser
|
||||
- [Pyodide](https://github.com/iodide-project/pyodide) Run Python in the browser
|
||||
- [Klipse](https://github.com/viebel/klipse) Run interactive code in tech blogs
|
||||
- Matlab / R
|
||||
- Mathematica
|
||||
|
Loading…
Reference in New Issue
Block a user