Add files via upload

This commit is contained in:
Peter Norvig authored and GitHub committed 2023-05-15 20:35:00 -07:00
1 parent 05772b7d29
commit 05110f351f
1 file changed
+465
+465
View File
@@ -0,0 +1,465 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "747a6cce-af90-465c-8276-0b0be8fb365f",
"metadata": {},
"source": [
"<div style=\"text-align:right\"><i>Peter Norvig<br>May 2023</div>\n",
" \n",
"# Wei-Hwa's Diamond-Buying Game \n",
"\n",
"Wei-Hwa Huang writes: \"*You are playing a game where you are trying to buy diamonds with coins. You can buy a random bag of diamonds for 12 coins, look in the bag, and [immediately] decide whether to sell it back for 11 coins or keep it. You can also purchase a bag for 1 coin but you **must** sell it back for 1 coin (i.e., you can basically figure out the distribution [by peeking at multiple bags for free]). What is a general strategy for maximizing the number of diamonds you keep (that is, total number of diamonds in bags not sold back)? Include analysis of end cases (e.g., if you spend your last 12 coins, it is never a good idea to sell the bag back).*\n",
"\n",
"## Strategy\n",
"\n",
"A good strategy starts by peeking at enough 1-coin bags to have a good estimate of the probability distribution of bags. The result of the peeking is a discrete probability distribution: a dict of `{number_of_diamonds: probability}`. I'm assuming that \"number of diamonds\" is always a non-negative integer. The number of peeks you take will depend on how heavy-tailed you think the distribution might be, and on how much patience you have. \n",
"\n",
"## Probability Distributions"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "a40a4728-650d-47c6-bcfa-d29fb9d188fc",
"metadata": {},
"outputs": [],
"source": [
"from typing import *\n",
"import functools\n",
"import random\n",
"import statistics\n",
"\n",
"class ProbDist(Counter):\n",
" \"\"\"A discrete probability distribution, {item: probability}; sum of probabilities is 1.0\"\"\"\n",
" def __init__(self, iterable):\n",
" super().__init__(iterable)\n",
" k = sum(self.values())\n",
" for x in self:\n",
" self[x] /= k\n",
" \n",
"def normal(mu=100, sigma=10, k=1_000) -> ProbDist: \n",
" \"\"\"Probability distribution formed from k samples from a normal distribution, \n",
" N(mu, sigma); each sample is rounded to a non-negative integer.\"\"\" \n",
" return ProbDist(max(0, round(random.gauss(mu, sigma))) \n",
" for _ in range(k))\n",
"\n",
"assert ProbDist([1, 2, 3, 1]) == {1: 0.5, 2: 0.25, 3: 0.25}"
]
},
{
"cell_type": "markdown",
"id": "3173b10c-7dc6-4ca2-aa7d-13e209547d9a",
"metadata": {},
"source": [
"## Maximizing Expected Number of Diamonds\n",
"\n",
"Given a discrete probability distribution, and assuming that our goal is to maximize the expected (average) number of diamonds, one suggestion was to establish a **cutoff point**: any bags over the cutoff would be kept; others would be sold back (except that a bag should be kept if selling it back would not result in enough coins to buy another bag). But is that the best strategy? I don't think so, no matter what the cutoff point.\n",
"\n",
"Rather than try to impose a strategy such as the cutoff-poiint strategy, it is better to go directly to the optimal strategy: the strategy that, in each situation, chooses to keep or sell back based on which action has the highest expected return, assuming the optimal strategy continues to be played.\n",
"\n",
"The function `expectation` returns a function `E` such that `E(c)` returns the expected number of diamonds you can obtain starting with `c` coins, and applying the optimal strategy."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ffec3268-7693-41b9-a1ed-f119333f2426",
"metadata": {},
"outputs": [],
"source": [
"def expectation_fn(p:ProbDist, buy=12, sell=11) -> Callable:\n",
" \"\"\"Returns a function that will compute the expected number of diamonds, given `coins`.\n",
" This is 0 if you can't afford a bag, or is the weighted average, over all possible bag values, of\n",
" the probability of the bag times the maximum of the expectations of keeping the bag or selling it back.\"\"\"\n",
" @functools.lru_cache(None)\n",
" def E(coins:int) -> float:\n",
" def keep(bag): return bag + E(coins - buy)\n",
" def sellback(bag): return E(coins - buy + sell)\n",
" if coins < buy:\n",
" return 0\n",
" else:\n",
" return sum(p[bag] * max(keep(bag), sellback(bag)) for bag in p)\n",
" return E"
]
},
{
"cell_type": "markdown",
"id": "ac96e139-6681-4c81-af6a-36ff9100072d",
"metadata": {},
"source": [
"## Examing the Results\n",
"\n",
"Here I define `E` and call `E(121)`. With 121 coins, I can buy ten bags, and return one; the expected result is a little more than 10 times 100:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "409509b3-fe9f-45f9-ad22-fc1e4b763fe0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1016.9165738912558"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"E = expectation_fn(normal(100, 10))\n",
"E(121)"
]
},
{
"cell_type": "markdown",
"id": "982b433c-b60e-48e4-a55d-315670f440e6",
"metadata": {},
"source": [
"Each run will be slightly different (by about 1%) because the random samples are different, so the probability distribution estimate is different:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b2e7f7bd-5d72-4539-bcc1-7a34d0332da4",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1018.7010335947339"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"E = expectation_fn(normal(100, 10))\n",
"E(121)"
]
},
{
"cell_type": "markdown",
"id": "b07e7e0a-9909-401a-b01b-3c49fae783ba",
"metadata": {},
"source": [
"The expected result for 12 coins is about 100; for 11 coins it is zero."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "a7fec6fd-08c4-4865-8758-12342963608c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"100.53299999999999"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"E(12)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "56845df1-5ef4-4d16-b183-fd8e2ecbbcfe",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"E(11)"
]
},
{
"cell_type": "markdown",
"id": "2ebf54e1-b1c2-4820-9094-c7a69d88846b",
"metadata": {},
"source": [
"## Visualizing the Results\n",
"\n",
"The number of diamonds should increase with the number of coins, and also with the standard deviation of the distribution. Below is a plot showing this:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "3ff6635c-170b-43b8-9591-2717d8007eb8",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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 truncated
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"def plot(sigmas=(60, 40, 25, 5), coins=range(45, 76)):\n",
" \"\"\"Plot the expected number of diamonds for different sigma/starting coins combinations.\"\"\"\n",
" for sigma in sigmas:\n",
" E = expectation_fn(normal(100, sigma))\n",
" vals = [E(c) for c in coins]\n",
" plt.plot(coins, vals, 'o:', label=f'sigma={sigma}')\n",
" plt.title('Expected number of diamonds for different starting coins and distributions')\n",
" plt.legend(); plt.xlabel('starting coins'); plt.ylabel('diamonds')\n",
"\n",
"plot()"
]
},
{
"cell_type": "markdown",
"id": "946b1ca2-3099-4991-8703-10bbd1d3daec",
"metadata": {},
"source": [
"In this plot, the lowest line, corresponding to a standard deviation of 5, has big jumps when we reach 48, 60, and 72 coins, which corresponds to being able to buy 4, 5, and 6 bags. Basically, for standard deviation 5, the strategy with, say, 48 coins is \"buy 4 bags, and keep them, no matter what.\" For distributions with larger standard deviations, the jump is less pronounced, because the strategy with 48 coins is \"if we get 4 good bags, then keep them, but if we get a bad bag at the beginning, sell it back and there is a good chance that a careful selection of 3 bags will result in more diamonds than 4 bags that include the bad one.\""
]
},
{
"cell_type": "markdown",
"id": "772006f7-ac24-4c73-958e-2251bec960b0",
"metadata": {},
"source": [
"# Visualizing the Strategy\n",
"\n",
"Now we know how many diamonds to expect from the optimal strategy, but what **is** the optimal strategy? Is it equivalent to, or similar to, the cutoff strategy, for some cutoff? I'll show scatter plots where each dot indicates a situation where the optimal strategy, given a number of coins and a bag value, is to sell the bag back; each empty space means to keep the bag."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "f8d70f31-7fd3-47f2-9d7e-642c0e80b605",
"metadata": {},
"outputs": [],
"source": [
"def decision_fn(p:ProbDist, buy=12, sell=11) -> Callable:\n",
" \"\"\"Return a function, fn(coins, bag), that tells whether to keep or sell back the bag.\"\"\"\n",
" E = expectation_fn(p, buy, sell)\n",
" return lambda coins, bag: (\n",
" 'keep' if bag + E(coins - buy) >= E(coins - buy + sell) else 'sellback')\n",
"\n",
"def scatter(p:ProbDist, coins=range(45, 76)):\n",
" \"\"\"Plot (coins, bag value) points for which the optimal action is to sell back.\"\"\"\n",
" fn = decision_fn(p)\n",
" XY = [(c, x) for c in coins for x in range(min(p), max(p) + 1) \n",
" if fn(c, x) == 'sellback']\n",
" plt.scatter(*T(XY), marker='.')\n",
" plt.xlabel('coins'); plt.ylabel('bag value'); \n",
" plt.title('Combinations of coins / bag value that should be sold back')\n",
"\n",
"def T(matrix): \"Transpose\"; return zip(*matrix)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "ea737adf-801a-4eb2-96db-e84abd46c994",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAZAAAAEWCAYAAABIVsEJAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAk8klEQVR4nO3debwkVX338c8XBoQBlW0wIMLIIkGITnBkeVQeAkYREVxixIi4L3n0pYiKxEThUYloNK6gQUQRBcQ9IdGgGNwCmhkWBTcQZwScGYZhU0TWX/4452px6aW6bvftU93f9+t1X/fe7t+tOqfqdJ+uqv7eVkRgZmY2qPXG3QAzM2snTyBmZtaIJxAzM2vEE4iZmTXiCcTMzBrxBGJmZo1M/QQi6XhJn+5x/xWS9h/Ruj8q6S2jWHYTknaVdImk30h6zRCX+1tJOw5reTXXuULSE+dznYOS9ElJ75i0dc1ab8/H1xyXHZJ27nLfCyV9t8t9i/PfLhhFu+rotz/69O0CSS8dcnsabZNiJxBJfyNpWX7yWSXpq5IeP9/tiIjdI+KCuS6n04COiFdGxNvnuuwhOga4ICIeGBEfHNZCI2LTiLh6WMubIWlDSTdI2nTYy26zXk+eDZc3sknA2q3ICUTS0cD7gX8EHgJsD5wMHDbGZk2DHYArxt2IAewHXBoRvx13Q8ymUXETiKQHA28DXhURX4yI2yLiroj4t4h4Y655gKT3S/p1/nq/pAfk+/aXdK2kYyRdn49eni7pYEk/l3SjpDfPWu1Gkj6bT91cLOnRlfb84VRIfiV2jqRP5dorJC2t1B4r6Rf5vh9Leka+fTfgo8C++Yjq5nz7fQ5jJb1M0lW5jf8qadvKfSHplZKulHSTpJMkKd+3s6RvSbolvyL/bI/te2hu9835UHi3fPs3gb8APpzb+IgOf7uFpE/kbX6TpC8P0PadK30+SdK/5+30fUk75fsk6X15v90i6YeS9ujWF+Bg4D963P/YvB9uyu3eKK9nc0nnSlqb7ztX0naV9j5c0rdz+76R29vxFbikn0g6pPL7grwP9sy/f07S6tyfb0vavcty7nfUMGu7PUDSeyT9StIapdOfG3dYTsexlm3eabvnv/uApGsk3SppuaQn5NsPAt4MPCcv77Iu7X+TpOvysn8m6cDK3Ruq+2NmtzwOb873HVq57z6najpto8p9W+Zxd6ukHwA7daqb5cV5LK+S9PrKstbTHx/L65Qe81t0We9WefzcnMf+dySt169vHZbzxtyOX0t6cY227yTpB3lcfaXavl5jTtLGkt4raWW+/7tdxtGzlJ77ej3+ICKK+gIOAu4GFvSoeRtwEbA1sAj4b+Dt+b7989+/FdgAeBmwFjgTeCCwO/B7YMdcfzxwF/BXuf4NwC+BDfL9K4AnVmp/T3riWh94J3BRpV3PBrYlTczPAW4Dtsn3vRD47qx+fBJ4R/75AOAGYE/gAcCHgG9XagM4F9iMdES2Fjgo33cW8Pd5vRsBj++y3R6R2/SXua/HAFcBG+b7LwBe2mO7/zvwWWDz/Pf/d4C271zp843AXsAC4DPA2fm+JwPLcx8F7Daz/bq056fArl3uWwFcDjwM2AL4XmVbbwk8C1iYx8TngC9X/vZC4D3AhsDjgVuBT3dZz1uBz1R+fyrw08rvL87reADpqPrSLvu/0/iobrf3A/+a+/JA4N+Ad3ZpU6dldd3u+f4j8nZZALweWA1sVBn3Hfuf798VuAbYNv++GNip32Mmj6GrSBPUhnkc/WZmnzJrPM7u16ztczZwDrAJsAdw3extUPm7xflvz8r1f0Z6PM08zo8iPb9sl/fbvwBndVnWO0kT9gb56wmksduvb9V9fxCwJrd7E9Jz1R/61mGdF+T+zdR/obp/6D3mTsp//9C8P/5PrpvZJguAF+W2d1z/fdrSr2C+v4DnAav71PwCOLjy+5OBFfnn/YHbgfXz7w/MG2bvSv1y4OmVAV6dBNYDVgFPqDwRVSeQb1RqHwnc3qOdlwKH9XlQzwyijwPvrty3KWliW1x5sDy+cv85wLH5508BpwDb9dlubwHOmdXX64D9Oz1gZ/3tNsC9wOYd7qvT9uoEcmql9mDyEy7pQfZzYB9gvT592RH4RY/7VwCvnLWejvXAEuCm/PP2pBcgCyv3f5ruE8jOpCeGhfn3zwBv7VK7Wd4WD+6w/zuNj8jLF2ni36ly377AL7usp9tY67jduyzjJuDRlXHfawLZGbgeeCL5hVflvuPp8pghPdmuru5r0pP68Z3G4+x+VbbP+nm8/Wnlvn+cvQ0q9y3Of1utfzfw8fzzT4ADZ439u+jwopb0YvYrzHqyrdG36r4/DTixUvcI+k8g1fpHAneSn/O6jTnS4/32mf3aZZu8AfgxfZ5LZr6KO4UFrAO2Uu93A2wLrKz8vjLf9odlRMQ9+efb8/c1lftvJz3Jzbhm5oeIuBe4dtbyqlZXfv4d6fTXAgBJR0q6NB+y3kx6hbBVj35U3adPkc7rryO9Uui27pk+HEN6kvlBPlTudgg8ex33kvr+0C71VQ8DboyImxq2vapjPyLim8CHSa+S1kg6RdKDuizjqfQ+fQWV/UpljEhaKOlf8mH8rcC3gc0krZ9rboyI33VZzn1ExFWkJ5ynSVoIHEp6BYmk9SWdmE+F3Eqa1KD+mJixiHS0tLwytr6Wbx9Et/GDpNcrnY67JS//wXXbmbfBUaTJ4npJZ6tyCrPDemceM9sC1+RxOGMl9cZj1SLSK+fZ+7ufjuODdC3wS5Vt/RPgHtL12Nn+ifRq/TxJV0s6Nt8+SN+2HULbNyA9b/Yac1uRzlD8osdy3wicFBHX1mhDkRPIhaRD3qf3qPk1aSfP2D7f1tTDZn7I5y+3G3R5knYAPga8GtgyIjYjnUJRLok+i7hPnyRtQjqlcF2/dUfE6oh4WURsC7wCOFmd3wI4ex0i9b3vOkgDdgtJmw2z7bNFxAcj4jGkU42PIA3oTg4mnVLr5WGVn6tj5PWk0y57R8SDSBfjIe2rVaR+LuyynE7OAp5LepPHj/MTKsDf5NueSHpCXlxZz2y3kSaJVCD9SeW+G0gvenaPiM3y14Mjotu7z/qNtfvI1zveBPw16QhzM+AW6o9dIuLMiHg8aRwE8K4aq/418LCZawbZ9vxx3NxnmwDVbVK1lnTUOHt/99NtfFwDPKWyrTeLiI0i4n7jOSJ+ExGvj4gdgacBR+frP/36VrVqCG2/izROeo25G0jPrb2uDz0J+AdJz6rRhvImkIi4hXRe+SSli98LJW0g6SmS3p3LziJ1cpGkrXL9XN5m+BhJz8yvio4C7iCdAx3EJqQHzloASS8iHYHMWANsJ2nDLn9/JvAiSUuU3hDwj8D3I2JFvxVLerb+eBH4ptyOezqUngM8VdKBkjYgPZHeQbqG1FNErAK+SpqcNs/7ZOaJt3HbZ/XjsZL2zm27jTTY79ePfNFvL9KhfC+vkrRdvsD4ZtL1G0inNW8Hbs73HVfp50pgGXC80tuE9yU9MfRyNumB97fko4/Keu4gHY0tJG2Xbi4Dds/bcCPSq/mZNt1LenHyPklbA0h6qKQnd1lWv7E22wNJT8BrgQWS3gpUj/zWAItnPRn+gVJ+6IC8739P2radxt9s3yft52PyeNqftK3PzvdfCjwzPwfsDLyk00Ly2YYvkvbZQkmPBF5QY/1vyfW7k877z4yPjwIn5BeF5OeZwzotQNIhSm9iEela2T35q1/fqs4BXijpkfmFy3EdamY7olL/NuDzeTt0HXN5HJ0G/LOkbfPRyr55v824gnRN5iT1uOg/o7gJBCAi/hk4GvgH0qC+hvTK/su55B2kB/kPgR8BF+fbmvoK6aL3TcDzgWdGxF0DtvnHwHtJR1BrSBfmvlcp+SZp56yWdEOHvz+fdI3iC6RXJDsBh9dc/WOB70v6LelC62sj4pcd1vEz0sXSD5FejTwNeFpE3FlzPc8nvdL5Kemc91FDaHvVg0hPlDeRDsvXkS5mz3YgcGFE/L7P8s4EzgOuzl8zY+T9wMakbXAR6XRQ1fNI1xjW5b/5LOlB2VGeXC8kXZCsvgPuU7kf15HOK3d9URIRPyc9EXwDuBKY/W6jN5FOlVyUT018g3QU1UnPsdbBf5JeHPw8t/f33PcUyefy93WSLu7w9w8ATiRtz9WkN7fMfqfj/eRxdyjwlPy3JwNHRsRPc8n7SOf21wCnk64vdfNq0im51aTrC5/ot37gW6Rtej7wnog4L9/+AdLj6DxJvyHtt727LGMX0r74LWkMnBwRF9To2x9ExFdJY/KbuT3frNH2M0j9XE06LTUT/O035t5Aes78H9KbKt7FrHkgIi4DDgE+JukpvRqhfAHFrDUknQxcHhEnz9P6Pku64FznlaHZ1CjyCMSsj0uBL41q4flU2k5KeYCDSOeUvzyq9Zm11dj+F4xZUxFxyohX8Sekc+pbkt6R97cRccmI12nWOj6FZWZmjfgUlpmZNdLqU1hbbbVVLF68eNzNMDNrleXLl98QEYMGUe+n1RPI4sWLWbZs2bibYWbWKpLqpN378iksMzNrxBOImZk14gnEzMwa8QRiZmaNeAIxM7NGPIGYmVkjUzuBLF95Eyf911UsX9np85FGV2dmNilanQNpavnKm3jeqRdx5933suGC9fjMS/fhMTtsPvI6M7NJMpVHIBddvY47776XewPuuvteLrp63bzUmZlNkqmcQPbZcUs2XLAe6ws2WLAe++y45bzUmZlNklb/N96lS5dG039lsnzlTVx09Tr22XHLnqebhl1nZjZukpZHxNI5L2daJxAzs2k1rAlkKk9hmZnZ3HkCMTOzRjyBmJlZI55AzMyskamdQJxEb69x7jvv5/byY3n4nER3Er1VxrnvvJ/by4/l0ZjKIxAn0dtrnPvO+7m9/FgejamcQJxEb69x7jvv5/byY3k0pjZI6CR6e41z33k/t5cfy3/kJDpLine truncated
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"scatter(normal(100, 5))"
]
},
{
"cell_type": "markdown",
"id": "c45ef1a0-6748-46a0-a135-5617d2bcf687",
"metadata": {},
"source": [
"This shows, for example, that with 48 coins there is no bag value that should be sold back; it is always better to get 4 bags than to sell one back and be left with only 3 bags. But with each extra coin, we can afford to be more picky, and the bag value that we sell back increases, until we get up to 60 coins, where again the best azction is to keep all 5 bags.\n",
"\n",
"However, with a larger standard deviation we get a different scatter plot (there are so many dots they run together to form lines, but that's not important):"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "50b2ab97-4323-44e7-a5dd-d6bf730cc45f",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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 truncated
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"scatter(normal(100, 25))"
]
},
{
"cell_type": "markdown",
"id": "7ee02075-06c2-4c7c-a515-3f18d8ed89d4",
"metadata": {},
"source": [
"Let's investigate this more carefully:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "3086e247-dce3-48ee-9819-ea8526a1289b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{78: 'sellback',\n",
" 79: 'sellback',\n",
" 80: 'sellback',\n",
" 81: 'sellback',\n",
" 82: 'sellback',\n",
" 83: 'keep',\n",
" 84: 'keep',\n",
" 85: 'keep',\n",
" 86: 'keep',\n",
" 87: 'keep'}"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"p = normal(100, 25)\n",
"E = expectation_fn(p)\n",
"f = decision_fn(p)\n",
"\n",
"{bag: f(48, bag) for bag in range(78, 88)}"
]
},
{
"cell_type": "markdown",
"id": "f1281db2-373d-4f3e-9406-6f60f751e650",
"metadata": {},
"source": [
"This says that with 48 coins we should sell back a bag of value of 82 or less, even though selling back means we can only get 3 bags instead of 4. The 3 bags will, on average, make up for it, because we can sell back bad ones 11 times. (The exact point at which 'sellback' switches to 'keep' will vary, depending on the samples in the distribution.)\n",
"\n",
"Here we compare the expected values of keeping versus selling back a bag of value 78:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "db3cd7f9-993e-47a0-bddc-6aff66b8e343",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"380.0048248123251"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"78 + E(36) # What if we kept the bag of value 78, and got 3 other bags (about 100 each) for the 36 other coins?"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "3af8869b-75c0-4fe5-8e46-d780822d1919",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"384.36968315108277"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"E(47) # What if we sold back the 78-diamond bag, and selected 3 good bags with the remaining 47 coins?"
]
},
{
"cell_type": "markdown",
"id": "5c6d203f-19d8-4640-84d6-460635ac8367",
"metadata": {},
"source": [
"We see that selling back is better."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.9.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}