diff --git a/ipynb/Cheryl.ipynb b/ipynb/Cheryl.ipynb index a0635fc..b956f3e 100644 --- a/ipynb/Cheryl.ipynb +++ b/ipynb/Cheryl.ipynb @@ -9,20 +9,20 @@ "# 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 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", - "Let's work through this puzzle statement by statement.\n", + "Let's work through the puzzle line by line.\n", "\n", "\n", "\n", @@ -31,7 +31,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -50,17 +50,17 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ - "def Month(date): return date.split()[0]\n", - "def Day(date): return date.split()[1]" + "def month(date): return date.split()[0]\n", + "def day(date): return date.split()[1]" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -69,18 +69,18 @@ "'May'" ] }, - "execution_count": 3, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Month('May 15')" + "month('May 15')" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -89,13 +89,13 @@ "'15'" ] }, - "execution_count": 4, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Day('May 15')" + "day('May 15')" ] }, { @@ -104,25 +104,35 @@ "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 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 birthday could be either May 15, May 16, or May 19. We'll call `{'May 15', 'May 16', 'May 19'}` his **belief set**. We will say that a person **knows** the birthdate when they get down to a belief set with only one possibility. The type 2 uncertainty is that we don't know that Albert was told \"May\", so we have uncertainty about his belief set. But we do know some statements about his belief set, 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 sets. \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.\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:" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "BeliefSet = set\n", "\n", - "def tell(part, dates=dates) -> BeliefSet:\n", - " \"Cheryl tells a part of her birthdate to someone; return a belief set of possible dates.\"\n", + "def told(part: str, dates=dates) -> BeliefSet:\n", + " \"\"\"Cheryl told a part of her birthdate to someone; return a belief 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", + "def know(beliefs: BeliefSet) -> bool:\n", + " \"\"\"A person `knows` the answer if their belief set has only one possibility.\"\"\"\n", " return len(beliefs) == 1" ] }, @@ -130,12 +140,14 @@ "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:" + "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 sets: " ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -144,25 +156,18 @@ "{'May 15', 'May 16', 'May 19'}" ] }, - "execution_count": 6, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "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')" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -171,71 +176,74 @@ "{'August 15', 'May 15'}" ] }, - "execution_count": 7, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "tell('15')" + "told('15')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "With two possibilities, Bernard does not know the birthdate:" + "We can then check whether statements 3 through 5 are true in this scenario. The first part of statement 3 is Albert saying \"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\"):" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "False" + "True" ] }, - "execution_count": 8, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "know(tell('15'))" + "not know(told('May'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Overall Strategy\n", + "If the rest of statement 3, along with statements 4 and 5 also 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 takes a set of possible dates as input, and returns the subset of 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 sets 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 items (here a set of dates) and returns the subset that satisfies all the predicates:" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 28, "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 a subset of the dates for which all three statements are true.\"\"\"\n", " return satisfy(dates, statement3, statement4, statement5)\n", "\n", "def satisfy(items, *predicates) -> BeliefSet:\n", - " \"Return the subset of items that satisfy all the 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" + " if all(pred(item) for pred in predicates)}" ] }, { @@ -249,24 +257,25 @@ "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 `statement3` 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 set was such that I didn't **know** her birthday. But I can see that for **all** 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", - "execution_count": 10, + "execution_count": 29, "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))" + " \"\"\"Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too.\"\"\"\n", + " albert_dates = told(month(date))\n", + " return (not know(albert_dates) \n", + " and all(not know(told(day(d))) \n", + " for d in albert_dates))" ] }, { @@ -278,7 +287,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -287,7 +296,7 @@ "{'August 14', 'August 15', 'August 17', 'July 14', 'July 16'}" ] }, - "execution_count": 11, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -296,6 +305,35 @@ "satisfy(dates, statement3)" ] }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'May 15': {'August 15', 'May 15'},\n", + " 'May 16': {'July 16', 'May 16'},\n", + " 'May 19': {'May 19'},\n", + " 'June 17': {'August 17', 'June 17'},\n", + " 'June 18': {'June 18'},\n", + " 'July 14': {'August 14', 'July 14'},\n", + " 'July 16': {'July 16', 'May 16'},\n", + " 'August 14': {'August 14', 'July 14'},\n", + " 'August 15': {'August 15', 'May 15'},\n", + " 'August 17': {'August 17', 'June 17'}}" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{d: told(day(d)) for d in dates}" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -304,7 +342,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**. Then, out of those possible dates, I considered just the dates that **satisfy** Albert's **statement 3**, and now I **know**." ] }, { @@ -315,8 +353,9 @@ "source": [ "def statement4(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_dates = told(day(date))\n", + " after3_dates = satisfy(at_first_dates, statement3)\n", + " return not know(at_first_dates) and know(after3_dates)" ] }, { @@ -350,8 +389,18 @@ "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 5) before we know.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "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:" @@ -365,7 +414,9 @@ "source": [ "def statement5(date) -> bool:\n", " \"Albert: Then I also know when Cheryl's birthday is.\"\n", - " return know(satisfy(tell(Month(date)), statement4))" + " albert_dates = told(month(date))\n", + " after4_dates = satisfy(albert_dates, statement4)\n", + " return know(after4_dates)" ] }, { @@ -431,87 +482,44 @@ "\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/) with a very similar format:\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 0600 and 1000.\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", - "Everything works pretty much the same as with Cheryl's birthday, except we're dealing with hours and minutes of times, not days and months of dates. So it is a quick copy and edit. Note that I need to explicitly pass the second argument to `tell`, because it relied on having the default being the birthday dates, not the bus times." + "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 write 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": [], - "source": [ - "bus_times = '06:32 06:43 06:50 07:17 07:46 08:19 08:32 09:17 09:19 09:50'.split()\n", - "\n", - "def hour(time): return time[:3] # First three characters\n", - "def minutes(time): return time[-3:] # Last three characters\n", - "\n", - "def steves_bus(times) -> BeliefSet:\n", - " \"Return a subset of the times for which all three statements are true.\"\n", - " return satisfy(times, statement3b, statement4b, statement5b)\n", - "\n", - "def statement3b(time) -> bool:\n", - " \"Alice then says: I don’t know when Steve’s bus leaves but I am sure that neither does Annie\"\n", - " times = tell(hour(time), bus_times)\n", - " return (not know(times) \n", - " and all(not know(tell(minutes(d), bus_times)) for d in times))\n", - "\n", - "def statement4b(time) -> bool:\n", - " \"Annie Replies: I didn’t know his bus, but now I do\"\n", - " times = tell(minutes(time), bus_times)\n", - " return (not know(times) and know(satisfy(times, statement3b)))\n", - "\n", - "def statement5b(time) -> bool:\n", - " \"Alice responds: Now I do as well!\"\n", - " return know(satisfy(tell(hour(time), bus_times), statement3b, statement4b))" - ] - }, - { - "cell_type": "code", - "execution_count": 18, + "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'08:32'}" + "{'08 32'}" ] }, - "execution_count": 18, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "steves_bus(bus_times)" + "dates[:] = '06 32, 06 43, 06 50, 07 17, 07 46, 08 19, 08 32, 09 17, 09 19, 09 50'.split(', ')\n", + "cheryls_birthday()" ] }, { - "cell_type": "code", - "execution_count": 19, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ - "know(steves_bus(bus_times))" + "Steve took the 8:32 bus." ] } ],