Add files via upload
This commit is contained in:
1 parent
5e0ecb0766
commit
3e4c4e5b87
1 file changed
+158
-185
+158
-185
@@ -18,12 +18,14 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The [June 15, 2015 post on Bike Snob NYC](http://bikesnobnyc.blogspot.com/2015/06/lets-get-this-show-on-road-once-we-all.html) leads with \"*Let's talk about bike locks.*\" Here's what I want to talk about: in my local bike shop, I saw a combination lock called *WordLock®*,\n",
|
||||
"The [June 15, 2015 post](http://bikesnobnyc.blogspot.com/2015/06/lets-get-this-show-on-road-once-we-all.html) on *Bike Snob NYC* leads with \"*Let's talk about bike locks.*\" Here's what I want to talk about: in my local bike shop, I saw a combination lock called *WordLock®*,\n",
|
||||
"which replaces digits with letters. I classified this as a Fred lock,\n",
|
||||
"\"[Fred](http://bikesnobnyc.blogspot.com/2014/06/a-fred-too-far.html)\" being the term for an amateurish cyclist with the wrong equipment.\n",
|
||||
"\"[Fred](http://bikesnobnyc.blogspot.com/2014/06/a-fred-too-far.html)\" being the term for an amateurish cyclist with inappropriate equipment.\n",
|
||||
"I tried the combination \"FRED,\" and was amused with the result:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"<p><center><img src=\"http://norvig.com/ipython/fredbuns.jpg\"></center>\n",
|
||||
"<p>\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"FRED BUNS! Naturally I set all the other locks on the rack to FRED BUNS as well. But my curiosity was raised ... \n",
|
||||
"\n",
|
||||
@@ -36,14 +38,14 @@
|
||||
"Could different letters make words in every horizontal line?\n",
|
||||
"5. Is it a coincidence that the phrase \"FRED BUNS\" appears, or was it planted there by mischievous WordLock® designers? \n",
|
||||
"\n",
|
||||
"Vocabulary\n",
|
||||
"===\n",
|
||||
"# Vocabulary\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Before we can answer the questions, we'll need to be clear about the vocabulary of the problem and how to represent concepts in code:\n",
|
||||
"\n",
|
||||
"* **Lock**: For our purposes a lock can be modeled as a `list` of 4 **tumblers**. \n",
|
||||
"* **Tumbler:** Each tumbler has 10 distinct letters. I will represent a tumbler as a `str` of 10 letters.\n",
|
||||
"* **Combination**: Choosing a letter from each tumbler gives a combination, such as \"FRED\" or \"BUNS\". There are 10<sup>4</sup> = 10,000 combinations.\n",
|
||||
"* **Combination**: Choosing a letter from each tumbler gives a combination, such as \"FRED\" or \"BUNS\". There are 4<sup>10</sup> = 10,000 combinations.\n",
|
||||
"* **Word**: Some combinations (such as \"BUNS\") are *words*; others (such as \"SOMN\") are not words. We'll need a collection of dictionary words.\n",
|
||||
"\n",
|
||||
"Now on to the code! First the imports I will need and the vocabulary concepts:"
|
||||
@@ -133,7 +135,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"I would prefer to deal with the string `'BUNS'` rather than the tuple `('B', 'U', 'N', 'S')`, so I will define a function, `combinations`, that takes a lock as input and returns a list of strings representing the combinations:"
|
||||
"I would prefer to deal with the string `'BUNS'` rather than the tuple `('B', 'U', 'N', 'S')`, so I will define a function, `combinations`, that takes a lock as input and returns a set of strings representing the combinations:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -146,7 +148,7 @@
|
||||
"source": [
|
||||
"def combinations(lock):\n",
|
||||
" \"Return a list of all combinations that can be made by this lock.\"\n",
|
||||
" return [Word(combo) for combo in itertools.product(*lock)]"
|
||||
" return {Word(combo) for combo in itertools.product(*lock)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -159,22 +161,22 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['FRED',\n",
|
||||
" 'FRES',\n",
|
||||
" 'FRND',\n",
|
||||
" 'FRNS',\n",
|
||||
" 'FUED',\n",
|
||||
" 'FUES',\n",
|
||||
" 'FUND',\n",
|
||||
" 'FUNS',\n",
|
||||
" 'BRED',\n",
|
||||
"{'BRED',\n",
|
||||
" 'BRES',\n",
|
||||
" 'BRND',\n",
|
||||
" 'BRNS',\n",
|
||||
" 'BUED',\n",
|
||||
" 'BUES',\n",
|
||||
" 'BUND',\n",
|
||||
" 'BUNS']"
|
||||
" 'BUNS',\n",
|
||||
" 'FRED',\n",
|
||||
" 'FRES',\n",
|
||||
" 'FRND',\n",
|
||||
" 'FRNS',\n",
|
||||
" 'FUED',\n",
|
||||
" 'FUES',\n",
|
||||
" 'FUND',\n",
|
||||
" 'FUNS'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
@@ -286,7 +288,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"So that means that no lock could ever make more than 4,360 words. Let's define `words_from(lock)`:"
|
||||
"So that means that no lock could ever make more than 4,360 words. Let's define `words_from(lock)` to be the intersection of the dictionary words and the words that can be made with a lock:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -299,7 +301,7 @@
|
||||
"source": [
|
||||
"def words_from(lock): \n",
|
||||
" \"A list of words that can be made by lock.\"\n",
|
||||
" return [c for c in combinations(lock) if c in WORDS]"
|
||||
" return WORDS & combinations(lock)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -312,7 +314,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['FRED', 'FUND', 'FUNS', 'BRED', 'BUND', 'BUNS']"
|
||||
"{'BRED', 'BUND', 'BUNS', 'FRED', 'FUND', 'FUNS'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
@@ -328,8 +330,6 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"*Note*: An alternative is to represent a collection of words as a `set`; then `words_from` could do `return combinations(lock) & WORDS`. \n",
|
||||
"\n",
|
||||
"I will also introduce the function `show` to print out a lock and its words:"
|
||||
]
|
||||
},
|
||||
@@ -442,7 +442,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Random Locks"
|
||||
"# Baseline: Random Locks"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -494,7 +494,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Wow, that's not very many words. Let's repeat 100 times and take the best one, with \"best\" determined by the function `word_count`:"
|
||||
"Wow, that's not very many words. Let's repeat 100 times and take the best one, with \"best\" meaning the maximum `word_count`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -542,7 +542,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Question 2: More Words (via Greedy Locks)"
|
||||
"# Question 2: More Words (via Greedy Lock)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -563,8 +563,8 @@
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[('S', 373),\n",
|
||||
" ('T', 268),\n",
|
||||
" ('P', 268),\n",
|
||||
" ('T', 268),\n",
|
||||
" ('B', 267),\n",
|
||||
" ('D', 251),\n",
|
||||
" ('C', 248),\n",
|
||||
@@ -641,7 +641,7 @@
|
||||
" # Then update words to only include the ones that can be made with this tumbler\n",
|
||||
" counter = Counter(word[i] for word in words)\n",
|
||||
" tumbler = Tumbler(L for (L, _) in counter.most_common(c))\n",
|
||||
" words = [w for w in words if w[i] in tumbler]\n",
|
||||
" words = {w for w in words if w[i] in tumbler}\n",
|
||||
" lock.append(tumbler)\n",
|
||||
" return lock"
|
||||
]
|
||||
@@ -657,7 +657,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Lock: STPBDCLMAR OAIEURLHYN RNALEOTISM SETADNLKPY\n",
|
||||
"Lock: SPTBDCLMAR OAIEURLHYN RNALEOTISM SETADNLKPY\n",
|
||||
"Count: 1177\n",
|
||||
"Words: AALS AEON AERY AHED AHIS AHOY AILA AILS AIMS AINS AIRN AIRS AIRT AIRY AITS ALAE ALAN ALAS ALEA ALEE ALEK ALES ALIA ALIT ALLS ALLY ALMA ALME ALMS ALOE ALTS ANAL ANAS ANAY ANES ANIL ANIS ANNA ANNE ANOA ANON ANSA ANTA ANTE ANTS ARAK AREA ARES ARIA ARID ARIE ARIL ARMS ARMY ARON ARSE ARTS ARTY AULD AUNT AURA AYAN AYES AYIN AYLA BAAL BAAS BAIL BAIT BALD BALE BALK BALL BALS BAMS BAND BANE BANK BANS BARD BARE BARK BARN BARS BASE BASK BASS BAST BATE BATS BATT BEAD BEAK BEAN BEAT BEEN BEEP BEES BEET BELL BELS BELT BEMA BEND BENE BENS BENT BERK BEST BETA BETS BIAS BILE BILK BILL BIMA BIND BINE BINS BINT BIOS BIRD BIRK BIRL BISE BISK BITE BITS BITT BLAE BLAT BLED BLET BLIN BLIP BLOT BOAS BOAT BOIL BOLA BOLD BOLE BOLL BOLT BOND BONE BONK BONY BOOK BOON BOOS BOOT BORA BORE BORK BORN BORT BOSK BOSS BOTA BOTS BOTT BRAD BRAE BRAN BRAS BRAT BRAY BREA BRED BREE BREN BRIA BRIE BRIN BRIS BRIT BROS BULK BULL BUMP BUMS BUNA BUND BUNK BUNN BUNS BUNT BUOY BURA BURD BURL BURN BURP BURS BURY BUSK BUSS BUST BUSY BUTE BUTS BUTT BYES BYRE BYRL BYTE CAEL CAID CAIN CALE CALK CALL CAME CAMP CAMS CANE CANS CANT CARA CARD CARE CARK CARL CARN CARP CARS CART CASA CASE CASK CAST CATE CATS CEES CEIL CELL CELS CELT CENT CERE CESS CETE CHAD CHAP CHAT CHAY CHIA CHID CHIN CHIP CHIS CHIT CHON CHOP CIAN CINE CION CIRE CIST CITE CITY CLAD CLAN CLAP CLAY CLIP CLOD CLON CLOP CLOT CLOY COAL COAT COED COEN COIL COIN COLA COLD COLE COLS COLT COLY COMA COME COMP CONE CONK CONN CONS CONY COOK COOL COON COOP COOS COOT CORA CORD CORE CORK CORN CORS CORY COSS COST COSY COTE COTS CRAP CRED CRIS CRIT CROP CUED CUES CULL CULT CUNT CURD CURE CURL CURN CURS CURT CUSK CUSP CUSS CUTE CUTS CYAN CYMA CYME CYST DAIS DALE DALS DAME DAMN DAMP DAMS DANA DANE DANK DANS DARE DARK DARN DART DATA DATE DEAD DEAL DEAN DEED DEEP DEES DEET DEIL DELE DELL DELS DELT DEME DEMY DENE DENS DENT DENY DEON DERE DESK DHAK DHAL DIAL DIED DIEL DIES DIET DILL DIME DIMS DINA DINE DINK DINS DINT DIOL DION DIRE DIRK DIRL DIRT DISK DISS DITA DITE DITS DOAT DOES DOIT DOLE DOLL DOLS DOLT DOME DOMS DONA DONE DONS DORE DORK DORP DORS DORY DOSE DOSS DOST DOTE DOTS DOTY DRAT DRAY DREE DREK DRIP DROP DUAD DUAL DUEL DUES DUET DUIT DULL DULY DUMA DUMP DUNE DUNK DUNS DUNT DUOS DURA DURE DURN DUSK DUST DUTY DYAD DYED DYES DYNE LAID LAIN LALL LAMA LAME LAMP LAMS LANA LAND LANE LANK LARA LARD LARK LARS LASE LASS LAST LATE LATS LEAD LEAK LEAL LEAN LEAP LEAS LEEK LEES LEET LEIA LEIS LELA LENA LEND LENS LENT LEON LESS LEST LETS LIAN LIED LIEN LIES LILA LILT LILY LIMA LIME LIMN LIMP LIMY LINA LINE LINK LINN LINS LINT LINY LION LIRA LIRE LISA LISP LIST LITE LITS LOAD LOAN LOID LOIN LOLA LOLL LONE LOOK LOON LOOP LOOS LOOT LORD LORE LORN LORY LOSE LOSS LOST LOTA LOTS LUES LUIS LULL LUMA LUMP LUMS LUNA LUNE LUNK LUNT LUNY LURE LURK LUST LUTE LYES LYLA LYLE LYRA LYRE LYSE MAES MAIA MAID MAIL MAIN MALE MALL MALT MAMA MANA MANE MANS MANY MARA MARE MARK MARL MARS MART MARY MASA MASK MASS MAST MATE MATS MATT MEAD MEAL MEAN MEAT MEED MEEK MEET MELD MELL MELS MELT MEME MEMS MEND MERE MERK MERL MESA MESS META METE MHOS MIEN MILA MILD MILE MILK MILL MILS MILT MIME MINA MIND MINE MINK MINT MIRA MIRE MIRK MIRS MIRY MISE MISS MIST MITE MITT MITY MOAN MOAS MOAT MOIL MOLA MOLD MOLE MOLL MOLS MOLT MOLY MOME MOMS MONA MONK MONS MONY MOOD MOOL MOON MOOS MOOT MORA MORE MORN MORS MORT MOSK MOSS MOST MOTE MOTS MOTT MULE MULL MUMP MUMS MUNS MUON MURA MURE MURK MUSA MUSE MUSK MUSS MUST MUTE MUTS MUTT MYLA MYNA MYRA PAID PAIK PAIL PAIN PALE PALL PALP PALS PALY PAMS PANE PANS PANT PARA PARD PARE PARK PARS PART PASE PASS PAST PATE PATS PATY PEAK PEAL PEAN PEAS PEAT PEED PEEK PEEL PEEN PEEP PEES PEIN PELE PELT PEND PENS PENT PEON PERE PERK PERP PERT PEST PETS PHAT PHIS PHON PHOT PIAL PIAN PIAS PIED PIES PILE PILL PILY PIMA PIMP PINA PINE PINK PINS PINT PINY PION PIRN PISS PITA PITS PITY PLAN PLAT PLAY PLEA PLED PLIE PLOD PLOP PLOT PLOY POET POIS POLE POLL POLS POLY POME POMP POMS POND PONE PONS PONY POOD POOL POON POOP POOS PORE PORK PORN PORT POSE POST POSY POTS PRAT PRAY PREE PREP PREY PROA PROD PROP PROS PULA PULE PULL PULP PULS PUMA PUMP PUNA PUNK PUNS PUNT PUNY PURE PURL PURS PUSS PUTS PUTT PYAS PYES PYIN PYRE RAIA RAID RAIL RAIN RAIS RALE RAMP RAMS RAND RANK RANT RARE RASE RASP RATE RATS READ REAL REAP REED REEK REEL REES REID REIN REIS RELY REMS REMY REND RENE RENT REST RETE RETS RHEA RHOS RIAL RIAN RIAS RIEL RILE RILL RIME RIMS RIMY RIND RINK RINS RIOT RISE RISK RITA RITE ROAD ROAN ROES ROIL ROLE ROLL ROME ROMP ROMS ROOD ROOK ROOT RORY ROSA ROSE ROSS ROSY ROTA ROTE ROTL ROTS RUED RUES RUIN RULE RULY RUMP RUMS RUNE RUNS RUNT RUSE RUSK RUST RUTS RYAN RYAS RYES RYND RYOT SAAD SAID SAIL SAIN SALE SALL SALP SALS SALT SAME SAMP SANA SAND SANE SANK SANS SARA SARD SARK SASS SATE SEAL SEAN SEAS SEAT SEED SEEK SEEL SEEN SEEP SEES SEIS SELL SELS SEME SEND SENE SENT SERA SERE SERS SETA SETS SETT SHAD SHAE SHAT SHAY SHEA SHED SHES SHIN SHIP SHIT SHOD SHOE SHOP SHOT SIAL SILD SILK SILL SILT SIMA SIMP SIMS SINE SINK SINS SIRE SIRS SITE Line truncated
|
||||
]
|
||||
@@ -742,7 +742,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Lock: STPBDCLMWF OAIEURLHYP RNALEOTISC SETADNLKHY\n",
|
||||
"Lock: SPTBDCLMWF OAIEURLHYP RNALEOTISC SETADNLKHY\n",
|
||||
"Count: 1240\n",
|
||||
"Words: BAAL BAAS BACH BACK BAIL BAIT BALD BALE BALK BALL BALS BAND BANE BANK BANS BARD BARE BARK BARN BARS BASE BASH BASK BASS BAST BATE BATH BATS BATT BEAD BEAK BEAN BEAT BECK BEEN BEES BEET BELL BELS BELT BEND BENE BENS BENT BERK BEST BETA BETH BETS BIAS BICE BILE BILK BILL BIND BINE BINS BINT BIOS BIRD BIRK BIRL BISE BISK BITE BITS BITT BLAE BLAH BLAT BLED BLET BLIN BLOT BOAS BOAT BOCK BOIL BOLA BOLD BOLE BOLL BOLT BOND BONE BONK BONY BOOK BOON BOOS BOOT BORA BORE BORK BORN BORT BOSH BOSK BOSS BOTA BOTH BOTS BOTT BRAD BRAE BRAN BRAS BRAT BRAY BREA BRED BREE BREN BRIA BRIE BRIN BRIS BRIT BROS BUCK BULK BULL BUNA BUND BUNK BUNN BUNS BUNT BUOY BURA BURD BURL BURN BURS BURY BUSH BUSK BUSS BUST BUSY BUTE BUTS BUTT BYES BYRE BYRL BYTE CACA CAEL CAID CAIN CALE CALK CALL CANE CANS CANT CARA CARD CARE CARK CARL CARN CARS CART CASA CASE CASH CASK CAST CATE CATS CECA CEES CEIL CELL CELS CELT CENT CERE CESS CETE CHAD CHAT CHAY CHIA CHID CHIN CHIS CHIT CHON CIAN CINE CION CIRE CIST CITE CITY CLAD CLAN CLAY CLOD CLON CLOT CLOY COAL COAT COCA COCK COED COEN COIL COIN COLA COLD COLE COLS COLT COLY CONE CONK CONN CONS CONY COOK COOL COON COOS COOT CORA CORD CORE CORK CORN CORS CORY COSH COSS COST COSY COTE COTS CRED CRIS CRIT CUED CUES CULL CULT CUNT CURD CURE CURL CURN CURS CURT CUSK CUSS CUTE CUTS CYAN CYST DACE DAIS DALE DALS DANA DANE DANK DANS DARE DARK DARN DART DASH DATA DATE DEAD DEAL DEAN DECK DEED DEES DEET DEIL DELE DELL DELS DELT DENE DENS DENT DENY DEON DERE DESK DHAK DHAL DIAL DICE DICK DIED DIEL DIES DIET DILL DINA DINE DINK DINS DINT DIOL DION DIRE DIRK DIRL DIRT DISH DISK DISS DITA DITE DITS DOAT DOCK DOCS DOES DOIT DOLE DOLL DOLS DOLT DONA DONE DONS DORE DORK DORS DORY DOSE DOSS DOST DOTE DOTH DOTS DOTY DRAT DRAY DREE DREK DUAD DUAL DUCE DUCK DUCT DUEL DUES DUET DUIT DULL DULY DUNE DUNK DUNS DUNT DUOS DURA DURE DURN DUSK DUST DUTY DYAD DYED DYES DYNE FACE FACT FAIL FAIN FALL FANE FANS FARD FARE FARL FART FASH FAST FATE FATS FEAL FEAT FECK FEED FEEL FEES FEET FELL FELT FEND FENS FEOD FERE FERN FESS FEST FETA FETE FETS FIAT FICE FILA FILE FILL FILS FIND FINE FINK FINN FINS FIRE FIRN FIRS FISH FIST FITS FLAK FLAN FLAT FLAY FLEA FLED FLEE FLEY FLIT FLOE FOAL FOES FOIL FOIN FOLD FOLK FOND FONS FONT FOOD FOOL FOOT FORA FORD FORE FORK FORT FOSS FRAE FRAT FRAY FRED FREE FRET FRIT FROE FUCK FUEL FULL FUND FUNK FUNS FURL FURS FURY FUSE FUSS FYCE LACE LACK LACS LACY LAID LAIN LALL LANA LAND LANE LANK LARA LARD LARK LARS LASE LASH LASS LAST LATE LATH LATS LEAD LEAH LEAK LEAL LEAN LEAS LECH LEEK LEES LEET LEIA LEIS LELA LENA LEND LENS LENT LEON LESS LEST LETS LIAN LICE LICH LICK LIED LIEN LIES LILA LILT LILY LINA LINE LINK LINN LINS LINT LINY LION LIRA LIRE LISA LIST LITE LITS LOAD LOAN LOCA LOCH LOCK LOID LOIN LOLA LOLL LONE LOOK LOON LOOS LOOT LORD LORE LORN LORY LOSE LOSS LOST LOTA LOTH LOTS LUCA LUCE LUCK LUCY LUES LUIS LULL LUNA LUNE LUNK LUNT LUNY LURE LURK LUSH LUST LUTE LYCH LYES LYLA LYLE LYRA LYRE LYSE MACE MACH MACK MACS MACY MAES MAIA MAID MAIL MAIN MALE MALL MALT MANA MANE MANS MANY MARA MARE MARK MARL MARS MART MARY MASA MASH MASK MASS MAST MATE MATH MATS MATT MEAD MEAL MEAN MEAT MEED MEEK MEET MELD MELL MELS MELT MEND MERE MERK MERL MESA MESH MESS META METE METH MHOS MIAH MICA MICE MICK MICS MIEN MILA MILD MILE MILK MILL MILS MILT MINA MIND MINE MINK MINT MIRA MIRE MIRK MIRS MIRY MISE MISS MIST MITE MITT MITY MOAN MOAS MOAT MOCK MOCS MOIL MOLA MOLD MOLE MOLL MOLS MOLT MOLY MONA MONK MONS MONY MOOD MOOL MOON MOOS MOOT MORA MORE MORN MORS MORT MOSH MOSK MOSS MOST MOTE MOTH MOTS MOTT MUCH MUCK MULE MULL MUNS MUON MURA MURE MURK MUSA MUSE MUSH MUSK MUSS MUST MUTE MUTS MUTT MYAH MYCS MYLA MYNA MYRA MYTH PACA PACE PACK PACS PACT PACY PAID PAIK PAIL PAIN PALE PALL PALS PALY PANE PANS PANT PARA PARD PARE PARK PARS PART PASE PASH PASS PAST PATE PATH PATS PATY PEAK PEAL PEAN PEAS PEAT PECH PECK PECS PEED PEEK PEEL PEEN PEES PEIN PELE PELT PEND PENS PENT PEON PERE PERK PERT PEST PETS PHAT PHIS PHON PHOT PIAL PIAN PIAS PICA PICE PICK PICS PIED PIES PILE PILL PILY PINA PINE PINK PINS PINT PINY PION PIRN PISH PISS PITA PITH PITS PITY PLAN PLAT PLAY PLEA PLED PLIE PLOD PLOT PLOY POCK POET POIS POLE POLL POLS POLY POND PONE PONS PONY POOD POOH POOL POON POOS PORE PORK PORN PORT POSE POSH POST POSY POTS PRAT PRAY PREE PREY PROA PROD PROS PUCE PUCK PULA PULE PULL PULS PUNA PUNK PUNS PUNT PUNY PURE PURL PURS PUSH PUSS PUTS PUTT PYAS PYES PYIN PYRE SAAD SACK SACS SAID SAIL SAIN SALE SALL SALS SALT SANA SAND SANE SANK SANS SARA SARD SARK SASH SASS SATE SEAL SEAN SEAS SEAT SECS SECT SEED SEEK SEEL SEEN SEES SEIS SELL SELS SEND SENE SENT SERA SERE SERS SETA SETH SETS SETT SHAD SHAE SHAH SHAT SHAY SHEA SHED SHES SHIN SHIT SHOD SHOE SHOT SIAL SICE SICK SICS SILD SILK SILL SILT SINE SINH SINK SINS SIRE SIRS SITE SITH SITS SLAT SLAY SLED SLID SLIT SLOE SLOT SOAK SOCA SOCK SOIL SOLA SOLD SOLE SOLS SONE SONS SOOK SOON SOOT SORA SORD SORE SORN SORT SOTH SOTS SPAE SPAN SPAS SPAT SPAY SPED SPIK SPIN SPIT SPOT SPRY SRIS SUCH Line truncated
|
||||
]
|
||||
@@ -756,7 +756,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"How about starting from a random lock? "
|
||||
"We got up to 1240 words, an improvement of 5%, but can we go beyond that? I'll improve 50 random locks (this will take around 7 minutes):"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -770,41 +770,13 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Lock: TMSLBPFWCD AEHYLORIWU CNATELISRO LDSYANKETH\n",
|
||||
"Count: 1240\n",
|
||||
"Words: BAAL BAAS BACH BACK BAIL BAIT BALD BALE BALK BALL BALS BAND BANE BANK BANS BARD BARE BARK BARN BARS BASE BASH BASK BASS BAST BATE BATH BATS BATT BEAD BEAK BEAN BEAT BECK BEEN BEES BEET BELL BELS BELT BEND BENE BENS BENT BERK BEST BETA BETH BETS BIAS BICE BILE BILK BILL BIND BINE BINS BINT BIOS BIRD BIRK BIRL BISE BISK BITE BITS BITT BLAE BLAH BLAT BLED BLET BLIN BLOT BOAS BOAT BOCK BOIL BOLA BOLD BOLE BOLL BOLT BOND BONE BONK BONY BOOK BOON BOOS BOOT BORA BORE BORK BORN BORT BOSH BOSK BOSS BOTA BOTH BOTS BOTT BRAD BRAE BRAN BRAS BRAT BRAY BREA BRED BREE BREN BRIA BRIE BRIN BRIS BRIT BROS BUCK BULK BULL BUNA BUND BUNK BUNN BUNS BUNT BUOY BURA BURD BURL BURN BURS BURY BUSH BUSK BUSS BUST BUSY BUTE BUTS BUTT BYES BYRE BYRL BYTE CACA CAEL CAID CAIN CALE CALK CALL CANE CANS CANT CARA CARD CARE CARK CARL CARN CARS CART CASA CASE CASH CASK CAST CATE CATS CECA CEES CEIL CELL CELS CELT CENT CERE CESS CETE CHAD CHAT CHAY CHIA CHID CHIN CHIS CHIT CHON CIAN CINE CION CIRE CIST CITE CITY CLAD CLAN CLAY CLOD CLON CLOT CLOY COAL COAT COCA COCK COED COEN COIL COIN COLA COLD COLE COLS COLT COLY CONE CONK CONN CONS CONY COOK COOL COON COOS COOT CORA CORD CORE CORK CORN CORS CORY COSH COSS COST COSY COTE COTS CRED CRIS CRIT CUED CUES CULL CULT CUNT CURD CURE CURL CURN CURS CURT CUSK CUSS CUTE CUTS CYAN CYST DACE DAIS DALE DALS DANA DANE DANK DANS DARE DARK DARN DART DASH DATA DATE DEAD DEAL DEAN DECK DEED DEES DEET DEIL DELE DELL DELS DELT DENE DENS DENT DENY DEON DERE DESK DHAK DHAL DIAL DICE DICK DIED DIEL DIES DIET DILL DINA DINE DINK DINS DINT DIOL DION DIRE DIRK DIRL DIRT DISH DISK DISS DITA DITE DITS DOAT DOCK DOCS DOES DOIT DOLE DOLL DOLS DOLT DONA DONE DONS DORE DORK DORS DORY DOSE DOSS DOST DOTE DOTH DOTS DOTY DRAT DRAY DREE DREK DUAD DUAL DUCE DUCK DUCT DUEL DUES DUET DUIT DULL DULY DUNE DUNK DUNS DUNT DUOS DURA DURE DURN DUSK DUST DUTY DYAD DYED DYES DYNE FACE FACT FAIL FAIN FALL FANE FANS FARD FARE FARL FART FASH FAST FATE FATS FEAL FEAT FECK FEED FEEL FEES FEET FELL FELT FEND FENS FEOD FERE FERN FESS FEST FETA FETE FETS FIAT FICE FILA FILE FILL FILS FIND FINE FINK FINN FINS FIRE FIRN FIRS FISH FIST FITS FLAK FLAN FLAT FLAY FLEA FLED FLEE FLEY FLIT FLOE FOAL FOES FOIL FOIN FOLD FOLK FOND FONS FONT FOOD FOOL FOOT FORA FORD FORE FORK FORT FOSS FRAE FRAT FRAY FRED FREE FRET FRIT FROE FUCK FUEL FULL FUND FUNK FUNS FURL FURS FURY FUSE FUSS FYCE LACE LACK LACS LACY LAID LAIN LALL LANA LAND LANE LANK LARA LARD LARK LARS LASE LASH LASS LAST LATE LATH LATS LEAD LEAH LEAK LEAL LEAN LEAS LECH LEEK LEES LEET LEIA LEIS LELA LENA LEND LENS LENT LEON LESS LEST LETS LIAN LICE LICH LICK LIED LIEN LIES LILA LILT LILY LINA LINE LINK LINN LINS LINT LINY LION LIRA LIRE LISA LIST LITE LITS LOAD LOAN LOCA LOCH LOCK LOID LOIN LOLA LOLL LONE LOOK LOON LOOS LOOT LORD LORE LORN LORY LOSE LOSS LOST LOTA LOTH LOTS LUCA LUCE LUCK LUCY LUES LUIS LULL LUNA LUNE LUNK LUNT LUNY LURE LURK LUSH LUST LUTE LYCH LYES LYLA LYLE LYRA LYRE LYSE MACE MACH MACK MACS MACY MAES MAIA MAID MAIL MAIN MALE MALL MALT MANA MANE MANS MANY MARA MARE MARK MARL MARS MART MARY MASA MASH MASK MASS MAST MATE MATH MATS MATT MEAD MEAL MEAN MEAT MEED MEEK MEET MELD MELL MELS MELT MEND MERE MERK MERL MESA MESH MESS META METE METH MHOS MIAH MICA MICE MICK MICS MIEN MILA MILD MILE MILK MILL MILS MILT MINA MIND MINE MINK MINT MIRA MIRE MIRK MIRS MIRY MISE MISS MIST MITE MITT MITY MOAN MOAS MOAT MOCK MOCS MOIL MOLA MOLD MOLE MOLL MOLS MOLT MOLY MONA MONK MONS MONY MOOD MOOL MOON MOOS MOOT MORA MORE MORN MORS MORT MOSH MOSK MOSS MOST MOTE MOTH MOTS MOTT MUCH MUCK MULE MULL MUNS MUON MURA MURE MURK MUSA MUSE MUSH MUSK MUSS MUST MUTE MUTS MUTT MYAH MYCS MYLA MYNA MYRA MYTH PACA PACE PACK PACS PACT PACY PAID PAIK PAIL PAIN PALE PALL PALS PALY PANE PANS PANT PARA PARD PARE PARK PARS PART PASE PASH PASS PAST PATE PATH PATS PATY PEAK PEAL PEAN PEAS PEAT PECH PECK PECS PEED PEEK PEEL PEEN PEES PEIN PELE PELT PEND PENS PENT PEON PERE PERK PERT PEST PETS PHAT PHIS PHON PHOT PIAL PIAN PIAS PICA PICE PICK PICS PIED PIES PILE PILL PILY PINA PINE PINK PINS PINT PINY PION PIRN PISH PISS PITA PITH PITS PITY PLAN PLAT PLAY PLEA PLED PLIE PLOD PLOT PLOY POCK POET POIS POLE POLL POLS POLY POND PONE PONS PONY POOD POOH POOL POON POOS PORE PORK PORN PORT POSE POSH POST POSY POTS PRAT PRAY PREE PREY PROA PROD PROS PUCE PUCK PULA PULE PULL PULS PUNA PUNK PUNS PUNT PUNY PURE PURL PURS PUSH PUSS PUTS PUTT PYAS PYES PYIN PYRE SAAD SACK SACS SAID SAIL SAIN SALE SALL SALS SALT SANA SAND SANE SANK SANS SARA SARD SARK SASH SASS SATE SEAL SEAN SEAS SEAT SECS SECT SEED SEEK SEEL SEEN SEES SEIS SELL SELS SEND SENE SENT SERA SERE SERS SETA SETH SETS SETT SHAD SHAE SHAH SHAT SHAY SHEA SHED SHES SHIN SHIT SHOD SHOE SHOT SIAL SICE SICK SICS SILD SILK SILL SILT SINE SINH SINK SINS SIRE SIRS SITE SITH SITS SLAT SLAY SLED SLID SLIT SLOE SLOT SOAK SOCA SOCK SOIL SOLA SOLD SOLE SOLS SONE SONS SOOK SOON SOOT SORA SORD SORE SORN SORT SOTH SOTS SRIS SUCH SUCK SUED SUES SUET SUIT SULK SUNK SUNN SUNS SURA SURD Line truncated
|
||||
"CPU times: user 6min 50s, sys: 1.44 s, total: 6min 51s\n",
|
||||
"Wall time: 6min 53s\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"show(improved_lock(random_lock()))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We got up to 1240 words, but can we go beyond? I'll improve 50 random locks, taking 6000 steps each rather than just 3000 (this will take around 15 minutes):"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"CPU times: user 15min 48s, sys: 9.03 s, total: 15min 57s\n",
|
||||
"Wall time: 19min 40s\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%time improved_locks = [improved_lock(random_lock(seed=i), 6000) for i in range(50)]"
|
||||
"%time improved_locks = [improved_lock(random_lock(seed=i)) for i in range(50)]"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -816,7 +788,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 28,
|
||||
"execution_count": 27,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -824,10 +796,10 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Counter({1232: 3, 1235: 5, 1240: 42})"
|
||||
"Counter({1232: 6, 1235: 5, 1237: 8, 1240: 31})"
|
||||
]
|
||||
},
|
||||
"execution_count": 28,
|
||||
"execution_count": 27,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -840,13 +812,13 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"So 42/50 locks got a score of 1240.\n",
|
||||
"My first reaction to this was that if I discovered 42 different locks with score 1240, then probably there is at least one undiscovered lock with a score above 1240. But a discussion with [Matt Chisholm](https://blog.glyphobet.net/faq) changed my thinking. The key is to realize that some locks that look different are actually the same; they just have the letters in a different order. I define the function `alock` to put each tumbler into alphabetical order (and make the lock be a tuple rather than a list, so that it can be an entry in a `dict` or `set`):"
|
||||
"So 31/50 locks got a score of 1240.\n",
|
||||
"My first reaction to this was that if I discovered 31 different locks with score 1240, then probably there is at least one undiscovered lock with a score above 1240. But a discussion with [Matt Chisholm](https://blog.glyphobet.net/faq) changed my thinking. The key is to realize that some locks that look different are actually the same; they just have the letters in a different order. I define the function `alock` to put each tumbler into alphabetical order (and make the lock be a tuple rather than a list, so that it can be an entry in a `dict` or `set`):"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 29,
|
||||
"execution_count": 28,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
@@ -866,7 +838,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"execution_count": 47,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -877,10 +849,15 @@
|
||||
"{('BCDFHLMPST', 'AEHILORUWY', 'ACELMNORST', 'ADEKLNPSTY'): 1232,\n",
|
||||
" ('BCDFLMPSTW', 'AEHILOPRUY', 'ACEILNORST', 'ADEHKLNSTY'): 1240,\n",
|
||||
" ('BCDFLMPSTW', 'AEHILORUWY', 'ACEILNORST', 'ADEHKLNSTY'): 1240,\n",
|
||||
" ('BCDGLMPSTW', 'AEHILORUWY', 'AEILMNORST', 'ADEKLNPSTY'): 1235}"
|
||||
" ('BCDGLMPSTW', 'AEHILORUWY', 'AEILMNORST', 'ADEKLNPSTY'): 1235,\n",
|
||||
" ('BCDHLMPSTW', 'AEHILOPRUY', 'ACEILNPRST', 'ADEHKLNSTY'): 1232,\n",
|
||||
" ('BCDHLMPSTW', 'AEHILORUWY', 'ACEILNORST', 'ADEHKLNSTY'): 1237,\n",
|
||||
" ('BCDHLMPSTW', 'AEHILORUWY', 'ACEILNPRST', 'ADEHKLNSTY'): 1232,\n",
|
||||
" ('BCDLMPRSTW', 'AEHILOPRUY', 'ACEILNPRST', 'ADEHKLNSTY'): 1232,\n",
|
||||
" ('BCDLMPRSTW', 'AEHILORUWY', 'ACEILNORST', 'ADEHKLNSTY'): 1237}"
|
||||
]
|
||||
},
|
||||
"execution_count": 30,
|
||||
"execution_count": 47,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -898,12 +875,12 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"So out of the 50 `improved_locks` there are actually only 4 distinct ones. And only two have a score of 1240:"
|
||||
"So out of the 50 `improved_locks` there are actually only 9 distinct ones. And only two have a score of 1240:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"execution_count": 48,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -915,14 +892,13 @@
|
||||
" ('BCDFLMPSTW', 'AEHILORUWY', 'ACEILNORST', 'ADEHKLNSTY')}"
|
||||
]
|
||||
},
|
||||
"execution_count": 31,
|
||||
"execution_count": 48,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"peaks = {alock(lock) for lock in improved_locks if word_count(lock) == 1240}\n",
|
||||
"peaks"
|
||||
"{L for L in _ if word_count(L) == 1240}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -931,48 +907,14 @@
|
||||
"source": [
|
||||
"These two differ in just one letter (a `P` or a `W` in the second tumbler). \n",
|
||||
"\n",
|
||||
"This discovery changes my whole thinking about the space of scores for locks. Previously I imagined a spiky \"porcupine-shaped\" landscape, with 42 different peaks hitting a height of 1240. But now I have a different picture of the landscape: a single peak containing the two locks (one with a `P` and one with a `W`), surrounded by rolling hills. To verify this picture, I'll look at all the neighbors of the two peak locks, and see which ones are at least 1240:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 45,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{('BCDFLMPSTW', 'AEHILOPRUY', 'ACEILNORST', 'ADEHKLNSTY'): 1240,\n",
|
||||
" ('BCDFLMPSTW', 'AEHILORUWY', 'ACEILNORST', 'ADEHKLNSTY'): 1240}"
|
||||
]
|
||||
},
|
||||
"execution_count": 45,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"alphabetset = set(alphabet)\n",
|
||||
"\n",
|
||||
"def neighborhood(lock): \n",
|
||||
" \"Yield all locks resulting from a change of one letter in one tumbler.\"\n",
|
||||
" for i in range(len(lock)):\n",
|
||||
" for new in alphabetset - set(lock[i]):\n",
|
||||
" for old in lock[i]:\n",
|
||||
" lock2 = Lock(lock)\n",
|
||||
" lock2[i] = lock2[i].replace(old, new)\n",
|
||||
" yield lock2\n",
|
||||
" \n",
|
||||
"unique_locks(lock for peak in peaks for lock in neighborhood(peak) if word_count(lock) >= 1240)"
|
||||
"This discovery changes my whole thinking about the space of scores for locks. Previously I imagined a spiky \"porcupine-shaped\" landscape, with 31 different peaks hitting a height of 1240 (and probably other peaks that we haven't discovered yet). But now I have a different picture of the landscape: a single peak containing the two locks (one with a `P` and one with a `W`), surrounded by rolling hills."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Nothing new. This doesn't prove there is no lock with score over 1240, but it does mean we have looked hard to find one and failed.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Question 3: Simultaneous Words"
|
||||
]
|
||||
@@ -981,14 +923,14 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Can we make a lock that spells 10 words simultaneously? One possible approach would be to start with any lock and randomly change it (just as we did with `improved_lock`), but measure improvements by the number of words formed. My intuition is that this approach would work, eventually, but that progress would be very slow, because most random changes to a letter would not make a word.\n",
|
||||
"Can we make a lock that spells 10 words simultaneously? One possible approach would be to start with any lock and randomly change it (just as we did with `improved_lock`), but measuring improvements by the number of horizontal words formed. My intuition is that this approach would work, eventually, but that progress would be slow, because most random changes to a letter would not make a word.\n",
|
||||
"\n",
|
||||
"An alternative approach is to think of the lock not as a list of 4 vertical tumblers (each with 10 letters), but rather as a list of 10 horizontal words (each with 4 letters). I'll call this the *word list* representation, and note that a lock and a word list are *[matrix transposes](http://en.wikipedia.org/wiki/Transpose)* of each other—they swap rows for columns. There is an [old trick](https://books.google.com/books?id=eH6jBQAAQBAJ&pg=PA574&lpg=PA574&dq=lisp+transpose+matrix&source=bl&ots=Yixwj8m3k4&sig=KoeuJnFhRnJsiD06_Cx56rUOetQ&hl=en&sa=X&ved=0CB4Q6AEwAGoVChMIyM-WiriLxgIVD6OICh2QcwBK#v=onepage&q=transpose%20matrix&f=false) to compute the transpose of a matrix `M` with the expression `zip(*M)`. But `zip` returns tuples; we want strings, so we can define `transpose` as:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 33,
|
||||
"execution_count": 49,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1006,7 +948,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"execution_count": 50,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1026,7 +968,7 @@
|
||||
" 'BIKE']"
|
||||
]
|
||||
},
|
||||
"execution_count": 34,
|
||||
"execution_count": 50,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -1058,7 +1000,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 35,
|
||||
"execution_count": 51,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1103,7 +1045,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 36,
|
||||
"execution_count": 52,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1111,19 +1053,19 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['KNUR',\n",
|
||||
" 'FLAG',\n",
|
||||
" 'APES',\n",
|
||||
" 'EVIL',\n",
|
||||
" 'COZY',\n",
|
||||
" 'TICK',\n",
|
||||
" 'PHON',\n",
|
||||
" 'LATU',\n",
|
||||
" 'DUPE',\n",
|
||||
" 'REFT']"
|
||||
"['RAID',\n",
|
||||
" 'ANSH',\n",
|
||||
" 'OYER',\n",
|
||||
" 'UPON',\n",
|
||||
" 'KUDU',\n",
|
||||
" 'GLUM',\n",
|
||||
" 'JIMP',\n",
|
||||
" 'IFFY',\n",
|
||||
" 'NEVE',\n",
|
||||
" 'FOAL']"
|
||||
]
|
||||
},
|
||||
"execution_count": 36,
|
||||
"execution_count": 52,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -1136,12 +1078,12 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"That was easy! Can we go to 11?"
|
||||
"That was easy! Can we [go to 11](https://www.youtube.com/watch?v=4xgx4k83zzc)?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 37,
|
||||
"execution_count": 53,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1149,20 +1091,20 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['BRAS',\n",
|
||||
" 'EMIR',\n",
|
||||
" 'NIPA',\n",
|
||||
" 'PESO',\n",
|
||||
" 'ONYX',\n",
|
||||
" 'WYNN',\n",
|
||||
" 'CUKE',\n",
|
||||
" 'MOLY',\n",
|
||||
" 'FLOC',\n",
|
||||
" 'THEM',\n",
|
||||
" 'HARL']"
|
||||
"['AMAH',\n",
|
||||
" 'HARM',\n",
|
||||
" 'UPDO',\n",
|
||||
" 'LIVE',\n",
|
||||
" 'GWEN',\n",
|
||||
" 'FELT',\n",
|
||||
" 'MUFF',\n",
|
||||
" 'ROTI',\n",
|
||||
" 'SCUP',\n",
|
||||
" 'EDGY',\n",
|
||||
" 'BROS']"
|
||||
]
|
||||
},
|
||||
"execution_count": 37,
|
||||
"execution_count": 53,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -1199,7 +1141,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 38,
|
||||
"execution_count": 54,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1227,7 +1169,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 39,
|
||||
"execution_count": 55,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1246,7 +1188,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 40,
|
||||
"execution_count": 56,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1267,7 +1209,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 41,
|
||||
"execution_count": 57,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1275,19 +1217,19 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['UMAR',\n",
|
||||
" 'DISH',\n",
|
||||
" 'TEMP',\n",
|
||||
" 'JUDE',\n",
|
||||
" 'WORM',\n",
|
||||
" 'STOW',\n",
|
||||
" 'BHUT',\n",
|
||||
" 'YAGI',\n",
|
||||
" 'ACTA',\n",
|
||||
" 'GYPS']"
|
||||
"['TUBA',\n",
|
||||
" 'PROF',\n",
|
||||
" 'AXAL',\n",
|
||||
" 'SCUD',\n",
|
||||
" 'YAWN',\n",
|
||||
" 'FLEY',\n",
|
||||
" 'EGGS',\n",
|
||||
" 'BILK',\n",
|
||||
" 'CHIT',\n",
|
||||
" 'LEHR']"
|
||||
]
|
||||
},
|
||||
"execution_count": 41,
|
||||
"execution_count": 57,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -1310,29 +1252,18 @@
|
||||
"There is still one unanswered question: did the designers of WordLock® deliberately put \"FRED BUNS\" in, or was it a coincidence? Hacker News reader [emhart](https://news.ycombinator.com/user?id=emhart) (aka the competitive lockpicker Schyler Towne) astutely commented that he had found the [patent](https://www.google.com/patents/US6621405) assigned to WordLock; it describes an algorithm similar to my `greedy_lock`.\n",
|
||||
"After seeing that, I'm inclined to believe that \"FRED BUNS\" is the coincidental result of running the algorithm. On\n",
|
||||
"the other hand, there is a [followup patent](https://www.google.com/patents/US20080053167) that discusses a refinement\n",
|
||||
"\"wherein the letters on the wheels are configured to spell a first word displayed on a first row of letters and a second word displayed on a second row of letters.\" So the possibility of a two-word phrase was somthing that Wordlock LLc. was aware of.\n",
|
||||
"\"wherein the letters on the wheels are configured to spell a first word displayed on a first row of letters and a second word displayed on a second row of letters.\" So the possibility of a two-word phrase was something that Wordlock LLc. was aware of.\n",
|
||||
"\n",
|
||||
"We see below that the procedure described in the [patent](https://www.google.com/patents/US6621405) is not quite as good as `greedy_lock`, because the patent states that at each tumbler position \"*the entire word list is scanned*\" to produce the letter frequencies, whereas `greedy_lock` scans only the words that are consistent with the previous tumblers. Because of that difference, `greedy_lock` produces more words, 1177 to 1161:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 42,
|
||||
"execution_count": 58,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(1177, 1161)"
|
||||
]
|
||||
},
|
||||
"execution_count": 42,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def greedy_lock_patented(t=4, c=10, words=WORDS):\n",
|
||||
" \"Make a lock with t tumblers, each consisting of the c letters that cover the most words at that position.\"\n",
|
||||
@@ -1342,11 +1273,53 @@
|
||||
" # Then update words to only include the ones that can be made with this tumbler\n",
|
||||
" counter = Counter(word[i] for word in words)\n",
|
||||
" tumbler = Tumbler(L for (L, _) in counter.most_common(c))\n",
|
||||
" # words = [w for w in words if w[i] in tumbler] # <<<< The patent does not update the word list\n",
|
||||
" # words = {w for w in words if w[i] in tumbler} # <<<< The patent does not update the word list\n",
|
||||
" lock.append(tumbler)\n",
|
||||
" return lock\n",
|
||||
"\n",
|
||||
"word_count(greedy_lock()), word_count(greedy_lock_patented())"
|
||||
" return lock"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 59,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1177"
|
||||
]
|
||||
},
|
||||
"execution_count": 59,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"word_count(greedy_lock())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 60,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1161"
|
||||
]
|
||||
},
|
||||
"execution_count": 60,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"word_count(greedy_lock_patented())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1366,7 +1339,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 43,
|
||||
"execution_count": 61,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1377,7 +1350,7 @@
|
||||
"'pass'"
|
||||
]
|
||||
},
|
||||
"execution_count": 43,
|
||||
"execution_count": 61,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -1394,9 +1367,9 @@
|
||||
" assert len(WORDS) == 4360\n",
|
||||
" \n",
|
||||
" assert fredbuns == ['FB', 'RU', 'EN', 'DS']\n",
|
||||
" assert combinations(fredbuns) == ['FRED','FRES','FRND','FRNS','FUED','FUES','FUND','FUNS',\n",
|
||||
" 'BRED','BRES','BRND','BRNS','BUED','BUES','BUND','BUNS']\n",
|
||||
" assert words_from(fredbuns) == ['FRED', 'FUND', 'FUNS', 'BRED', 'BUND', 'BUNS']\n",
|
||||
" assert combinations(fredbuns) == {'FRED','FRES','FRND','FRNS','FUED','FUES','FUND','FUNS',\n",
|
||||
" 'BRED','BRES','BRND','BRNS','BUED','BUES','BUND','BUNS'}\n",
|
||||
" assert words_from(fredbuns) == {'FRED', 'FUND', 'FUNS', 'BRED', 'BUND', 'BUNS'}\n",
|
||||
" assert \"FRED\" in words_from(wordlock) and \"BUNS\" in words_from(wordlock)\n",
|
||||
"\n",
|
||||
" assert wordlock == ['SPHMTWDLFB', 'LEYHNRUOAI', 'ENMLRTAOSK', 'DSNMPYLKTE']\n",
|
||||
@@ -1427,7 +1400,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 48,
|
||||
"execution_count": 62,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1438,7 +1411,7 @@
|
||||
"{7, 15, 23, 28, 31, 39, 47, 55, 60, 63, 71, 79, 87, 92, 95}"
|
||||
]
|
||||
},
|
||||
"execution_count": 48,
|
||||
"execution_count": 62,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -1447,7 +1420,7 @@
|
||||
"# Numbers up to 100 that are not the sum of 3 squares\n",
|
||||
"nums = range(11)\n",
|
||||
"sums = {A**2 + B**2 + C**2 for A in nums for B in nums for C in nums}\n",
|
||||
"set(range(max(nums)**2)) - sums"
|
||||
"set(range(101)) - sums"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -1467,7 +1440,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.5.1"
|
||||
"version": "3.6.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
Reference in new issue
Block a user