Add files via upload

This commit is contained in:
Peter Norvig 2020-04-12 14:36:12 -07:00 committed by GitHub
parent a50eb607eb
commit 80befc1acc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 911 additions and 0 deletions

272
ipynb/ElementSpelling.ipynb Normal file
View 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&mdash;\"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&mdash;violating [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)&mdash;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
}

639
ipynb/StableMatching.ipynb Normal file

File diff suppressed because one or more lines are too long