Add files via upload

This commit is contained in:
Peter Norvig 2019-02-19 20:48:08 -08:00 committed by GitHub
parent ed719ca866
commit 983c698e78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,217 +1,185 @@
{
"metadata": {
"name": "",
"signature": "sha256:b9e2e6c3f7e82432833f1416dd0552c13753f683e1888682faf2910ef7d9f8ff"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When is Cheryl's Birthday?\n",
"===\n",
"<div align=right><i>Peter Norvig<br>April 2015<br>Python 3: Feb 2019</i></div>\n",
"\n",
"Peter Norvig, April 2015\n",
"\n",
"This logic puzzle has been [making the rounds](https://www.google.com/webhp?#q=cheryl%27s+birthday) (and [not always favorably](https://www.newyorker.com/cartoons/daily-cartoon/daily-cartoon-thursday-april-16th-cheryl-singapore-math)):\n",
"# When is Cheryl's Birthday?\n",
"\n",
"\n",
"1. Albert and Bernard just became friends with Cheryl, and they want to know when her birthday is. Cheryl gave them a list of 10 possible dates:\n",
"<pre>\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",
"</pre>\n",
"> 2. Cheryl then tells Albert and Bernard separately the month and the day of the birthday respectively.\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",
"2. Cheryl then tells Albert and Bernard separately the month and the day of the birthday respectively.\n",
"Let's work through this puzzle statement by statement.\n",
"\n",
"3. **Albert**: I don't know when Cheryl's birthday is, but I know that Bernard does not know too.\n",
"\n",
"4. **Bernard**: At first I don't know when Cheryl's birthday is, but I know now.\n",
"\n",
"5. **Albert**: Then I also know when Cheryl's birthday is.\n",
" \n",
"6. So when is Cheryl's birthday?\n",
"\n",
"Problem-Solving Tools\n",
"---\n",
"\n",
"Cheryl's puzzle was designed to be solved with a pencil, the greatest problem-solving tool in the history of mathematics (although some prefer a pen, chalk, marker, or a [stick for drawing in the sand](http://www.hellenicaworld.com/Greece/Science/en/Archimedes.html)). But I will show how to solve it with another tool: **computer code**. I choose this tool for four reasons: \n",
"- It is a more direct way to find the solution. All I have to do is faithfully describe the problem with code that says: *\"for each of the 10 possible dates, tell Albert the month and Bernard the day and check if statements 3 through 5 are true.\"* The intended [pencil and paper solution](https://scontent-iad.xx.fbcdn.net/hphotos-xpa1/v/t1.0-9/s720x720/11111802_983395601695416_3208022346737572922_n.jpg?oh=15fcb7edc4689dd9c71385b613446465&oe=55DD4488) requires not just understanding the *problem*, but also creatively discovering the steps of the *solution*&mdash;a harder task.\n",
"- With tested, debugged code, you're less likely to make a mistake that leads you to a [wrong answer](http://www.theguardian.com/science/alexs-adventures-in-numberland/2015/apr/15/why-the-cheryl-birthday-problem-turned-into-the-maths-version-of-thatdress).\n",
"- You'll learn how to solve problems that are similar, but can't be solved with pencil and paper because they have millions of possibilities rather than just 10.\n",
"- Solving puzzles is fun; programming is fun; solving puzzles with programs is double fun.\n",
"\n",
"We will translate each of the 6 statements in the puzzle into Python code:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1. Cheryl gave them a list of 10 possible dates:\n",
"---\n"
"## 1. Cheryl gave them a list of 10 possible dates:\n"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"DATES = ['May 15', 'May 16', 'May 19',\n",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"dates = ['May 15', 'May 16', 'May 19',\n",
" 'June 17', 'June 18',\n",
" 'July 14', 'July 16',\n",
" 'August 14', 'August 15', 'August 17']"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We'll also define accessor functions for the month and day of a date:"
"We'll define accessor functions for the month and day of a date:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def Month(date): return date.split()[0]\n",
"\n",
"def Day(date): return date.split()[1]"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 2
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"Month('May 15')"
],
"language": "python",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 3,
"text": [
"data": {
"text/plain": [
"'May'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"prompt_number": 3
"source": [
"Month('May 15')"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"Day('May 15')"
],
"language": "python",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 4,
"text": [
"data": {
"text/plain": [
"'15'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"prompt_number": 4
"source": [
"Day('May 15')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2. Cheryl then tells Albert and Bernard separately the month and the day of the birthday respectively.\n",
"---\n",
"## 2. Cheryl then tells Albert and Bernard separately the month and the day of the birthday respectively.\n",
"\n",
"We can define the idea of **telling**, and while we're at it, the idea of **knowing** a birthdate:"
"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",
"\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:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"def tell(part, possible_dates=DATES):\n",
" \"Cheryl tells a part of her birthdate to someone; return a new list of possible dates that match the part.\"\n",
" return [date for date in possible_dates if part in date]\n",
"\n",
"def know(possible_dates):\n",
" \"A person knows the birthdate if they have exactly one possible date.\"\n",
" return len(possible_dates) == 1"
],
"language": "python",
"execution_count": 5,
"metadata": {},
"outputs": [],
"prompt_number": 5
"source": [
"BeliefSet = 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",
" 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",
" return len(beliefs) == 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that we use a *list of dates* to represent someone's knowledge of the possible birthdates, and that someone *knows* the birthdate when they get down to only one possibility. For example: If Cheryl tells Albert that her birthday is in May, he would have a list of three possible birthdates:"
"For example: If Cheryl tells Albert that her birthday is in May, he would know there is a set of three possible birthdates:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'May 15', 'May 16', 'May 19'}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tell('May')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 6,
"text": [
"['May 15', 'May 16', 'May 19']"
]
}
],
"prompt_number": 6
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And if she tells Bernard that her birthday is on the 15th, he would end up with two possibilities:"
"And if she tells Bernard that her birthday is on the 15th, he would know there are two possibilities:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"tell('15')"
],
"language": "python",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 7,
"text": [
"['May 15', 'August 15']"
"data": {
"text/plain": [
"{'August 15', 'May 15'}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"prompt_number": 7
"source": [
"tell('15')"
]
},
{
"cell_type": "markdown",
@ -222,170 +190,135 @@
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"know(tell('15'))"
],
"language": "python",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 8,
"text": [
"data": {
"text/plain": [
"False"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"prompt_number": 8
"source": [
"know(tell('15'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Overall Strategy\n",
"---\n",
"## Overall Strategy\n",
"\n",
"When Cheryl tells Albert `'May'` then *he* knows there are three possibilities, but *we* (the puzzle solvers) don't, because we don't know what Cheryl said. So what can we do? We will 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 possible dates. If all goes well, there should be exactly one solution. \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",
"\n",
"Here is the main function, `cheryls_birthday`:"
"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:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"def cheryls_birthday(possible_dates=DATES):\n",
" \"Return a list of the possible dates for which statements 3 to 5 are true.\"\n",
" return filter(statements3to5, possible_dates)\n",
"\n",
"def statements3to5(date): return statement3(date) and statement4(date) and statement5(date)\n",
"\n",
"## TO DO: define statement3, statement4, statement5"
],
"language": "python",
"execution_count": 9,
"metadata": {},
"outputs": [],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" (*Python note:* `filter(predicate, items)` returns a list of all items for which `predicate(item)` is true.)\n",
"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",
"\n",
" 3. Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too.\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"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The function `statement3` takes as input a 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 in a form that is closer to Python code:\n",
"## 3. Albert: I don't know when Cheryl's birthday is, and I know that Bernard does not know."
]
},
{
"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",
"\n",
"> **Albert**: After Cheryl told me the month of her birthdate, I didn't know her birthday. I don't know which day Cheryl told Bernard, but I know that for all of the possible dates, if Bernard is told that day, he wouldn't know the birthdate.\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",
"\n",
"That I can translate directly into code:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"def statement3(date):\n",
" \"Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too.\"\n",
" possible_dates = tell(Month(date))\n",
" return (not know(possible_dates) \n",
" and all(not know(tell(Day(d)))\n",
" for d in possible_dates))"
],
"language": "python",
"execution_count": 10,
"metadata": {},
"outputs": [],
"prompt_number": 10
"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))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can try the function on a date:"
"We haven't solved the puzzle yet, but let's take a peek and see which dates satisfy statement 3:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"statement3('May 15')"
],
"language": "python",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 11,
"text": [
"False"
"data": {
"text/plain": [
"{'August 14', 'August 15', 'August 17', 'July 14', 'July 16'}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"prompt_number": 11
"source": [
"satisfy(dates, statement3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In fact, we can see all the dates that satisfy statement 3:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"filter(statement3, DATES)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 12,
"text": [
"['July 14', 'July 16', 'August 14', 'August 15', 'August 17']"
]
}
],
"prompt_number": 12
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"4. Bernard: At first I don't know when Cheryl's birthday is, but I know now.\n",
"---\n",
"## 4. Bernard: At first I don't know when Cheryl's birthday is, but I know now.\n",
"\n",
"Again, a paraphrase:\n",
"\n",
"> **Bernard:** At first Cheryl told me the day, and I didn't know. Then 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. Then, out of the possible dates, I considered just the dates for which Albert's statement 3 is true, and now I know."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"def statement4(date):\n",
" \"Bernard: At first I don't know when Cheryl's birthday is, but I know now.\"\n",
" at_first = tell(Day(date))\n",
" return (not know(at_first)\n",
" and know(filter(statement3, at_first)))"
],
"language": "python",
"execution_count": 12,
"metadata": {},
"outputs": [],
"prompt_number": 13
"source": [
"def statement4(date):\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)))"
]
},
{
"cell_type": "markdown",
@ -396,79 +329,73 @@
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"filter(statement4, filter(statement3, DATES))"
],
"language": "python",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 14,
"text": [
"['July 16', 'August 15', 'August 17']"
"data": {
"text/plain": [
"{'August 15', 'August 17', 'July 16'}"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"prompt_number": 14
"source": [
"satisfy(dates, statement3, statement4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wait a minute&mdash;I thought that Bernard **knew**?! Why are there three possible dates remaining? Bernard does indeed know the birthdate,\n",
"because he knows something we don't know: the day. We won't know the birthdate until after statement 5.\n",
"Wait a minute&mdash;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",
"\n",
"5. Albert: Then I also know when Cheryl's birthday is.\n",
"---\n",
"## 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:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"def statement5(date):\n",
" \"Albert: Then I also know when Cheryl's birthday is.\"\n",
" return know(filter(statement4, tell(Month(date))))"
],
"language": "python",
"execution_count": 14,
"metadata": {},
"outputs": [],
"prompt_number": 15
"source": [
"def statement5(date):\n",
" \"Albert: Then I also know when Cheryl's birthday is.\"\n",
" return know(satisfy(tell(Month(date)), statement4))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"6. So when is Cheryl's birthday?\n",
"---\n",
"\n",
"Let's see:"
"## 6. So when is Cheryl's birthday?\n",
"\n"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"cheryls_birthday()"
],
"language": "python",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 16,
"text": [
"['July 16']"
"data": {
"text/plain": [
"{'July 16'}"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"prompt_number": 16
"source": [
"cheryls_birthday()"
]
},
{
"cell_type": "markdown",
@ -479,26 +406,44 @@
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"know(cheryls_birthday())"
],
"language": "python",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 17,
"text": [
"data": {
"text/plain": [
"True"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"prompt_number": 17
}
],
"metadata": {}
}
"source": [
"know(cheryls_birthday())"
]
}
],
"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": 1
}