{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Gesture Typing \n",
    "===\n",
    "\n",
    "Typing quickly and accurately on a smartphone screen is hard! One invention to make it easier is **gesture typing**, in which your finger can trace a **path** consisting of letter-to-letter **segments**. When you lift your finger the path (and the word) is complete. Below we see the path for the word \"hello.\" Note that the path is imprecise; it didn't quite hit the \"L\", but the word was recognized anyways, because \"Hello\" is a known word, whereas \"Hekko\", \"Hwerklo\", etc., are not.\n",
    "\n",
    "<img src=\"http://norvig.com/gesture.png\">\n",
    "\n",
    "Questions About Gesture Typing\n",
    "===\n",
    "\n",
    "My colleague Nicolas Schank examined (and answered) the question of what word has the longest path length. I mentioned this to [Shumin Zhai](http://www.shuminzhai.com/), the pioneer of gesture typing, and between the three of us we expanded the list of questions:\n",
    "\n",
    " 1. What words have the longest path length?\n",
    " 2. What words have the highest ratio of path length to word length? \n",
    " 3. What is the average segment length, over a typical typing work load?\n",
    " 3. Is there a better keyboard layout to minimize the average segment length over a work load?\n",
    " 4. How often are two words confused because they have similar paths?\n",
    " 5. Is there a better keyboard layout to minimize confusion? \n",
    " 6. Is there a better keyboard layout to maximize overall user satisfaction?\n",
    "\n",
    "Let's look at each of these questions, but first, let's get a rough idea for of the concepts we will need to model.\n",
    "\n",
    "Vocabulary\n",
    "===\n",
    "\n",
    "We will need to talk about the following concepts:\n",
    "\n",
    "* **Keyboard**: We'll need to know the **location** of each letter on the keyboard (we consider only letters, not the other symbols).\n",
    "* **Location**: A location is a **point** in two-dimensional space (we assume keyboards are flat).\n",
    "* **Path**: A path connects the letters in a word. In the picture above the path is curved, but a shortest path is formed by connecting straight line **segments**, so maybe we need only deal with straight lines.\n",
    "* **Segment**: A line segment is a straight line between two points.\n",
    "* **Words**: We will need a list of allowable words (in order to find the one with the longest path).\n",
    "* **Work Load**: If we want to find the average path length over a typical work load, we'll have to represent a work load: not\n",
    "just a list of words, but a measure of how frequent each word is.\n",
    "* **Confusion**: We need some measure of *whether* (or perhaps *to what degree*) the paths for two words can be confused with each other.\n",
    "* **Satisfaction**: This is a very difficult concept to define.  A user will be more satisfied with a keyboard if it allows for fast, accurate typing, and if it gives the user a feeling of mastery, not frustration.\n",
    "\n",
    "**Note**: Before we get started writing any real code, I've taken all the imports I will need throughout this notebook and gathered them together here:\n",
    "\n",
    "\n",
    "             "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Populating the interactive namespace from numpy and matplotlib\n"
     ]
    }
   ],
   "source": [
    "%pylab --no-import-all inline\n",
    "from __future__ import division\n",
    "from collections import Counter\n",
    "import matplotlib.pyplot as plt\n",
    "import urllib\n",
    "import itertools\n",
    "import random \n",
    "import re"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Representing Keyboards and Points\n",
    "===\n",
    "\n",
    "The representation for a keyboard needs to describe the location of each of the letters. Using the principle of *\"Do the simplest thing that could possibly work,\"* I represent a keyboard as a `dict` of `{letter: point}` pairs, where there will be 26 letters, A-Z,\n",
    "and each point will mark the x-y coordinates of the center of the corresponding key.  In a standard keyboard the letters are not all on a strict rectangular grid; the **A** key is half way between the **Q** and **W** in the horizontal direction. I would like to have a programmer-friendly way of defining keyboard layouts.  For example, a programmer should be able to write:\n",
    "\n",
    "    Keyboard(('Q W E R T Y U I O P',\n",
    "              ' A S D F G H J K L ',\n",
    "              '   Z X C V B N M   '))\n",
    "              \n",
    "and this would be equivalent to the `dict`:\n",
    "\n",
    "    {'Q': Point(0, 0),   'W': Point(1, 0), ...\n",
    "     'A': Point(0.5, 1), 'S': Point(1.5, 1), ...\n",
    "     'Z': Point(1.5, 2), 'X': Point(2.5, 2), ...}\n",
    "              \n",
    "Note that one key width is two characters in the input to `Keyboard`.  Here is the implementation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def Keyboard(rows):\n",
    "    \"A keyboard is a {letter: location} map, e.g. {'Q':Point(0, 0), 'A': Point(0.5, 1)}.\"\n",
    "    return {ch: Point(x/2, y) \n",
    "            for (y, row) in enumerate(rows)\n",
    "            for (x, ch) in enumerate(row)\n",
    "            if ch != ' '}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What about `Point`? At first glance, Python does not appear to have a two-dimensional point as a builtin data type, but\n",
    "on second thought, it does: `complex`. A complex number is a point in the two-dimensional complex plane;\n",
    "we can use that to model the two-dimensional (x, y) plane. Because complex numbers are built in, manipulating them will be efficient. A bonus is that the distance between points `A` and `B` is simply  `abs(A-B)`; easier than the usual formula involving squares and a square root. Thus, the simplest possible thing I could do to represent points is\n",
    "\n",
    "<pre>\n",
    "Point = complex\n",
    "</pre>\n",
    "\n",
    "That would work fine. However, I would like to refer to the x coordinate of point `p` as `p.x` rather than `p.real`, and I would like points to display nicely, so I will do the second-simplest thing: make `Point` be a subclass of `complex` with `x` and `y` properties and a  `__repr__` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class Point(complex):\n",
    "    \"A point in the (x, y) plane.\"\n",
    "    def __repr__(self): return 'Point({}, {})'.format(self.x, self.y)\n",
    "    x = property(lambda p: p.real)\n",
    "    y = property(lambda p: p.imag)\n",
    "    \n",
    "def distance(A, B):\n",
    "    \"The distance between two points.\"\n",
    "    return abs(A - B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Design notes for Point:** Alternative representations for points include an `(x, y)` tuple, or a NumPy two-element array, or a namedtuple. \n",
    "\n",
    "**Design notes for Keyboard:** Alternatives for `Keyboard` include a subclass of `dict`, or a class that contains a `dict`.  \n",
    "I considered support for keyboards\n",
    "in which one row is, say, offset 1/3 of a key rather than 1/2 key from the row above. In the end I decided to keep the function `Keyboard` as simple as possible; you can always define a function, `FancyKeyboard`, if you want more.\n",
    "\n",
    "Now we can check that `Keyboard` works:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'A': Point(0.5, 1.0),\n",
       " 'B': Point(5.5, 2.0),\n",
       " 'C': Point(3.5, 2.0),\n",
       " 'D': Point(2.5, 1.0),\n",
       " 'E': Point(2.0, 0.0),\n",
       " 'F': Point(3.5, 1.0),\n",
       " 'G': Point(4.5, 1.0),\n",
       " 'H': Point(5.5, 1.0),\n",
       " 'I': Point(7.0, 0.0),\n",
       " 'J': Point(6.5, 1.0),\n",
       " 'K': Point(7.5, 1.0),\n",
       " 'L': Point(8.5, 1.0),\n",
       " 'M': Point(7.5, 2.0),\n",
       " 'N': Point(6.5, 2.0),\n",
       " 'O': Point(8.0, 0.0),\n",
       " 'P': Point(9.0, 0.0),\n",
       " 'Q': Point(0.0, 0.0),\n",
       " 'R': Point(3.0, 0.0),\n",
       " 'S': Point(1.5, 1.0),\n",
       " 'T': Point(4.0, 0.0),\n",
       " 'U': Point(6.0, 0.0),\n",
       " 'V': Point(4.5, 2.0),\n",
       " 'W': Point(1.0, 0.0),\n",
       " 'X': Point(2.5, 2.0),\n",
       " 'Y': Point(5.0, 0.0),\n",
       " 'Z': Point(1.5, 2.0)}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "qwerty = Keyboard(('Q W E R T Y U I O P',\n",
    "                   ' A S D F G H J K L ',\n",
    "                   '   Z X C V B N M   '))\n",
    "qwerty"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Computing Path Length\n",
    "===\n",
    "\n",
    "Now let's figure out the path length for a word: the sum of the lengths of segments between letters.  So the path length for `'WORD'` would be the sum of the segment lengths for `'WO'` plus `'OR'` plus `'RD'`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "13.118033988749895"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "W, O, R, D = qwerty['W'], qwerty['O'], qwerty['R'], qwerty['D'], \n",
    "distance(W, O) + distance(O, R) + distance(R, D)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's make a function to compute this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def path_length(word, kbd=qwerty):\n",
    "    \"The total path length for a word on this keyboard: the sum of the segment lengths.\"\n",
    "    return sum(distance(kbd[word[i]], kbd[word[i+1]])\n",
    "               for i in range(len(word)-1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "13.118033988749895"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "path_length('WORD')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's check with a simpler example that we know the answer to:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.0"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "path_length('TO')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That makes it clearer&mdash;the **O** is four keys to the right of the **T**, on the same row, so the distance between them is 4.\n",
    "\n",
    "Here's another one that you can verify on your own:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "path_length('TYPEWRITER') == 1 + 4 + 7 + 1 + 2 + 4 + 3 + 2 + 1 == 25"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Question 1: Longest Path Length?\n",
    "===\n",
    "\n",
    "To know what the longest word is, we'll have to know what the allowable words are. The so-called TWL06 word list gives all the words that are legal in the game of Scrabble; that seems like a reasonable list (although it omits proper nouns). Here's how to load a copy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "WORDS = set(urllib.urlopen('http://norvig.com/ngrams/TWL06.txt').read().split())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "21"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(WORDS)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's a lot of words; which one has the longest path?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'PALEOMAGNETISMS'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "max(WORDS, key=path_length)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And the longest ten paths? Including the lengths? We'll use a helper function, `print_top`, which prints the top *n* items in a seqence according to some key function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "72.2 PALEOMAGNETISMS\n",
      "70.0 ANTIQUARIANISMS\n",
      "69.9 ELECTROANALYSIS\n",
      "69.9 ANTIAPHRODISIAC\n",
      "69.3 PARLIAMENTARIAN\n",
      "68.9 BLEPHAROSPASMS\n",
      "68.6 BIDIALECTALISMS\n",
      "67.6 PALEOGEOGRAPHIC\n",
      "67.6 SPERMATOZOANS\n",
      "67.1 APOCALYPTICISMS\n"
     ]
    }
   ],
   "source": [
    "def print_top(n, sequence, key=None, fmt='{:.1f} {}'):\n",
    "    \"Find the top n elements of sequence as ranked by key function, and print them.\"\n",
    "    for x in sorted(sequence, key=key, reverse=True)[:n]:\n",
    "        print(fmt.format(key(x), x))\n",
    "              \n",
    "print_top(10, WORDS, path_length)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Question 2: Highest Path Length to Word Length Ratio?\n",
    "===\n",
    "\n",
    "Very long words tend to have long path lengths.  But what words have the highest *ratio*\n",
    "of path length to word length? (I decided to measure word length by number of letters; an alternative is number of segments.) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def path_length_ratio(word, kbd=qwerty): return path_length(word, kbd) / len(word)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6.9 PALAPA\n",
      "6.7 PAPAL\n",
      "6.4 PAPA\n",
      "6.4 JALAPS\n",
      "6.2 SLAPS\n",
      "6.2 KAMALA\n",
      "6.2 LAPELS\n",
      "6.2 PAPS\n",
      "6.2 HALALA\n",
      "6.1 SPALE\n"
     ]
    }
   ],
   "source": [
    "print_top(10, WORDS, path_length_ratio)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aside: Where did \"TYPEWRITER\" Come From?\n",
    "===\n",
    "\n",
    "How did I find \"TYPEWRITER,\" a long word whose letters are all on the top row?  I could have [looked it up](http://lmgtfy.com/?q=longest+word+typed+all+on+one+row+on+typewriter), but instead I used the following code:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10.0 PERPETUITY\n",
      "10.0 PEPPERTREE\n",
      "10.0 REPERTOIRE\n",
      "10.0 PREREQUIRE\n",
      "10.0 PROPRIETOR\n",
      "10.0 TYPEWRITER\n",
      "9.0 POTPOURRI\n",
      "9.0 TERRITORY\n",
      "9.0 TWITTERER\n",
      "9.0 PUTTYROOT\n"
     ]
    }
   ],
   "source": [
    "def on_top_row(word): return all(L in 'QWERTYUIOP' for L in word)\n",
    "\n",
    "print_top(10, filter(on_top_row, WORDS), len)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Question 3: Average Segment Length on Work Load?\n",
    "===\n",
    "\n",
    "What is the average segment length for a typical typing work load? To answer that, we need to know what a typical work load is.\n",
    "\n",
    "Representing Workloads\n",
    "---\n",
    "\n",
    "We will read a file of \"typical\" text, and count up how many times each segment is used. We'll define a `Workload` as a `dict` of the form `{segment: proportion, ...},` e.g. `{'AB': 0.02}`, where each key is a two-letter string representing a segment, and each value is the proportion of time that segment appears in the workload.  Since the distance from `A` to `B` on a keyboard is the same as the distance from `B` to `A`, we don't need both `AB` and `BA` as keys; we'll combine the counts for both of them under the alphabetically first one (in this case,  `AB`). So `Workload` will first generate all two-character *bigrams* in the text, then filter out the non-alphabetic ones, then take the `segment` of each pair, and count up (with `Counter`) how many times each segment occurs, and finally normalize these counts so that they sum to 1.0, but retain their proportion to each other."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def Workload(text):\n",
    "    \"\"\"Create a Workload--a dict of the form {'AB': 1000, ...} \n",
    "    saying how often each letter pair occurs in text.\"\"\"\n",
    "    pairs = filter(str.isalpha, bigrams(text))\n",
    "    return normalize(Counter(segment(A, B) for (A, B) in pairs))\n",
    "\n",
    "def bigrams(sequence):\n",
    "    \"Every sequence of two adjacent elements (overlapping) in sequence.\"\n",
    "    return (sequence[i:i+2] for i in range(len(sequence) - 1))\n",
    "    \n",
    "def segment(A, B):\n",
    "    \"Canonical two-character segment: segment('A', 'B') == segment('B', 'A') == 'AB'.\"\n",
    "    return A+B if (A < B) else B+A\n",
    "\n",
    "def normalize(dictionary):\n",
    "    \"Normalize a {key: val} dict so that the sum of the vals is 1.0.\"\n",
    "    total = sum(dictionary.values())\n",
    "    for k in dictionary:\n",
    "        dictionary[k] /= total\n",
    "    return dictionary"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see what a workload looks like for a tiny text:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({'OO': 0.55, 'GO': 0.1, 'DO': 0.05, 'HS': 0.05, 'IS': 0.05, 'AL': 0.05, 'AO': 0.05, 'HO': 0.05, 'OT': 0.05})"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Workload('SHOT IS GOOD -- GOOOOOOOOOOOAL!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I happened to have a file of about a megabyte of random text, `smaller.txt`; that should work fine as a typical work load:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "module 'urllib' has no attribute 'urlopen'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-13-388de13c3aca>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mTEXT\u001b[0m     \u001b[0;34m=\u001b[0m \u001b[0murllib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0murlopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'http://norvig.com/ngrams/smaller.txt'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      2\u001b[0m \u001b[0mWORKLOAD\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mWorkload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mTEXT\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: module 'urllib' has no attribute 'urlopen'"
     ]
    }
   ],
   "source": [
    "TEXT     = urllib.urlopen('http://norvig.com/ngrams/smaller.txt').read().upper()\n",
    "WORKLOAD = Workload(TEXT)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's peek at the most common segments:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('HT', 0.04144819308354474),\n",
       " ('ER', 0.04050225926898767),\n",
       " ('EH', 0.03576926529702987),\n",
       " ('IN', 0.02699818128015268),\n",
       " ('AN', 0.02320447132440709),\n",
       " ('NO', 0.022344984888333263),\n",
       " ('EN', 0.021994208025641622),\n",
       " ('IT', 0.021467211506811055),\n",
       " ('ES', 0.020667573255841017),\n",
       " ('DE', 0.020619362217840744)]"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "WORKLOAD.most_common(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "How many distict segments are there?  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "283"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(WORKLOAD)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we want to compute the average segment length, on a given keyboard, over all the segments that appear in a workload, so just multiply the length of each segment by the proportion of time it appears and sum these up:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Close enough.  Now we can compute the workload average:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'WORKLOAD' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-12-7d3b00ebb5d6>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mdef\u001b[0m \u001b[0mworkload_average\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkbd\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mworkload\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mWORKLOAD\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      2\u001b[0m     \u001b[0;34m\"The average segment length over a workload of segments.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m     return sum(distance(kbd[A], kbd[B]) * workload[A+B]\n\u001b[1;32m      4\u001b[0m                for (A, B) in workload)\n",
      "\u001b[0;31mNameError\u001b[0m: name 'WORKLOAD' is not defined"
     ]
    }
   ],
   "source": [
    "def workload_average(kbd, workload=WORKLOAD):\n",
    "    \"The average segment length over a workload of segments.\"\n",
    "    return sum(distance(kbd[A], kbd[B]) * workload[A+B]\n",
    "               for (A, B) in workload)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.2333097802127644"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "workload_average(qwerty)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, on average, your finger has to travel a little over 3 keys from one letter to the next over a typical workload."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aside: Visualizing a Keyboard\n",
    "---\n",
    "\n",
    "We'll need a way of visualizing what a keyboard looks like. I could just `print` letters, but I think it is more compelling to use IPython's `plot` facility. In the function `plot_kbd` we'll draw a square around the center point of each key, and annotate the square with the key letter. In `show_kbd`, we'll call `plot_kbd` and also print out the keyboard's name and workload average:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def plot_kbd(kbd, K=20):\n",
    "    \"Plot the keyboard with square keys, K units on a side.\"\n",
    "    H = K / 2       ## (K is Key width/height; H is half K)\n",
    "    # Draw a square for each key\n",
    "    for L in kbd:\n",
    "        x, y = K * kbd[L].x, -K * kbd[L].y\n",
    "        plot_square(x, y, H, label=L)\n",
    "    # Show the plot\n",
    "    plt.axis('equal'); plt.axis('off'); plt.show()\n",
    "\n",
    "def plot_square(x, y, H, label='', style='k-'):\n",
    "    \"Plot a square with center (x, y), half-width H, and optional label.\"\n",
    "    plt.plot([x-H, x+H, x+H, x-H, x-H], \n",
    "             [y-H, y-H, y+H, y+H, y-H], style)  \n",
    "    plt.annotate(label, (x-H/4, y-H/4)) # H/4 seems to place label well.\n",
    "    \n",
    "def show_kbd(kbd, name='keyboard'):\n",
    "    \"Plot a keyboard and print statistics about it.\"\n",
    "    plot_kbd(kbd)    \n",
    "    print('workload average = {:.1f} for {}'\n",
    "          .format(workload_average(kbd), name))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYYAAAD7CAYAAABuSzNOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAADbxJREFUeJzt3Xvw7HVdx/HnS4ibF+RijCNiOcrQCAVndELNJmXMxCah\nBgN1aEYtm3QicWIyx1sX71wSkdTKkcYAyxtjZjemvJBmHrQDZnomvOskcio6KHGO7/7Y74/2/TMY\n4Oxnf+e35/mYYeC3OvvaPWf399z9/n67m6pCkqQ199roCyBJ2rsYBklSYxgkSY1hkCQ1hkGS1BgG\nSVJjGCRJjWGQJDWGQZLUGAZJUmMYJEmNYZAkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJ\njWGQJDWGQZLUGAZJUmMYJEmNYZAkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWG\nQZLUGAZJUmMYJEmNYZAkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWGQZLUGAZJ\nUmMYJEmNYZAkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWGQZLUGAZJUmMYJEmN\nYZAkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWGQZLUGAZJUmMYJEmNYZAkNYZB\nktQYBklSYxgkSY1hkCQ1hkGS1Oy/0RdgX5PkJuCwJc0VELf2+h23NtfWMq/Tjqo6fElbt0tVLXtz\nn5akqmopNyq3NseOW5traxWv03oeSpIkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGPZySR6U\n5L1JPpdke5I3JPm+BW9ckORX577+YJK3zH39+iS/tsC93Um2Jrl2+vd5izrvO9naluR9Se43YOPw\nuevy9SRfmft64S8iTfLhJD819/UZST6w6J3pvB+SZNu6016W5NwBWzcv+jzvZGv+fvX5JBcO+rua\nv/1dmeSgRW+MYBj2fu8G3l1VxwIPBw4BXrfgjY8CjwFIEuBI4BFz//tjgGsWuLezqrZU1UnTv1+7\nwPO+o60TgB3A8xY9UFU3rV0X4FLggrnrtmvRe8AvAxckOSDJfYDfBX5lwM6aZb0Kdpmvtp2/Xx0L\n3Bd45YCd+dvfbcz+7vZ6hmEvluQJwLer6jKAmr1M/QXA2UkOWeDUNUxhYBaE64Cbkxya5ADgOGDr\n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\no3aqahdwHnAhcE5V7R40dTzwyUHnfbuq2gH8I/Dk6aQzgXcOnDx4OpT0L8y+cf/2oJ0CLgGekeS+\ngzbmt64AzpqeNfww8PHBmyvPMGgRfge4rqr+bAlbpwJfA05YwtYyXMEsCEz/Hvlo95bpUNIPMYvR\nH48aqqr/Bt4OnDNqY27rOuAHmD1b+HP6MyPdA4ZBeyTJTwCnA89bwtaJwCnAycC5SRZ9zH/N9cAj\nB533eu8DTklyEnBwVV27jNGq+hhwZJIjB878HvBs4JCBG2uuAl6Hh5EWwjDoHktyGPBHwNlVdcsS\nJt/E7BDSV4DXMuhnDFV1NXBAkuesnZbkhCSPHbC1E/g7Zn+Oo7+p3f5IOslxzO7/3xq1Mx0qeyfw\nnDv/v+/5FrM/v1dU1fUDt/YZhkF74rnAA4BL535ddWuSMxY9lOQXgS9O37QBLgWOS/K4RW9NTgee\nmGR7km3AK4FvDNq6nNmx8dFhOGjt72naOrvGvO/+/HmeDxyx7rSFb1XVV6vqjYM29jl+HoOaVX0v\nfLc2x84qb20mPmOQJDWGQZLUGAZJUmMYJEmNYZAkNYZBktQYBklSYxgkSY0vcFOTxBuE9iU7qmoZ\nnxuxqRgGSVLjoSRJUmMYJEmNYZAkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWG\nQZLUGAZJUmMYJEmNYZAkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWGQZLUGAZJ\nUmMYJEmNYZAkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWGQZLUGAZJUmMYJEmN\nYZAkNYZBktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWGQZLUGAZJUmMYJEmNYZAkNYZB\nktQYBklSYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWGQZLUGAZJUmMYJEmNYZAkNYZBktQYBklS\nYxgkSY1hkCQ1hkGS1BgGSVJjGCRJjWGQJDWGQZLUGAZJUmMYJEmNYZAkNYZBktQYBklSYxgkSY1h\nkCQ1hkGS1BgGSVJjGCRJjWGQJDWGQZLUGAZJUmMYJEmNYZAkNYZBktQYBklSYxgkSY1hkCQ1/wtr\n248BxMHn+gAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x109fe18d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "ename": "NameError",
     "evalue": "name 'workload_average' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-11-84031174ed37>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mshow_kbd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mqwerty\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-10-b3c79385436d>\u001b[0m in \u001b[0;36mshow_kbd\u001b[0;34m(kbd, name)\u001b[0m\n\u001b[1;32m     19\u001b[0m     \u001b[0mplot_kbd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkbd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     20\u001b[0m     print('workload average = {:.1f} for {}'\n\u001b[0;32m---> 21\u001b[0;31m           .format(workload_average(kbd), name))\n\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m: name 'workload_average' is not defined"
     ]
    }
   ],
   "source": [
    "show_kbd(qwerty)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Question 4: Keyboard with Minimal Workload Average?\n",
    "===\n",
    "\n",
    "Now for a much harder question: can we find a different keyboard layout that has a smaller average segment length over the workload? First, let's note that there are two ways to modify a keyboard:  \n",
    "\n",
    "* Keep the keys in the same locations but swap letters. (This is an operation you can do on a physical keyboard just by prying off the key caps and swapping them.) \n",
    "* Change the locations of keys.  (On a physical keyboard you'd need a saw and glue to do this, but it is easier on a virtual keyboard.)  \n",
    "\n",
    "Let's start by limiting ourselves to just swapping letters.  \n",
    "\n",
    "This is an **optimization** problem.  There are many permutations of letters; too many to try them all. To be precise, there are 26! (26 factorial) permutations, which is about 10<sup>26</sup> (fun fact: 25 and 26 are the only integers for which n! &approx; 10<sup>n</sup>).  If we can't try them all, we need some way to sample the configurations, trying to make progress towards a better one. Again, we'll try the simplest thing that could possibly work: \n",
    "\n",
    "  1. Pick two keys at random.\n",
    "  2. Swap them.\n",
    "  3. If that gives a better (lower) workload total, keep them that way.\n",
    "  4. If not, swap back.\n",
    "  5. Repeat this for a given number of times, say 1000."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def improve(kbd, swaps=1000, scorer=workload_average):\n",
    "    \"Minimize scorer(kbd) by swapping keys and keeping improvements.\"\n",
    "    score = scorer(kbd)\n",
    "    letters = list(kbd)\n",
    "    for _ in range(swaps):\n",
    "        A, B = random.sample(letters, 2)   # Step 1: pick two keys\n",
    "        swap(kbd, A, B)                    # Step 2: swap them\n",
    "        score2 = scorer(kbd)\n",
    "        if score2 < score:                 # Step 3: If better, keep them\n",
    "            score = score2                 # (and record the new best total)\n",
    "        else:\n",
    "            swap(kbd, B, A)                # Step 4: swap back if not better\n",
    "    return kbd\n",
    "\n",
    "def swap(kbd, A, B): kbd[A], kbd[B] = kbd[B], kbd[A]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note 1:** This strategy is called **hillclimbing**, drawing on the metaphor of getting to a high peak by trying to take a step, and continuing if the step is uphill, and returning if it is not.  This technique often finds a local maximum&mdash;a solution that is better than all its neighbors, but not as good as another solution that is many steps away.\n",
    "\n",
    "**Note 2:** I make `scorer` be a parameter, in case we later decide we want to minimize something else other than `workload_average` (such as confusingness).\n",
    "\n",
    "**Note 3:** The procedure  `improve` modifies its argument.  If you called `improve(qwerty)`, then `qwerty` would be modified. We\n",
    "probably don't want that. So, in analogy with the built-in function `sorted`, I will define the pure function `improved` to return a new keyboard, without modifying its argument:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def improved(kbd, swaps=1000, scorer=workload_average): \n",
    "    \"Minimize scorer(kbd) by swapping keys and keeping improvements; don't modify kbd.\"\n",
    "    return improve(kbd.copy(), swaps, scorer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see how well we can do:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEZpJREFUeJzt3XtMlYXjx/EPtxIFJ5hdVCwJLALUQ0YIktNWlM6cmddN\nxUpTZza8DS8FopvLTLHVamHGcg286yZ4SdNQOabGaYFiWEpeWCkIDRRN6fn90Q5fcLWfl+c5wOH9\n2ti+nPY9n/Mg+OY856KHYRiGAACtmmdT3wAAQNMjBgAAYgAAIAYAABEDAICIAQBAxAAAIGIAABAx\nAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAg\nYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEA\nQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABED\nAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAi\nBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAIAk76a+Aa1FYGCgKisrXbLl4eEh\nwzDYagFb7nhM7rrlymMKCAjQ5cuXXbLl5GG46uhaOXf84WCr5eyw1XJ2XL3lxGkiAAAxAAAQAwCA\niAEAQMQAACBiAAAQMQAAiBg0S+Xl5Ro9erRCQkIUGhqqhQsXqq6uzvSdgQMHavfu3Y0uS09P17Rp\n00zf8vT01Lhx4+o/v3nzpjp16qQhQ4aYvuXn59fo88zMTL399tum70hSRUWFbDabbDabHnnkEXXt\n2lU2m01RUVG6ceOGaTu3HpOVvLy8ZLPZ1KNHDz3zzDNas2aNZc95T0pK0qpVq+o/T0hI0KRJk+o/\nnzVrllauXGnK1rlz5xQcHFz/4s/KykoFBwfr7Nmzplx/Q1u2bKn/vnB+eHl5adeuXaZvmYUYNEOJ\niYkKDQ2Vw+HQrl27VFRU1OgHxixjxoxRdnZ2o8vWrVunsWPHmr7Vrl07HT9+XNeuXZMkffPNN+ra\ntas8PDxM37r1Oq3YcOrYsaMcDoccDoemTJmimTNnyuFwqKCgQD4+PqbtWHkMt2rbtq0cDoeKi4u1\nZMkSZWRkWPL9J0n9+vVTfn6+JOnvv/9WRUWFTpw4Uf/f7Xa74uLiTNkKCgrS1KlTlZycLElKTk7W\nW2+9pW7duply/Q0NGzas/vvC4XBo6tSpeu6555SQkGD6llmIQTNTXV2t48ePa/HixfL391dwcLCW\nLl2qzZs3m741fPhw5eTk6ObNm5Kk0tJSlZWVqV+/fqZvSdKgQYOUk5MjScrKytKYMWNc8ipLV76S\n051e0O/l5aWEhATNnTtXy5Yts2Sjb9++stvtkqTjx48rIiJC/v7+qqqq0vXr11VcXKyoqCjT9pKS\nknT48GGlp6crPz9fs2fPNu26/0tJSYkWL16stWvXWr51L4hBM5Obm6v4+PhGl4WFhen8+fO6ePGi\nqVuBgYGKjo5Wbm6uJCk7O1ujRo0ydaOhUaNGKTs7W9evX1dhYaGeffZZS3Zqa2sb3T1PSUlx6W/W\n7uaFF15QZWWlampqTL/uzp07y9vbW+fOnZPdblffvn0VHR0tu92uY8eOKTIyUt7e5r2Fmre3t5Yt\nW6aZM2cqPT1dXl5epl33v7lx44bGjh2rFStWqGvXrpZu3Sti0Az9219chmHoypUrpm81PFW0bt06\njRkzxvQNp8jISJWWliorK0uDBw+2bMfX17fRXfS0tDS3+o3d1QzDkGEYlgU1NjZW+fn5ys/PV9++\nfdW3b1/l5+fLbrdbci91x44d6ty5swoLC02/7lu9++67ioyM1IgRIyzfulfEoJkZNGiQ8vLyGl1W\nXFys69evq3v37qbvvfLKK9q7d68cDoeuXr0qm81m+sate7Nnz3bZKSLJvU7dNIXdu3frgQceULt2\n7Sy5/ri4OB06dEiFhYWKjIxUTExMfRxiY2NN3frxxx+1Z88e2e12rVy5Ur///rup19/Q/v37tWXL\nFn388ceWbZiJGDQz/v7+Cg8PV2pqqqqrq3X69GktWLDAkmf4SP88S2XAgAGaOHGiJQ8c3+r1119X\namqqwsPDLd/Cvamrq9OePXu0YsUKzZkzx7Kd2NhYbd++XR07dpSHh4cCAgJUVVUlu91uagwMw9DU\nqVO1atUqBQUFac6cOZY9ZlBZWamJEyfqq6++siyiZiMGzVBmZqZOnjyp3r1768knn9TDDz+s9957\nz7K9MWPGqLCw0NJTRM5TDF26dNH06dPrL3PVs4lc9ZiBVTuufMzD+ZhLWFiYkpOT9cYbb2jGjBmW\n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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10da809d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9 for keyboard\n"
     ]
    }
   ],
   "source": [
    "show_kbd(improved(qwerty, 1000))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's a pretty good improvement! We decreased the workload average  by about a third. (If you are reading this in an active IPython notebook, you can re-run the cell above and see a different result each time.) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visualizing `improved`\n",
    "---\n",
    "\n",
    "Let's get a better feeling for what `improved` does.  We will keep track of the workload average after each swap, and plot that as a line.  (We know this line should be monotonically decreasing, starting at 3.23.)  We will then plot a dozen instances of this line (different each time because the swaps are random).  \n",
    "\n",
    "I'll copy-and-paste `improve`   (regrettably violating the [don't repeat yourself](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle), and make `improve_scores`, adding code that yields the average workload after each swap."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n/1d6ltoAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115db4550>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def improve_scores(kbd, swaps=1000, scorer=workload_average):\n",
    "    \"A version of 'improve' that yields the workload average after each swap.\"\n",
    "    total = scorer(kbd)\n",
    "    letters = list(kbd)\n",
    "    T = sum(WORKLOAD.values())             # <<< NEW\n",
    "    for _ in range(swaps):\n",
    "        A, B = random.sample(letters, 2)   # Step 1: pick two keys\n",
    "        swap(kbd, A, B)                         # Step 2: swap them\n",
    "        total2 = scorer(kbd)\n",
    "        if total2 < total:                 # Step 3: If better, keep them\n",
    "            total = total2                 # (and record the new best total)\n",
    "        else:\n",
    "            swap(kbd, B, A)                     # Step 4: swap back if not better\n",
    "        yield total / T                    # <<< NEW\n",
    "    \n",
    "def workload_plot(kbd, swaps):\n",
    "    plt.ylabel('Workload average segment length') \n",
    "    plt.xlabel('Number of swaps');  \n",
    "    plt.plot(list(improve_scores(kbd.copy(), swaps)))                         \n",
    "\n",
    "workload_plot(qwerty, 3000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's interesting; it looks like most of the progress in in the first few hundred swaps.  But this is just one random run of the algorithm.  Let's compare ten runs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nAWvav4zhjasWAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1116e3d10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for run in range(10):\n",
    "    workload_plot(qwerty, 3000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This plot is even more interesting. (Note: on most browsers you can drag the lower-right corner of the plot to make it bigger.) I note the following:\n",
    "\n",
    "* Each line follows a slightly different path (because the swaps are random).\n",
    "* The lines are grouped very tightly together; the variance is small.  Almost everywhere, the difference between the best line and the worst line is about 0.2 or less. By the end, almost all the lines are between 1.9 and 2.0.\n",
    "* We make rapid progress, decreasing from 3.2 to around 2.2 in about 200 swaps, and to around 2.0 in about 500 swaps.\n",
    "* After 1000 swaps, progress is slow, and after 2000 swaps progress is very slow.\n",
    "* Obviously, at any number of swaps the minimum of the 10 lines is less than the average of the 10 lines.\n",
    "\n",
    "This last point suggests a strategy: *repeat the hillclimbing search multiple times, and take the best result.* Let's investigate that strategy.\n",
    "\n",
    "Improvement with Repeated Runs\n",
    "---\n",
    "\n",
    "Suppose we have a certain budget of time that we are willing to spend to try to find a keyboard\n",
    "with a low workload average. Suppose the budget is, say, 3,000 swaps and scorings.  How should we spend this budget?  We could have a single run of `improved(qwerty, 3000)`.  Or we could repeat `improved(qwerty, 1000)` three times and take the best of the three results. Or repeat `improved(qwerty, 500)` six times.  From the plot above, it is not clear which of these approaches would be better. (From the plot it is clear that there is rapid progress in the first 500 swaps, so I wouldn't want each run to be much less than that.)\n",
    "\n",
    "The function `repeated_improved` allows you to decide how to split up your budget between long runs and repeated runs, and always takes the best of the repeated runs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def repeated_improved(kbd, repeats=20, swaps=1000, scorer=workload_average):\n",
    "    \"Try improved(kbd, swaps) multiple times and take the best result.\"\n",
    "    return min([improved(kbd, swaps, scorer) for run in range(repeats)],\n",
    "               key=scorer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see what this can do:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n3n33Xct3ih5Oadu2rRITEzVr1izLN+41aNAg7d+/X4MGDbJt4+6HOVwul4KCgrR06VJbtopuw6KX\nqVOn2rJz70M3DofD1odzFi1apLNnz6pTp05q3bq1pk+frkaNGtmy1bp1a+Xk5Cg8PLz4sjZt2qhu\n3bry9/e3bKfo9vLz89P69es1Y8YM275rnD9/vi5cuKA33nijxP1j2bJltuxZgb9n4CZlPT95RkaG\nRo4cqWXLlik4ONjWrbJgq3LssFV5dty9VbxJDNzDU+9IbFWOHbYqz467t4rwMBEAgBgAAIgBAEDE\nAAAgYgAAEDEAAIgYAABEDAAA4qylbuXOs0GyVXm2PPE6eeqWu3b8/PzcsnM3YuAmZX02oSc+69ET\nrxNblWfHk7fKg4eJAADEAABADAAAIgYAABEDAICIAQBAxAAAIGLgcZxOp+0b3t7ecrlcat++vSZN\nmqSCggJbdry8vDRkyJDi12/fvq169eqpV69etuxdvHhRAwcOVFBQkFq2bKn4+HgVFhbasrVw4UJ1\n69ZNbdq0kcvl0u7du23ZycnJKf77uw0aNFDjxo3lcrkUFhZm+fvNHfc96T/3v1atWqljx45avHix\nbb/Hf+bMGQUGBio3N1fSP3/oPjAwUKdPn7Zlr0IZPNRK+y5yOp22bxVt3Lp1y0RHR5vVq1fbtuNy\nuUx+fr4xxpj09HTTrl0706tXL8u3jDEmJibGxMfHmytXrpjjx4+b3r17m6SkJMu3srOzTWhoqLl2\n7ZoxxpicnBxz7ty5B94pzdbdEhISSnV9SrtVnvteWXZu375t1q9fb8LDw83s2bNt2TLGmMTERDNq\n1ChjjDGjRo0yH330kW1bFYnvDFBm1atXV48ePbRt2zbbNqKjo7V27VpJUmpqqgYNGmTLV4F5eXn6\n888/9cEHH8jX11eBgYGaOXOmfvjhB8u3jhw5ooCAANWqVUuS5O/vrwYNGli+cz923HYVxdvbW1FR\nUZo8ebISExNt2xk/frx27typ5ORk7dixQxMnTrRtqyIRA5TZ5cuXtWbNGkVFRdm2MWDAAKWlpenm\nzZvav3+/nnnmGVt20tPT1bVr1xKXBQcH6+zZszp//rylW926ddOdO3fUrFkzjRs3TseOHbP0+FXN\n888/r9zcXF29etWW41erVk2JiYmKi4tTcnKyvL29bdmpaMQApZafny+Xy6XevXurV69e6tatm21b\n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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10ac91510>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.0 for 3000 swaps repeated once\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEXpJREFUeJzt3XtM1fXjx/EXHDTBA4VmXsILpDZDjIOXRDSmrcwLOjVT\nXF6odJMy760UF6nTNO85a2nO1RzeMl2Kl1SaF1ArKRUtTWNqrBQEBcQL+v790WDi/P4m+Pkc5PB8\nbGxfTvt+XpwD9fR8Dp+jlzHGCABQrXlX9hcAAKh8xAAAQAwAAMQAACBiAAAQMQAAiBgAAEQMAAAi\nBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAA\nRAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEA\nAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBi\nAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBA\nxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAACQ5FPZX0B1UadOHeXm5rply8vL\nS8YYtqrAlifeJ0/dcud9CgwM1OXLl92yVcLLuOveVXOe+C8HW1Vnh62qs+PurRKcJgIAEAMAADEA\nAIgYAABEDAAAIgYAABEDAICIwSMpOztbgwcPVvPmzdWiRQslJCTo9u3blu84HA65XC49//zz6tWr\nl44fP275RonMzEyFhYWVuS0xMVHz58+3fMvpdFp+zP+l5DGMiIjQhAkTdPPmTVv3Nm3aJG9vb/3x\nxx+27uTm5iouLk4hISEKDQ1Vr169dPr0act3vL29NXTo0NLPi4uLVa9ePcXExFi+dffPRXJysp59\n9lmdP3/e8h1J+u677+Ryucp8OBwO7dixw5Y9KxCDR9CIESPUokULpaena8eOHTp+/LgWL15s+Y6f\nn5/S09P122+/acSIEZoxY4blG/8fLy+vKnXc+yl5DA8fPqwzZ85o586dtu4lJSWpd+/eSkpKsnXn\nrbfeUoMGDXTo0CFlZGRo2rRpysrKsnyndu3aysjI0PXr1yVJP/zwg4KCgmz5HpYcc/fu3Ro7dqy2\nb9+uxo0bW74jSf369VN6enrpx+jRo/Xiiy+qe/futuxZgRg8YvLz85WRkaEZM2bI399fISEhmj17\ntjZu3GjbpjFG2dnZqlWrlm0bns7Hx0fR0dFKSUmxbaOgoECHDh3S0qVLtXbtWlt3jhw5otmzZ6te\nvXqSpI4dOyo6OtqWvZ49e2rr1q2S/otdbGysbVff7t27V6NGjdLWrVsVHBxsy8a9Tp06pRkzZuib\nb75xy15FEYNHTHJysrp06VLmtlatWunChQu6ePGipVtFRUVyuVwKDg5WYmKiZs2aZenxq5MrV65o\n27ZtCg8Pt21j8+bNevXVV9WkSRPVq1dPR44csWXnfj+Ddho0aJDWrFmjGzdu6NixY3rhhRds2bl+\n/br69eunzZs3q2XLlrZs3OvWrVsaMmSIFixYoKCgILdsVhQxeATd7ymyMUaFhYWW7vj6+io9PV2Z\nmZlatmyZXnvtNUuPf7f7vdeKMcatp3TsUBLUoKAgORyOMue/rZaUlKSBAwdKkgYOHGjbqSJ3f0/C\nwsKUmZmppKQk9erVy7admjVrKioqSitWrLBt417Tpk1TWFhY6fftkWbgFg/6UF+9etU0a9aszG0n\nTpww9evXt3zL6XSW/u87d+6Yxx9/3BQWFj7wTnm2iouLzdNPP21u3rxZetvAgQPNnj17LN+6+35V\nVHm3rly5Ytq2bWu+//57W3ZycnKMn5+fadq0qWnWrJlp3LixadKkiS1b+fn5pmnTpuU6dkW3Sh6/\n6dOnm7p165rjx4+blJQU07t3b1u2ioqKTGRkpJk1a9YDH7+8OyVSUlJMy5YtTUFBge1bVuCZwSPG\n399foaGhSkxMVH5+vs6ePaupU6cqPj7e1t0DBw6oRYsW8vPzs+X4DodDXbt2Lf3T7KlTp3T06FHb\nzkO7W0BAgJYvX67333/flvPdGzZs0LBhw5SZmam//vpL586dU3BwsPbt22f5ltPpVEREhBISEnTp\n0iVJ0k8//aS9e/davlXizTffVGJiokJDQ23bkKRatWpp69atWr16tVauXGnbTslvY3399deqXbu2\nbTuWcnt+qqnyPNSXLl0ygwYNMiEhIaZGjRpm9OjRtmw5HA4THh5u2rRpY2JiYszevXvLtVOeLWOM\nOXv2rBk7dqwJDw83/fv3N7t27bJly9vb2wQFBZV+LFy4sFw75dny9/cv83lMTIxZs2aN5Ttdu3Y1\nO3bsKHPbkiVLTHx8vOVbxvz3TGT48OGmWbNmJjQ01PTu3dv8+eeflm/d+/gZY8yPP/5oYmJibN06\nf/68CQ4OLtczufI8frNmzTK1a9c24eHhZT7WrVtn+ZZV+PsM3KSi70+elpamkSNHav369WrVqpWt\nWxXBVtXYYavq7Lh7q3STGLiHp/4gsVU1dtiqOjvu3irBawYAAGIAACAGAAARAwCAiAEAQMQAACBi\nAAAQMQAASPKp7C+gOnHnu0GyVXW2PPE+eeqWu3YCAwPdsnM3YuAmFb2a0BOvevTE+8RW1dnx5K2H\nwWkiAAAxAAAQAwCAiAEAQMQAACBiAAAQMQAAiBh4HKfTafuGw+GQy+VS27ZtNXnyZN26dcvWnZKP\nc+fO2bJTIjs7W4MHD1bz5s3VokULJSQk6Pbt25bv5OTklN6nhg0bKigoSC6XSxEREZY/lvc+hnPn\nzrX0+Hfz9vbWpEmTSj+fN2+ePv74Y1u2cnNz9fbbb+uZZ57Rc889p44dO2rTpk2W75w/f14hISHK\nzc0t3Q0JCbH9Z7FS2Po3LOOhlfdb5HQ6bd8q2bh586bp2bNnuf5S8YrsPIzyPH69evUyCQkJ5urV\nq+bMmTOmb9++Zv78+bZslUhMTCzXRnm33PkYPvbYYyYkJMRkZ2cbY4yZN2+eSUxMtHzHGGMGDBhg\nPvzwQ5OVlWWMMeb06dPm008/tWVr7ty5ZtSoUcYYY0aNGmU++eSTB/7/lnerMvHMABVWo0YNdevW\nTfv376/sL+Wh5efnKyMjQzNmzJC/v79CQkI0e/Zsbdy40fZtUwWuTn0QNWrU0KhRo7Rw4UJbdwoL\nC/XLL79o1qxZatiwoSSpefPmZZ6VWGn8+PE6ePCgFi1apNTUVNt2KhsxQIXl5eVpy5Yt6t69uy3H\nLyoqKj29MWDAAFs2SiQnJ6tLly5lbmvVqpUuXLigixcv2rptp7sfQ5fLpfXr19u6Fx8fr9WrV+vq\n1au2bWzdulWdO3e27fj38vHx0dy5czVhwgQtWrRIDofDbdvuxHsTodxK/gMTEBCgvn37Kjo62pYd\nX19fpaen23Ls+7nfm5BVlfeV+V/c/Rj6+/tr2LBhWrJkiXx9fW3ZuPf79O6772r//v2qWbOmDh8+\nbMvmtm3b1KhRIx07dkwvvfSSLRuVjRig3Nz9Hxh36Nmzpz744IMyt508eVKNGjVS/fr1K+mrqprG\njRuniIgIxcXF2XL8Hj16aNKkSTLGyMvLS0uXLlVOTo7atWtny96vv/6qXbt2KS0tTZ07d9bgwYPV\noEEDW7YqE6eJAP33J9rQ0FAlJiYqPz9fZ8+e1ZQpU9S/f//K/tKqnMDAQL3++uv66quvbHnLZ6fT\nqXbt2mnq1KnKysqS9N/rCHYwxmj06NFavHixGjdurMmTJ/OaAR59RUVFeuKJJ2zf8bT3ji+xatUq\n/f777woPD9crr7yiVq1aafz48bbv2nk/733NYMqUKbZt3X0/Jk6cqOzsbNu2VqxYoX///VdRUVHq\n0KGDRowYYcuvzS5fvlzNmjUrPTUUHx+vkydPat++fZZvVTYvU5VPiFYD5TlnnZKSoi+//FJJSUm2\nbz0MT30vebaqxo4nbz0MXjPwEJ9//rm+/fZbzZw5s7K/FABVEM8MHnGe+CcYT7xPbFWdHU/eehi8\nZgAAIAYAAGIAABAxAACIGAAARAwAACIGAABx0VmV4M63ZfDEt5pgq+pseeJ9CgwMdMvOwyIGjzh3\nXqzCxUVsVeaWJ96nqoTTRAAAYgAAIAYAABEDAICIAQBAxAAAIGIAABAxQAUYY9SlSxdt37699Lb1\n69erR48elu6MHz9eixcvLv28e/fuGjlyZOnnEydO1MKFCy3bW758uaKjo9WmTRu5XC4dPnzYsmPf\nzel0lvl81apVGjNmjFu27ORwOORyudSyZUu1b99eK1eutOV3+bt166adO3eWuW3RokWKj4+3fKs6\nIQYoNy8vL33xxReaMGGCbty4oYKCAk2dOlXLli2zdKdz585KTU2VJN25c0c5OTk6ceJE6T9PS0tT\nVFSUJVtZWVn67LPPtG3bNh09elS7d+9W48aNLTn2ve698tXOK2HdeUWvn5+f0tPTdfLkSc2cOVPL\nly8vE3OrxMbGas2aNWVuW7t2rYYMGWL5VnXCFciokNDQUMXExGjOnDkqKCjQ8OHDFRwcbOlGZGSk\nxo8fL0nKyMhQ69at9c8//ygvL0++vr46efKkIiIiLNk6deqUnnrqKfn5+UmS6tSpY8lxH4SnXQnr\ncDjUvXt3Xbt2Te+8847GjRtn6fEHDBighIQEFRcXy8fHR5mZmcrKylLnzp0t3aluiAEq7KOPPpLL\n5VKtWrX0888/W378Ro0aycfHR+fPn1daWpoiIyP1999/Ky0tTQEBAQoLC5OPjzU/wtHR0Zo+fbqa\nNm2qvn376r333lPz5s0tOfa9ioqK5HK5Sj+/fPmy+vbta8tWZXr55ZeVm5urgoICS09X1alTRx06\ndFBycrL69OmjNWvWaNCgQZYdv7riNBEqzM/PT4MHD9bQoUNVo0YNWzY6deqk1NRUpaamKjIyUpGR\nkUpNTVVaWpqlfxL08vLSnj17tGHDBvn6+ioqKkrJycmWHf9uvr6+Sk9PL/2YPn26xz07kP57xmOM\nseVU1d2nitauXavY2FjLN6obYoCH4u3tbet56aioKB04cEDHjh1TWFiYOnbsWBqHTp06Wb7Xvn17\nzZkzR3PmzFFSUpLlx78fTwyBJO3cuVNPPvmkateubfmx+/Tpo927dys9PV3Xrl0r80wLFUMM8Ejr\n1KmTtmzZorp168rLy0uBgYHKy8tTWlqapTE4deqUTp8+LUkqLi7WwYMHbYlNdXD79m3t2rVLCxYs\n0OTJk23ZcDqd6tq1q+Li4njh2CK8ZoCHZuczg9atWysnJ0dvvPFG6W1t2rTRtWvXLH2Rt6CgQGPG\njFFeXp6cTqciIyM1fPhwy45/t/v9NpFdj6E7f5uo5LWQwsJCBQQEKD4+XnFxcbbtxcbGqn///lq3\nbp1tG9WJl/HU56goN963nq3K3PLE+1SVcJoIAEAMAADEAAAgYgAAEDEAAIgYAABEDAAAIgYAAHEF\nMu7hritW3XllLFtVZ8tdO4GBgW7ZqUqIAUpxRSZQfXGaCABADAAAxAAAIGIAABAxAACIGAAARAwA\nACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgY\nAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQ\nMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAA\nIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgB\nAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAAR\nAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAg6f8AhO3NmEIWK6AAAAAA\nSUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x111717450>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.0 for 1000 swaps repeated three times\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEYRJREFUeJzt3XlQVQXDx/HfBXQEwRFMxgVcSExi0YvmKGiOPlNkao6Z\nuUwqZuYyZoPbmGgSOmM55lKTOWHmNDW4pk3ivpQL16jhlguuKYo5piIqFC7oef5438sr5PO8gedc\n4PL9zPAHOHN+91yvfuHAvdgMwzAEAKjVvKr6BgAAqh4xAAAQAwAAMQAAiBgAAEQMAAAiBgAAEQMA\ngIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIG\nAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABE\nDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAA\niBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIA\nABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAACT5VPUNqC2CgoJUUFDgli2b\nzSbDMNiqAVueeE6euuXOcwoMDNT169fdsuViM9x1drWcJ/7jYKvm7LBVc3bcveXCZSIAADEAABAD\nAICIAQBAxAAAIGIAABAxAACIGFRb/v7+bt3YsmWLnnrqKeXl5VmylZSUpKVLl5a+n5CQoDFjxpS+\nP2XKFC1evNiULW9vb9ntdrVt21bPPPOMVq5cadnPbOfn58tut8tut6tp06YKCQmR3W5XbGys7t27\nZ9pO+cfDqlWr9NZbb5l2/P+2ZSUvLy8NHz689P2SkhI1btxY/fr1M33LMAx1795d27ZtK/3YunXr\n1Lt3b9O38vLyFBYWVvpE04KCAoWFhenChQumb5mFGFRTNpvNbRu7d+/W22+/rW3btik0NNSSrW7d\nuikzM1OS9ODBA+Xn5ysnJ6f0zx0Oh+Lj403Z8vPzk9Pp1PHjxzVv3jylpaWVCZGZGjVqJKfTKafT\nqXHjxmny5MlyOp3Kzs5WnTp1TNsp/3iw8vHhjseeS/369XXs2DHdvn1bkrRz506FhIRYchtsNpuW\n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\nFCnhTOdVAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115d974d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.0 for 500 swaps repeated six times\n"
     ]
    }
   ],
   "source": [
    "show_kbd(repeated_improved(qwerty, 1, 3000), \"3000 swaps repeated once\")\n",
    "show_kbd(repeated_improved(qwerty, 3, 1000), \"1000 swaps repeated three times\")\n",
    "show_kbd(repeated_improved(qwerty, 6, 500),  \"500 swaps repeated six times\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's do it again, to see how different the result is, and this time let's also check how long it takes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10da83b50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9 for 3000 swaps repeated once\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAETpJREFUeJzt3XtMlYXjx/HPAXWiBwtM8oIXSC0TzINKAiqzVoQXnJbX\nlWlmqalLRbPQJOcqzRvNzIWZtRwoOnUpXrJs3lBz0kIlL1Mm6ipBSEAipOf3x3fn/MCf398Enudw\ne782/uA0nw/nkm95OOdgMwzDEACgQfOo6S8AAFDziAEAgBgAAIgBAEDEAAAgYgAAEDEAAIgYAABE\nDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAA\niBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIA\nABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDE\nAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCA\niAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgqVFNfwENha+vr/Ly8tyyZbPZ\nZBgGW3Vgqz5ep/q65c7r5OPjo1u3brlly8lmuOvaNXD18X8OturODlt1Z8fdW06cJgIAEAMAADEA\nAIgYAABEDAAAIgYAABEDAICIQa2Uk5OjMWPGqHPnzurSpYsWLFigsrIy03eys7MVGBjoejFcXl6e\nAgMDdfXqVdO3PD095XA4XB9WbDhlZWUpODi4wmXx8fFasWKFJXvuuL9yc3Ndt12bNm3k7+8vh8Oh\nkJAQlZaWmrplt9srfL5x40bNmDHD1I3y8vLyNHHiRAUGBqp79+4aPHiwLl68aPrOM888o/3791e4\nbPXq1Zo2bZrpW9u3b6/weHc4HPL09NS+fftM3zILMaiFJkyYoC5duig9PV379u3TmTNnlJCQYPpO\n+/btNXXqVM2fP1+SNH/+fL355pvq0KGD6VvNmjVTenq668OKjf+PzWaz7NjuuL9atmzpuu2mTJmi\n2bNnKz09XadPn1bjxo1N3br3trLytpOkSZMmqXXr1jpx4oTOnj2rhQsX6saNG6bvjB07VsnJyRUu\n27x5s8aNG2f61vDhwys83qdOnaoBAwYoKirK9C3TGHCLB72pb9++bXTq1KnCZefOnTMiIiJM3zIM\nwygtLTV69OhhrFq1yggKCjLu3r37wH+2Mlt2u71Sx63O1pUrV4ygoKAKl8XHxxvLly83fau691dV\n/hes7HWp7Na999VXX31lTJ8+3ZKtgoICo2PHjpU6dlW3cnNzDT8/P6O0tNQwjP88Tjp06GD6zr3O\nnz9v+Pv7G9nZ2ZZvVQfvTVTLpKamqn///hUu69atm65du6Y///xTfn5+pu41atRIy5YtU3R0tL7/\n/nt5enqaenyn4uJiORwOSVJgYKC2bdtmyY67ufv+cofy95Uk3bp1S8OGDbNk6363n1V8fX0VGhqq\n1NRUxcTEKDk5WaNHj7Z0s7S0VOPGjdPKlSvl7+9v6VZ1EYNa6H7flhuGoaKiIkv29uzZo7Zt2yoj\nI0PPPvusJRteXl5KT0+35Nj3ut/7uhiGYdnpjvsdtybeW8Ys995XX3/9tU6dOmXJltWnoO7lPFUU\nExOjzZs3a8OGDZbuLVy4UMHBwRo5cqSlO2bgZwa1zKBBg3To0KEKl2VmZqqkpEQBAQGm7/3yyy86\ncOCA0tLStGrVKv3++++mb7ibv7+/8vPzK/xg9dy5cxX+tWuW/3Z/tW3bVo8++qjpezXByqhFR0fr\n8OHDlh3/XjExMfrhhx+Unp6uO3fuWPKYcPrpp5+0fft2rVmzxrINMxGDWsbb21vdu3dXfHy8CgoK\ndPnyZcXFxVnyjAfDMDR16lQlJCSoffv2mjt3rmJjY03fcTdPT08NHDhQSUlJkqQLFy7o119/VWRk\npOlb97u/3nvvPY0YMcL0rfrIbrcrJCRECxYs0M2bNyVJP//88/8JrJl7AwcO1MSJEy35wbGT8xlS\n33zzjZo3b27ZjpmIQS20ceNG/fbbb+rZs6eeeOIJtW7dWu+//77pO4mJierUqZPr1NC0adOUmZlp\nyb/U3H06YPHixTp9+rQcDofeffddffbZZ/LwsObhXv7+ev7559WtWzfNmjXLki0nK2/P+z2byMq9\n9evX69q1awoNDVVQUJAWL16sdu3aWbY3duxYZWRkaOzYsZZtrFu3Tjdv3tSUKVMqPL00JSXFss3q\n4vcZuElVzyGnpaVp8uTJSklJUbdu3Szdqgq26sYOW3Vnx91brk1i4B719YHEVt3YYavu7Lh7y4nT\nRAAAYgAAIAYAABEDAICIAQBAxAAAIGIAABAxAACIdy11K3e+JQNbdWerPl6n+rrlrh0fHx+37JRH\nDNykqq8mrI+veqyP14mturNTn7eqg9NEAABiAAAgBgAAEQMAgIgBAEDEAAAgYgAAEDGod+x2u+Ub\nnp6ecjgceuqppzR48GCdOXPGkh0PDw+98sorrs/v3r2rVq1aaejQoaZvOa9TSEiIZs+erX/++cf0\njfISExMVGRmpHj16yOFw6OTJk5bsOK+X82PZsmWW7EjueexJ0qxZs5SQkOD6PCoqSpMnT3Z9PmfO\nHK1atcqUrfLXKTU1VY8//riys7NNOXatY6BWq+xdZLfbLd8qv7FlyxZj1KhRlu04HA6juLjYMAzD\nSE1NNXr27GkMHTrUki3DMIzS0lIjJibG+O677x54o7Jb169fN4KDg42ioiLDMAwjNzfXuHHjhiVb\n1Xk8uHvrQXe2bt3qesyVlZUZvXr1MsLDw13/PSwszDhx4oQpW87rdODAAaNz587G5cuXH+jPVWWr\npvGdAarMMAzl5OSoadOmlm0MGjRIu3fvliQlJSVp7Nixlr6as1GjRoqMjNTBgwct27hw4YL8/PzU\nrFkzSZKvr6/atGlj2V59ExYWprS0NEnS2bNnFRQUJG9vb+Xn56ukpESZmZkKCQkxbe/QoUN64403\ntHv3bgUEBJh23NqGGKDSiouL5XA4FBAQoPj4eH344YeWbY0ePVrJyckqKSlRRkaGnn76acu2JOmv\nv/7Snj171LNnT8s2IiMj9e+//6pjx46aOXOmLl26ZNmW875yfqSkpFi25S5t27ZVo0aNlJ2drbS0\nNIWFhSk0NFRpaWk6deqUgoOD1aiROe+08/fff2v48OHauXOnunbtasoxaytigErz8vJSenq6srKy\ntHbtWr300kuWbQUHBysrK0tJSUkaPHiwZTvOvzT9/f3l6elZ4WcVZrPZbPrxxx+1detWeXl5KSIi\nQqmpqZZsOe8r58fIkSMt2XG38PBwHTt2TMeOHVNYWJjCwsJ07NgxpaWlqV+/fqbtNGnSRBEREVq/\nfr1px6ytiAGqZcSIEcrMzNSdO3cs24iJiVFsbKylp4icf2lev35dOTk52rVrlyU75fXp00dLly7V\n0qVLlZSUZPlefRIREaGjR48qIyNDwcHB6tu3rysO4eHhpu14eHhoy5YtOnnypD766CPTjlsbEQNU\n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\ngg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x111717f90>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9 for 1000 swaps repeated three times\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n8kDi4QAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115dccd50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9 for 500 swaps repeated six times\n",
      "CPU times: user 3.49 s, sys: 79.1 ms, total: 3.57 s\n",
      "Wall time: 3.53 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "show_kbd(repeated_improved(qwerty, 1, 3000), \"3000 swaps repeated once\")\n",
    "show_kbd(repeated_improved(qwerty, 3, 1000), \"1000 swaps repeated three times\")\n",
    "show_kbd(repeated_improved(qwerty, 6, 500),  \"500 swaps repeated six times\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So it looks like in about a second, we can repeatedly get down from a workload average of 3.2 to about 1.9.  The resulting keyboard will not be the same each time, but will tend to have the rare letters in the corners and the common ones in the middle.\n",
    "\n",
    "What happens if we work a lot harder? Say we have a budget of 30,000 instead of 3,000?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nIEnCGEiSgP8DJ/DebTgzRUcAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115d91890>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9 for 30000 swaps repeated once\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEXBJREFUeJzt3X9M1HXgx/EXP3TKDxOdOo0fSqAZYh6mKWiMtjTz17RM\ndPNnWdNlTUozsQXoMsr81bIf/phrORR/timKZZammDVvS8U0MxK1pSAkJBrg+/tH4wZ96/tVvPch\nx/Ox3eZd6153CD7vPnCHjzHGCADQpPk29A0AADQ8YgAAIAYAAGIAABAxAACIGAAARAwAACIGAAAR\nAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAA\nIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgA\nAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAx\nAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAg\nYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAABI8m/oG9BUtGnTRiUlJR7Z8vHx\nkTGGrUaw5Y33yVu3PHmfQkJCdOXKFY9s1fAxnrp3TZw3fnGw1Xh22Go8O57eqsFhIgAAMQAAEAMA\ngIgBAEDEAAAgYgAAEDEAAIgY3HW2bdsmh8NR5+Tn56fc3FwreyUlJXr22Wd133336YEHHlC/fv20\nfft2t+/4+fnJ4XCoa9eu6tOnj9auXWvt56h9fX31yiuvuM4vXrxY6enpVrZqbN++Xb6+vjp16pSV\n6w8KCrJyvf/G19dXEyZMcJ2vqqpSu3btNHz4cLdv1Xxe9O7dW7Nnz1ZlZaXbN2oUFhYqMjLS9eLP\nkpISRUZG6ty5c1b2ioqKlJycrKioKEVHR2v+/Pmqrq62suUOxOAuM2rUKDmdTtdp+vTpeuSRRzR4\n8GAre9OmTVP79u31zTffKD8/X59++qnOnDnj9p2AgAA5nU6dPHlSCxcu1KpVq7R8+XK370hS8+bN\ntW3bNhUXF0v6+wU8tmVlZWnYsGHKysqycv2euA81AgMDdeLECV2/fl2S9Pnnnys0NNTKbaj5vDh8\n+LDy8/OtPeiRpLCwME2fPl1z586VJM2dO1fPP/+8wsPDrexNnjxZ0dHRcjqdys3N1fHjx619zruF\ngUfU50N96tQpExoaagoLC61slZeXm86dO9/27arPVlBQUJ3zW7duNR07drS29dZbb5nU1FRjjDGL\nFy82aWlpVraMMaasrMxERESYX3/91dx///1Wdv758auP29lKTU01mzdvNsYYM2HCBJOZmWmGDRtm\nZavG4sWLzauvvnrLG7e7ZYwxlZWVpmfPnmbp0qWmR48epqqqysrO1atX/9fXVn5+vklISHD7lrvw\nzOAuVVlZqfHjx2vJkiUKDQ21srFz504NGDDAynX/fx577DGVlJSovLzcyvXPmDFD69ev19WrV61c\nf22fffaZHn/8cYWHh6tdu3Y6evSo9U3bxo4dqw0bNujGjRs6duyYHn74Yat7paWl2rFjh7VnwDX8\n/f319ttvKyUlRcuWLZOfn5+VnZycHA0cOLDOZd27d9f58+d16dIlK5t3ihjcpV5//XXFxsZqzJgx\n1jb++bT/hRdeUK9evdS3b19rmzWMMTLGWDv8ERwcrIkTJ2rFihVWrr+2rKws19/TmDFjrB0q8qTY\n2FgVFBQoKytLQ4cOtbZTUVEhh8OhkSNHavjw4UpMTLS2VWPXrl3q1KmTjh07ZnXn3z63jTH6888/\nre7Wm8efizRRt/Oh3rdvn+nataspLy+3ulVWVmbCw8PNzZs3XZcVFRXd1qGjW93652GOzZs3m3vv\nvfeWd+qzdeXKFdO5c2eTnp5u7TBRcXGxCQgIMBEREaZz584mLCzMhIeHu33H04eJjDEmIyPDtG3b\n1hw/ftzs27fP+mGi+rqdry2n02liYmLMuXPnTHh4uPntt9+s7PzXYaIOHTq4fctdeGZwlykpKdGU\nKVP0ySefKDAw0OpWUFCQHnroIaWmpurixYuSZP1RS3V1tb744gstWbJEs2fPtroVEhKip59+WmvW\nrLH2DGTz5s2aOHGiCgoK9Msvv+jcuXPq0qWLDhw4YGXPk6ZOnaq0tDTFxMQ09E1xC2OMpk+fruXL\nlyssLEyzZ8+u81Nn7hQcHKyYmBilpaWprKxMZ8+eVWpqqmbMmGFlzy08np8m6lY/1G+++aYJDAw0\nvXr1qnPKzs52+5Yxfz96njp1quncubPp06ePSUpKsrLl5+dnevXqZaKjo03v3r3NmjVr6jwjcedW\ncHCw68+///67CQgIMOnp6Va2kpKSTG5ubp3LVqxYYWbMmOHWHV9fXxMaGuo6LV269Jb+v/ps1f74\n1fjqq6/M8OHDPbJ1u25166OPPjLJycmu89XV1SYuLs7s37/frTs1Ll++bMaOHWsiIyNNs2bNzPTp\n02/5/22If5r5fQYe4q3vhc5W49hhq2F38vLyNG3aNG3atEndu3e3ulVfxMBDvPGLg63Gs8NW49nx\n9FYNvmcAACAGAABiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgCT/hr4BTYknf0EJW41nyxvvk7dueWon\nJCTEIzu1EQMPqe+rCb3xVY/eeJ/Yajw73rx1JzhMBAAgBgAAYgAAEDEAAIgYAABEDAAAIgYAABED\nrxEUFOT6c05Ojrp166bCwkIrW7NmzdLy5ctd5wcPHqxp06a5zr/88staunTpHe/Uvk+2+fn5yeFw\nuE5vv/229a24uDilpKTor7/+srYlSUVFRUpOTlZUVJSio6M1f/58VVdXu32noKBAsbGxdS5LS0vT\nu+++69YdY4wGDhyo3bt3uy7btGmThgwZ4tad2jz5udhQiIGXqHll5N69e/XSSy9p9+7dCgsLs7I1\nYMAAHTp0SJJ08+ZNFRcXKz8/3/Xf8/LylJCQcMc7nnwFa0BAgJxOp+s0Z84c61tHjhzRzz//rD17\n9ljbkqTJkycrOjpaTqdTubm5On78eJ2Y22Tj79DHx0cffvihUlJSdOPGDZWXlys1NVUrV650+1bt\nTW9HDLzI/v379dxzz2nnzp3q0qWLtZ3+/fsrLy9PknTixAn16NFDwcHBKi0t1Y0bN3Ty5EnFxcVZ\n2/cW/v7+SkxM1L59+6xtlJWV6cSJE1qwYIGCg4MVGRmpRYsWaevWrdY2PSEmJkbDhw9XZmamMjIy\nNGnSJKuf800Bb0fhJa5fv65Ro0bp66+/VteuXa1uderUSf7+/iosLFReXp769++vCxcuKC8vT61a\ntVJsbKz8/RvXp1ZFRYUcDofr/Lx58zRmzBirm3/88Yd27dqliRMnWtvIycnRwIED61zWvXt3nT9/\nXpcuXVL79u2tbdv2xhtvyOFwqEWLFvr+++8b+uY0eo3rKxb/qXnz5kpISNDq1au1bNky63vx8fE6\ndOiQDh06pJSUFF24cEGHDh3SPffcowEDBljfd7eWLVvK6XR6ZKsmPGfOnFFCQoImTJhgde/fDnHY\n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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115d91410>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9 for 2000 swaps repeated fifteen times\n"
     ]
    },
    {
     "data": {
      "image/png": "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\nT58+2rdvX+k//Pfee0+7d+/WxYsXbdmbPHmyVq9ercLCQluu35v17dtX+/btK3PZ8ePH1bRpUzVu\n3NjSreDgYF2+fLnME7rHjh2T0+m0dKd27dpyuVzau3evpk6dKl9fe7+M/fTTT9q1a5cyMzO1dOlS\n/f7777buVRRigHJzOBx65JFHNHv2bJ0/f16SbP1CHRQUpCFDhmj16tUefaLXGwQGBio8PFzz5s3T\n1atXdebMGb3yyisaOHCg5Vt+fn7q3r27kpKSJEmnTp3SoUOHPPKdu12MMRo/fryWLVumkJAQzZgx\ng+cMUPkVFRWpXr16HtlatWqV/vjjD8XFxSk6OlqjR4/WokWLLN24/Qv/tGnTlJeXZ+n1/68tT7N7\ne+3atTpx4oQiIyPVq1cvtWvXTlOmTLFla/78+crKypLT6dTLL7+s999/3/Lv3D35uVq5cqVatmyp\nnj17Svrvc1jHjx/X/v37PfY+eIqPqQovtF2NlefYbnp6uj766KPS78zs3LoX3vpa8mxVjR1v3roX\n/HIbL/HBBx9o06ZNWrBgQUW/KwCqIB4ZVHLe+B2MN94mtqrOjjdv3QueMwAAEAMAADEAAIgYAABE\nDAAAIgYAABEDAIA46axK8OTp957a8sbbxFbV2fHkVlBQkEd27hUxqOQ8ebIKJxexVZFb3nibqhIO\nEwEAiAEAgBgAAEQMAAAiBgAAEQMAgIgBAEDEAHehR48e2rlzZ5nLEhMTNWHCBEt3/Pz85HQ61aZN\nGz366KNas2aNbT8b7nA4yry9du1aTZo0yZYt9+1y/7H6d0ffaSsqKkpTp07V33//bduWr6+vnn32\n2dK3i4uL1bBhQ/Xr18/SHWOMunbtqh07dpRelpKSoj59+li6U91w0hnKbfjw4UpOTlavXr1KL1u/\nfr3efvttS3cCAgLkcrlUUlKiXbt2ad68ebpy5YomT55s6Y70/89GtfPsVPft8gT3VnFxsQYNGqSd\nO3fqySeftGWrTp06Onr0qG7cuKFatWrpq6++UnBwsOUfSx8fH61YsUKDBw9W9+7ddfPmTc2ePVtp\naWmW7lQ3PDJAuQ0aNEjbtm1TcXGxJCk7O1vnz59Xly5dbNnz8/NT7969NXPmTFu/i76dt52d6u/v\nr27duik9Pd3Wnb59+2rbtm2SpKSkJA0fPtyWj2V4eLj69eunt956S/Pnz9eoUaPUqlUry3eqE2KA\ncqtfv76io6OVmpoqSUpOTtbQoUNt33388cdVUFCga9euWX7dRUVFZQ7dzJ0717ZHB//cSklJsWXn\ndn/++ae2b9+uyMhIW3eGDh2q5ORk/fXXXzp8+LAee+wx27bmzp2rdevWKS0tTTNnzrRtp7rgMBHu\nivtQUf/+/bV+/XqtWbPG9k1jjIwxtnyRrl27dplDN5988ol++OEHy3futGUnd3hOnz6tuLi4Msf0\n7RAREaHs7GwlJSUpPj7e1q2AgAANGzZMgYGBqlGjhq1b1QGPDHBX+vfvr927d8vlcun69etyOp22\nb+7cuVP333+/6tSpY/uWtxwmcocnNzdXeXl52rp1q+2b/fv31/Tp0207RHQ7X19fj77SqTfjkQHu\nisPhUPfu3TVmzBiNGDHC1q2SkhKlp6dryZIlmjFjhq1b3qpu3bpauXKlnn76acXHx9v6BfS5555T\nUFCQwsPDtWfPHtt2YC1igLs2fPhwDRw4UBs2bLDl+t2HOAoLC1W3bl1NmDBBY8aMsWXrTj9NZPdz\nBm59+vTRwoULbdm6/TY4nU6FhYVpw4YNtjzH495q1qyZJk6cWHqZ3d+588jAGj7GWx4P457xuvVs\nVeSWN96mqoTnDAAAxAAAQAwAACIGAAARAwCAiAEAQMQAACBiAAAQZyDjHzx1Nqcnzxplq+pseWon\nKCjIIztVCTFAKc7IBKovDhMBAIgBAIAYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAA\nRAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEA\nAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBi\nAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBA\nxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMA\ngIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIG\nAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAACT9H9Cz7T/H81MQAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115db5790>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9 for 500 swaps repeated sixty times\n",
      "CPU times: user 29 s, sys: 314 ms, total: 29.3 s\n",
      "Wall time: 29.1 s\n"
     ]
    }
   ],
   "source": [
    "%%time \n",
    "show_kbd(repeated_improved(qwerty, 1, 30000), \"30000 swaps repeated once\")\n",
    "show_kbd(repeated_improved(qwerty, 15, 2000), \"2000 swaps repeated fifteen times\")\n",
    "show_kbd(repeated_improved(qwerty, 60, 500),  \"500 swaps repeated sixty times\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It looks like spending more time doesn't help.  I can't say we've found the best possible keyboard, but I can say it will take either a very long run time or a more clever algorithm to do much better than 1.9. I can also say that it doesn't seem to matter too much exactly how you manage the budget between repetitions and swaps.\n",
    "\n",
    "Keys in Different Locations\n",
    "---\n",
    "\n",
    "Now let's allow keys  to be in different physical locations.  Rather than allowing complete freedom of movement, we'll start from a few different fixed key layouts and swap keys from there. I'll define three layouts and gather them into a `dict`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "keyboards = {\n",
    "    'qwerty': Keyboard(('Q W E R T Y U I O P',\n",
    "                        ' A S D F G H J K L ',  \n",
    "                        '   Z X C V B N M   ')),\n",
    "\n",
    "    '4-by-7': Keyboard((' A B C D E F ',\n",
    "                        'G H I J K L M',\n",
    "                        ' N O P Q R S ',\n",
    "                        'T U V W X Y Z')),\n",
    "\n",
    "    '5-by-6': Keyboard((' A B C D E ',\n",
    "                        ' F G H I J ',\n",
    "                        'K L M N O P',\n",
    "                        ' Q R S T U ',\n",
    "                        ' V W X Y Z '))\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nhvwfGyro05uKM6QAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x111705710>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.7 for 4-by-7\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1117f4cd0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 3.2 for qwerty\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAEACAYAAABRQBpkAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEoRJREFUeJzt3XtMlfUDx/HP4aATBH+Bo9SgksQixHw0DSVH6crUqcvy\numnS0hlZJmIXtaJyWSxvXVZ5m7U5MPG2KV7KLLygZhwn3lIz8janyLFA0RSf3x8OArNCJL/P8bxf\nG1ucNv3sMHlzvoc9j8u2bVsAAL8WYHoAAMA8YgAAIAYAAGIAABAxAACIGAAARAwAACIGAAARAwCA\niAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYA\nABEDAICIAQBAxAAAIGIAABAxAABICjQ9ALUXHh4ur9drekY1LpdLtm2bnvEXTtzFppoJCwtTcXGx\n6Rk3PZfttK88asyJ/3CduEly5i421YwTN92MOCYCABADAAAxAACIGAAARAwAACIGAAARAwCAiAEA\nQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQO/tnTpUgUEBOinn34yPUVut1uWZen+++9Xz549tXPn\nTtOTKnm9XiUnJys6OlpxcXHq2bOn9u/fb2xPxXPVsmVLtW/fXnPnzjV+ieeKTRUfGRkZRvfg2nE/\nAx92vdd5HzBggMrKytS2bVulp6cb3RQaGqqSkhJJ0sKFC5Wdna0FCxbUyabr2SVJffv21T333KPU\n1FRFRERo8+bNOn/+vJKSkoxsqniuysvL9c033yg9PV0DBgzQSy+9dF176mLTf4H7GdwY3OnMT5WW\nlmrLli3Kzc1Vt27d6iwG18u2bRUVFalBgwamp0i6/Dzl5+dr8eLFlY8lJCQYXPQnt9utbt266ezZ\ns3r++efrJAbwX8TATy1btkyPP/647rjjDkVERCg/P19t27Y1tqesrEyWZcnr9aqsrEz5+fnGtlSV\nk5Ojzp07m57xjx599FF5vV6VlpYqJCTEyIaKr1+F8ePHq1+/fka2oHaIgZ/KzMzUmDFjJEn9+vVT\nZmam0RgEBQXJ4/FIkhYtWqSnnnpKeXl5xvZUcLlcpif8K9u2Zdu20a1Vv37wTbxn4MNqe5ZaXFys\nqKgoRUREyOVyqby8XC6XS7/++quxTVXPnG3bVlhYmI4dO6bg4ODr3nQ9u0pLS9WqVSsVFhbWyY66\n2HTl+fyiRYs0evRoHTlyxDGb6hLvGdwY/DaRH8rOztbQoUNVWFioX375RYcOHVLz5s21fv1609Mk\nSRs3blRMTEydheB6hISEqG3btpo4caJOnjwpSfrhhx+Um5treJkq30CeOnWqxo0bZ3oOfBzHRH4o\nKytLr776arXHnnzySWVlZRk7H684c7506ZLuvPNOTZ061ciOq5k9e7ZSU1PVoUMHNWzYUM2bN9f0\n6dON7al4rs6cOaNGjRopJSVFycnJxvZU3VShe/fuevfddw0uwrXimMiHOfHlsxM3Sc7cxaaaceKm\nmxHHRAAAYgAAIAYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAuFCdT/OFG68A\n18vlcunSpUumZ9z0uIS1j3Nay516hUkn7mJTzfBDz43BMREAgBgAAIgBAEDEAAAgYgAAEDEAAIgY\nAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGPglt9sty7IqPw4dOmR6kiTJ6/Xq2Wef1d13\n36377rtPCQkJWrp0qdFNISEh1T6fN2+eXnjhBUNrLrtyk5M4eRv+Gfcz8EPBwcHyeDymZ/zF8OHD\n1bJlS23YsEFNmzbVgQMHjMfgymvpO+Ha+k7Y8HecvA3/jBjAEc6cOaMff/xR2dnZlY+1aNFCaWlp\nBlf9ldNu/ALUFWLgh8rKymRZliQpOjpaixYtMrxIWrFihR566CHTM/6i6nMlScXFxerTp4/BRcB/\ngxj4oaCgIMcdE115vDBq1Cht2LBB9evX19atWw2t+utz9cUXX2jbtm3G9gD/Fd5AhiN0795dubm5\nlccwH3/8sdauXauTJ08aXlYdx0S4WREDOEJISIgeeOABTZgwQceOHZN0+X0EADcGx0R+yKm/8TF7\n9mylpaUpMTFRERERCgkJUUZGhtFNV/ttItPPn+m//++UlZXplltuMT0DteSyed3rs1wul+OOLZy4\nSXLmrptt07p16zRz5kxlZmY6ZhNqjlcGAK7bp59+qkWLFmnSpEmmp6CWeGXgw5z4E5MTN0nO3MWm\nmnHippsRbyADAIgBAIAYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAAxLWJjAgPD5fX\n6zU9A/A7YWFhKi4uNj3DkYiBAU688BabaoZNNefEXU7c5BQcEwEAiAEAgBgAAEQMAAAiBgAAEQMA\ngIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDnxISElL53zk5Obrnnnt0+PBhg4uqb3KKgIAA\nDRkypPLzixcvKiIiQr169TK6KS0trfLzDz74QG+99ZaxPRWKioo0cOBAtWjRQjExMZo4caLKy8uN\nbnK73bIsS+3atdO4ceN04cIFo3v8BTHwIS6XS5K0du1ajR49WqtWrVJUVJQjNjlJw4YNtWvXLp07\nd06S9PXXXysyMtLo1vr162vJkiU6deqUJOc8b8OGDVNMTIw8Ho9Wr16tnTt3asaMGUY3BQcHy+Px\naPPmzdq9e7dWr15tdI+/IAY+Jjc3VyNGjNCKFSvUvHlz03Mcq0ePHlqxYoUkKTMzU4MGDTJ6Hft6\n9eppxIgRmjZtmrENVyopKdGuXbv0zjvvKDQ0VNHR0Zo8ebIWL15sepqky89Zly5dtGHDBtNT/AIx\n8CHnzp3TE088oWXLlqlly5am5zjagAEDlJWVpfPnz6ugoEAPPvig6UlKSUnR/Pnz9fvvv5ueIuny\nUWPnzp2rPRYbG6sjR47oxIkThlb96fTp01q+fLm6detmeopfIAY+pH79+kpMTNTs2bNNT3G8+Ph4\nFRYWKjMzUz179jQ9R5IUGhqqoUOH6sMPPzQ9pdLVjqtM3w2srKxMlmWpT58+6tWrl5KSkoxt8SfE\nwIcEBAToq6++0tatWzV58mTTcxyvd+/eSktLM35EVNVLL72kOXPm6MyZM6anqEePHsrNza322J49\ne9SsWTPddttthlZJQUFB8ng8+v7775WamqqAAL5N3Qg8yz6mQYMGWrFihebPn6+5c+eanuNozzzz\njNLT0xUXF2d6SqWwsDD1799fc+bMMf4mcmhoqOLi4pSenq6SkhIdPHhQ48ePV9++fY3ughnEwIdU\nfPMICwvTqlWrNGnSJC1fvtzoprNnzyoqKqryY/r06Ub3SH8+T7fffrtGjRpV+ZjJb75V/+6xY8eq\nqKjI2Jaq5s2bp71796pNmzZ67LHHFBsbqzFjxhjdZDqS/splO+X1sx8xfSZ7NWyqGTbVnBN3OXGT\nU/DKAABADAAAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAASAo0PcBf\nOfEyvWyqGTbVnNN2hYWFmZ7gWMTAgLq6hK4TL8frxE2SM3exqWacuOlmxDERAIAYAACIAQBAxAAA\nIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBj4raKiIg0cOFAtWrRQTEyM\nJk6cqPLycmN73G63LMtS27ZtlZqaqj/++MPYlivNmjVLSUlJat26tSzL0tatW43uOXXqlCzLkmVZ\natq0qSIjIyufuwsXLhjZVFhYqPj4+GqPpaena8qUKUb24NoRAz81bNgwxcTEyOPxaPXq1dq5c6dm\nzJhhbE9wcLA8Ho+2bt2qn3/+WWvWrDG2papjx47po48+0sqVK7Vjxw6tXbtWUVFRRjc1btxYHo9H\nHo9HI0eOVGpqqjwej/Lz81WvXj2j26py2o1t8M+IgR8qKSnRrl279M477yg0NFTR0dGaPHmyFi9e\nbHqaAgMDlZSUpHXr1pmeIknat2+fbr31VgUHB0uSwsPD1bRpU8OrquPGL6gLxMAP5eTkqHPnztUe\ni42N1ZEjR3TixAlDqy777bfftHLlSrVp08bojgpJSUm6dOmS7rzzTr344os6cOCA6UnAf4IY+Kmr\nvYS3bVtnzpwxsEYqKyuTZVmKjIyU2+3WkCFDjOy4ksvl0rfffqvs7GwFBQUpMTFROTk5pmc5ztVu\nTWnbNkdFPoQY+KEePXooNze32mN79uzR+fPn1bx5cyObgoKC5PF4dPToURUVFWn58uVGdvyd9u3b\n6/3339f777+vzMxM03McJzIyUqdPn672Bvbu3btlWZbBVbgWxMAPhYaGKi4uTunp6SopKdHBgwc1\nYcIEpaSkmJ6mRo0aadasWXr55ZcdcRa+b98+7d+/X5J08eJFbd68WZ06dTK8ynncbrceeeSRylDu\n27dPO3bsUFJSkuFlqCli4KfmzZunvXv3qk2bNrr33nvVpEkTvfHGG8b2VD1OsCxLLVq00FdffWVs\nT4XS0lINGzZMcXFxSkxMVIMGDfT000+bnlWNU45i3n77beXn58uyLL322mv65JNPFBDAtxhf4bKd\n8OMXauVq57S1kZeXp+HDh2vhwoWKjY11xKa65sRdbKoZJ266GREDH+bEfyRO3CQ5cxebasaJm25G\nvIYDABADAAAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiGsT+TSnXK0S+C+5XC5d\nunTJ9IybXqDpAbg+Tmu5Uy8q5sRdbKoZfui5MTgmAgAQAwAAMQAAiBgAAEQMAAAiBgAAEQMAgIgB\nAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDv9SlSxetWbOm2mPTp09XSkqKoUXSmDFjNGPGjMrP\nu3XrpuHDh1d+PnbsWE2bNu2G7zp8+LCio6Pl9XolSV6vV9HR0Tp06NAN31LBtm117txZq1atqnxs\n4cKF6t69u7FNS5YskWVZ1T7cbrdWr15tbBOukQ2fVdsv38yZM+3k5ORqjyUkJNjr1683tik7O9vu\n37+/bdu2XV5ebrdr187u1KlT5f/v2LGjvWXLlhu+y7ZtOyMjwx4xYoRt27Y9YsQI+7333qv1n1VX\nm3bu3GnHxsba586ds0tKSuyYmBj74MGDRjdV9fnnn9sPP/xwnfxZfJu6MbjTmQ+r7Y1IiouLFRsb\nq6NHjyowMFCFhYVKSkrSr7/+amzTsWPHlJCQoEOHDqmgoEBTpkzR8ePHlZWVpaCgIDVp0kQnT55U\nYGDt7sd0PTdtuXjxotq1a6fk5GTNmTNH27dvl9vtrtWfVVebJOmVV15Rw4YNVVpaqv/973+aMGGC\n8U2StG/fPnXt2lV5eXmKjIx0xCb8O+505ofCw8PVoUMH5eTkqHfv3srKytKAAQOMbmrWrJkCAwN1\n+PBh5eXlqWPHjjp69Kjy8vLUqFEjxcfH1zoE1yswMFAZGRnq3r27vv766zoJQV148803ZVmWGjRo\noG3btpmeI0m6cOGCBg8erKlTp9ZJCHDj8J6Bnxo0aJCysrIkSQsWLNCgQYMML5I6deqkTZs2adOm\nTerYsaM6duyoTZs2KS8vTw899JDRbStXrlSzZs1UUFBgdEdVwcHBGjhwoIYMGaJ69eqZniNJev31\n1xUfH69+/fqZnoJrRAz8VO/evbV27Vp5PB6dPXtWlmWZnqTExERt3LhRBQUFio+PV0JCQmUcOnXq\nZGzX9u3b9c033ygvL0/Tpk3T8ePHjW25UkBAgGPuEfzdd99pyZIl+vjjj01PQS0QAz8VEhKiRx55\nRMnJyRo8eLDpOZIuvzJYvny5GjduLJfLpbCwMJ0+fVp5eXnGYmDbtp577jnNmDFDUVFRGjdunNLS\n0oxscTKv16vk5GR9+eWXatiwoek5qAVi4McGDRqkgoICRxwRSVKrVq106tQpJSQkVD7WunVr3XLL\nLQoPDzeyadasWbrrrrvUtWtXSVJKSor27Nmj9evXG9lzNU54ZfDZZ5/p5MmTGjlyZLVfL124cKHp\naaghfpvIhznxtyycuEly5i421YwTN92MeGUAACAGAABiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgB\nAEDEAAAgYgAAEDEAAIg7nfk8J1yx8kpO3CQ5cxeb/l1YWJjpCX6BGPgwruQIoK5wTAQAIAYAAGIA\nABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDE\nAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCA\niAEAQMQAACBiAAAQMQAAiBgAAEQMAACS/g8kdJfSMWWqigAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1117e9d10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.5 for 5-by-6\n"
     ]
    }
   ],
   "source": [
    "# Compare keyboards\n",
    "for name in keyboards:\n",
    "    show_kbd(keyboards[name], name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(**Note:** The plots of different-shaped keyboards have different-sized squares around the keys. Some of the plots have a lot of whitespace around them. If anyone knows an easy way to tell `plot` to display them better, let me know.)\n",
    "\n",
    "And here is a function to iterate over these layouts, applying `repeated_improved` to each one and displaying the improved keyboard, the workload average before and after improvement, and the time taken:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def report(keyboards, scorer=workload_average):\n",
    "    \"Iterate through a dict of {name: kbd} pairs, showing kbd before and after repeated_improved(kbd).\"\n",
    "    for (name, kbd) in keyboards.items():\n",
    "        show_kbd(repeated_improved(kbd, scorer=scorer), \n",
    "                 'repeated improved ' + name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115db5150>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.8 for repeated improved 4-by-7\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEVRJREFUeJzt3XlQ1PXjx/EXh47CoqLBeKFCYhGiLpYpaKRNmfeYeeDk\nQSVjjB3iMZNHkThajGfZ6ZEdDt7HJHhrgwpqBaUipmaM1zSKYiJ5+/n+0Q9GrO/8vujnvcD6fMw4\n027jvnYReLIf+CwelmVZAgA80Dwr+g4AACoeMQAAEAMAADEAAIgYAABEDAAAIgYAABEDAICIAQBA\nxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMA\ngIgBAEDEAAAgYgAAEDEAAIgYAABEDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIG\nAAARAwCAiAEAQMQAACBiAAAQMQAAiBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAABE\nDAAAIgYAABEDAICIAQBAxAAAIGIAABAxAACIGAAARAwAACIGAAARAwCAiAEAQMQAACBiAAAQMQAA\niBgAAEQMAAAiBgAAEQMAgIgBAEDEAAAgYgAAEDEAAIgYAAAkeVf0HXhQ1K1bV4WFhS7Z8vDwkGVZ\nbFWBLXd8TO665crH5O/vrwsXLrhkq4SH5apH94Bzxw8OtqrODltVZ8fVWyU4TAQAIAYAAGIAABAx\nAACIGAAARAwAACIGAAARg0pnzZo1cjqdZf54eXlp06ZNtm+dPHlSISEhpSfDFRYWKiQkRCdOnLB9\na/To0Zo7d27p5a5du2rEiBGll8eMGaPZs2fbsuVwOMpcXrx4sV5//XVbbvtuXl5eZf6tUlJSjO5E\nRkYqMTFR169fN7Ij/fPtZ9Ldbz8T73slLMtSp06dtHHjxtLrVqxYoW7duhnZKygo0KBBg9S8eXOF\nhoZq0qRJunXrlpEtW1hwiXt9U3/++efW008/bWwrJSXFio+PtyzLsuLj463333/fyNbKlSutAQMG\nWJZlWbdu3bLatm1rRUVFlf7/Dh06WHv37rVly+FwlLm8ePFia9SoUf/T373frfIq786NGzes3r17\nW999953xrftRGbcsy7IOHjxohYWFWVevXrWKioqs0NBQ6/jx47bvWJZl9ejRw5o0aZJ16dIl67ff\nfrP69OljzZw508iWHXhmUIkdOXJEycnJ+uabb4xtjB49Wnv27NGcOXOUmZmpsWPHGtnp0KGDsrKy\nJEm5ublq2bKl/Pz8dPHiRV27dk15eXmKjIw0sm250Un23t7eiomJ0Y4dOyr6rlRJ4eHh6tWrlz74\n4ANNmTJFw4YNU3BwsO07RUVFys3NVXJysvz8/BQSEqLp06dr9erVtm/ZhdcmqqRu3LihwYMHa9as\nWWrcuLGxHW9vb6WkpKhbt27asmWLvLy8jOw0bNhQ3t7eOnnypLKystShQwedPn1aWVlZqlWrliIi\nIuTtbc+745UrV+R0OksvX7hwQX369LHltv+/rQkTJqh///5GtiTpzz//1IYNGzR06FBjG65059sv\nJCREq1atMr757rvvyul0qkaNGvrxxx+NbKSnp6tTp05lrgsLC9OpU6d09uxZBQYGGtm9H8Sgkpo8\nebIiIiKMfmIpsWHDBjVs2FAHDhzQM888Y2wnKipKmZmZyszMVGJiok6fPq3MzEzVrl1bHTt2tG2n\nZs2aysnJKb381VdfGfugv3vLlJJPmseOHVN0dLSGDBlifNMVXPX2u5OPj48GDRokPz8/VatWzdiO\nh4fHP66zLEvFxcXGNu8Hh4kqoe+//15r1qzRvHnzjG/9/PPP2rp1q7KysjR79mz98ccfxraio6O1\ne/duHThwQBEREWrfvn1pHKKiooztusNhopJPmqdPn1ZBQYHWr19f0XepSvP09PzXT9Z26d69uzIy\nMspcl5eXp2vXrhk5LGUHYlDJFBYWKi4uTl9//bV8fX2NblmWpddee01z585VUFCQxo0bZ+x7BtLf\nzwzWr1+vevXqycPDQ/7+/rp48aKysrKMxsCd1KpVS/Pnz9f48ePdInLuys/PT+Hh4UpKSlJRUZGO\nHz+uiRMnKiEhoaLv2n9FDCqZzz77TOfOndPIkSPL/MjdihUrbN+aP3++mjVrVnpoKCEhQXl5edq5\nc6ftW5LUsmVLnT9/Xu3bty+9rlWrVqpTp47q1q1r287dX/F5eHgY+yqw5PBNyZ8JEyYY2bnz/jud\nTjVv3lzLly83svXXX38pKCio9M+cOXOM7Ej/fijFVUxvL168WIcPH1abNm306KOPqn79+nrnnXeM\nbt4Pfp+Bi7jra6GzVTV22KrYnaysLI0YMUIrVqxQWFiY0a17RQxcxB0/ONiqOjtsVZ0dV2+V4DAR\nAIAYAACIAQBAxAAAIGIAABAxAACIGAAARAwAAOJVS13Klafes1V1ttzxMbnrlqt2/P39XbJzJ2Lg\nIvd6NqE7nvXojo+Jraqz485b94PDRAAAYgAAIAYAABEDAICIAQBAxAAAIGIAABAxcDsOh8P4Rpcu\nXbR58+Yy182ZM8fIL/ueP3++YmJi1KpVKzmdTu3bt8/2DUk6f/586e8xbtCggRo3biyn06nIyEjd\nuHHDyObatWvl6empX3/91cjtlygoKNCgQYPUvHlzhYaGatKkSbp165btO/n5+YqIiChzXVJSkmbO\nnGnrjpeXl5xOp9q2batx48YZ+/e5c6t169bq0aOHDh48aGyrohEDN+OKMyRjY2O1dOnSMtctW7ZM\ngwcPtnXnzJkz+uijj7Rhwwbt379f27ZtU1BQkK0bJerVq6ecnBzl5ORo5MiRSkxMVE5OjrKzs1Wt\nWjUjm6mpqerZs6dSU1ON3H6J4cOHKzQ0VDk5Odq0aZMOHjyouXPnGt0sYeL90cfHRzk5OdqzZ48O\nHTqkTZs22b5x99Yvv/yi4cOHKzk52dhWRSMGKLd+/fopLS1NN2/elPT3V4RnzpxRx44dbd05cuSI\nAgMD5ePjI0mqW7euGjRoYOvGf2P6jNHLly9r7969mjdvnpYtW2Zsp6ioSLm5uUpOTpafn59CQkI0\nffp0rV692timq1SrVk1dunTRrl27jG9ZlqWCggLVqFHD+FZFIQYot7p166pdu3ZKT0+XJC1dulQD\nBw60fScmJka3b99W06ZN9cYbb+jYsWO2b1SUdevW6fnnn1eTJk0UEBCg7OxsIzvp6enq1KlTmevC\nwsJ06tQpnT171simq1y8eFHr169X165djW1cuXJFTqdTwcHBSkpK0rRp04xtVTRigHty56GiZcuW\nKTY21vYNDw8Pbd++XStXrlTNmjUVHR1dGqCqLjU1Vf3795ck9e/f3+ihon87VGPi9XL+7TYty7L9\nUFHJJ+g+ffqoV69eiomJsfX271SzZk3l5OQoPz9fn3zyiV588UVjWxXOQqVW3n8ih8Phkq2ioiIr\nMDDQys7Otlq0aGFs505ffvml9dJLLxnfSkpKsmbMmFHuv/e/bp0/f97y8fGxmjZtajVr1swKCgqy\nmjRpYmTr0qVLVrNmzcpcd+jQISsqKsr2rZs3b1qNGjWyrl+/Xnpd//79re3bt9u6cz/v4/ezdfv2\n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\nEDEAAEj6D3zChEhl1aMJAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x111709790>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9 for repeated improved qwerty\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x111707050>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.7 for repeated improved 5-by-6\n",
      "CPU times: user 19.3 s, sys: 221 ms, total: 19.5 s\n",
      "Wall time: 19.4 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "report(keyboards)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "So, any keyboard can be improved, but the more compact keyboards with a smaller diameter (`4-by-7` and `5-by-6`) perform slightly better than `qwerty`. \n",
    "\n",
    "But ... I still want to know: how close are we coming to the optimal keyboard?  Can we do better than about 1.7 or 1.8?  \n",
    "\n",
    "One thing to try is to start from a better position.  It is easier to do hillclimbing of Mt. Everest if you start at Everest Base Camp (elevation 17,000 ft) rather than at sea level.  Let's see what we can do."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Better Starting Keyboard\n",
    "---\n",
    "\n",
    "What if we started with a keyboard where the most common letters (as measured by the workload text) are in the center?  Then, on average, many of the common segments would tend to be short.  To make this a little more precise:\n",
    "\n",
    "1. Sort the letters of the alphabet, most frequent first.\n",
    "2. Find the center of the keyboard.\n",
    "3. Sort the key locations, closest to center first.\n",
    "4. Make a new keyboard where the most frequent letter is in the center, the next most frequent is the next closest to center, and so on.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "cat = ''.join    ## Function to join strings together into one big string\n",
    "\n",
    "# Order letters by frequency in TEXT\n",
    "ordered_letters = cat(sorted(list(qwerty), reverse=True, key=lambda L: TEXT.count(L)))\n",
    "\n",
    "def recentered(kbd):\n",
    "    \"Put the most frequent letters in the center of this keyboard.\"\n",
    "    center = mean(kbd.values())\n",
    "    ordered_locations = sorted(kbd.values(), key=lambda point: abs(point-center))\n",
    "    return dict(zip(ordered_letters, ordered_locations))\n",
    "\n",
    "def mean(numbers): return sum(numbers) / len(numbers)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'ETAONISRHDLCUMFWYGPBVKXJQZ'"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ordered_letters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nadOmmbpx6/WfPn26zp8/b+rxb+V4zCUkJERvvPGGxo8fr6lTp1q2FxoaqtLSUvXt27f6su7du6tV\nq1by8/MzfS8xMVGHDh1SYmKi6cd2+Oyzz3Tu3Dm9+uqrNZ5eunbtWss264qfZ+Ak93ofcl5eniZM\nmKC1a9cqJCTE0q17wVbj2GGr8ew4e6t6kxg4h6t+IrHVOHbYajw7zt5y4G4iAAAxAAAQAwCAiAEA\nQMQAACBiAAAQMQAAiBgAAMS7ljpVfb4lA1sNd8sVz8lVt5y14+vr65SdWxEDJ7nXVxO64qseXfGc\n2Go8O668VRfcTQQAIAYAAGIAABAxAACIGAAARAwAACIGAAARA5dRUlKi4OBglZWVSfrnB3IHBwfr\n1KlTluyVlZXp5Zdf1iOPPKLHHntMffv21caNG03dsNlsph7vv5SWllb/nFp/f38FBATIbrcrPDxc\nVVVVpm55eHjU+Lm48+fPN/X4t7Nx40a5u7vr6NGjlu44fhB8cHCwunXrpsGDB+v48eOmbhiGof79\n+2vLli3Vl61du1axsbGm7tzKmZ+L9YUfe9nA1eYFKwsWLNCJEyf0+eef65VXXlFwcLBmzpxpydaI\nESPUpUsXTZkyRf7+/jpx4oQ2btyo5ORk03Z8fHx06dKlu7o+dd261Zw5c+Tj46OkpCRLturjvBIS\nElRZWanw8HClpaVZtjV8+HB17dpVSUlJat26tfbt26dr164pOjra1J0jR44oPj5eBQUFqqqqUnh4\nuHJychQUFHRXv7+2f351uc0ay4vOiEEDV5tPpOvXr6tnz54aN26cVqxYoQMHDsjDw8P0rcuXLys0\nNFQnT56862Pfy059xsBms2n69OmWbDn7vCoqKhQaGqrdu3crJiZGRUVFlmw5doqLi2t1/NruOMyc\nOVPNmzdXRUWFWrZsqdTUVMu27ocY8HYULsTT01Pz589XbGystm3bVqsQ1MamTZsUFRVlybHvB5WV\nlbLb7dUfz5o1S/Hx8Zbtfffdd3r22WfVoUMHtW7dWj///LPCw8NN38nOzlb//v1NP+6dvP3227Lb\n7WrWrJn279/vtF1XRQxczObNm9WuXTsdOnRITz31lCUb/36zrsmTJ2vPnj1q2rSp8vPzLdl0JV5e\nXiooKHDaXkZGhqZNmyZJio+PV0ZGhiUxcOYb00mSt7e3Ro4cKR8fHzVp0sSp266IGLiQAwcOaPv2\n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\nMQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115db0910>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.1 for recentered qwerty\n"
     ]
    }
   ],
   "source": [
    "show_kbd(recentered(qwerty), 'recentered qwerty')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nAVZ8or8eYNsmAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115dc2510>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "for _ in range(10):\n",
    "    workload_plot(recentered(qwerty), 3000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's interesting.  At first glance it looks like there is much more variability in the lines, compared to the previous plot for the plain `qwerty` keyboard. But that's just an illusion.  The Y axis is much more compressed on this plot; if you look at the numbers on the Y axis, you see the variation is actually *smaller* on this plot.   \n",
    "\n",
    "Let's try the `report` function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\ncqX++usvRUdHq3Xr1oqLi9P8+fMt37H7AfleOxMnTlROTo5tW4WFhXI6nQoPD9fUqVM1fPhwjR07\n1pYt93HVr19fY8aMKbnOEx9XuzfWrFmj3377TVFRUXrmmWf01FNPaebMmbZulge/z8BDvPW90Nmq\nHDtsVexORkaGRowYoXXr1ik8PNzWrftFDDzEG/9xsFV5dtiqPDue3nLjZSIAADEAABADAICIAQBA\nxAAAIGIAABAxAACIGAAAJPlX9B14mHjqrQvYqlxb3nhM3rrlqZ2goCCP7NyJGHjI/Z5N6I1nPXrj\nMbFVeXa8eas8eJkIAEAMAADEAAAgYgAAEDEAAIgYAABEDAAAIgZex+Fw2L4xfvx4LV26tORy586d\nNWLEiJLLEydO1OLFi8u9c/exrFmzRm+99Va5b/decnNzS37fdN26dRUcHCyn06mWLVvq5s2blm75\n+fmV+v3Wdvxu5jvl5ORowIABatKkicLCwpSQkKBbt25ZvuOJv3vS/3/8WrVqpcmTJ1v++bnX1nPP\nPadu3brp0KFDtm1VNGLgZTxxhmT79u21f/9+SdLt27eVm5urI0eOlPz/jIwMRUdHl3vn7mOx89hq\n1aoll8sll8ulUaNGacKECXK5XMrKylKVKlUs3QoICCjZcrlcmjJliqW3f7e4uDiFhYXJ5XIpNTVV\nhw4dKhVzq3jq7Fz3xy8zM1NHjhxRamqq7Vu//PKL4uLiNGfOHNu2KhoxQJm1bdtWGRkZkqTDhw+r\nefPmCgwM1OXLl1VUVKSjR4+qZcuWlu968izOynDG6H+Rn5+vw4cPa86cOQoMDFRoaKjef/99bdy4\nsaLvWrlVqVJFnTp10vfff2/7ljFGOTk5qlq1qu1bFYW3o0CZ1atXT/7+/srOzlZGRobatm2rc+fO\nKSMjQ9WrV1dkZKT8/cv/V6uwsFBOp7Pk8qVLl9SrV69y325Fu/u4pk+frtjYWFu2UlJS1KFDh1LX\nhYeH6+zZs7pw4YLq1Kljy64nXL58WVu3btXMmTNt23B/rvLy8lRYWKisrCzbtioaMcB9adeunfbv\n36/9+/drwoQJOnfunPbv368aNWqoffv2lmxUq1ZNLper5PKXX36pn376yZLbrkh3H5fd7vXyTWV5\nv5x7cT9AV69eXb169VJMTIxtW3d+rjZs2KDXXnut5FmxtyEGuC/R0dHat2+fDh48qMjISDVo0EAL\n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\nJwAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1117f5910>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9 for repeated improved recentered qwerty\n"
     ]
    }
   ],
   "source": [
    "show_kbd(repeated_improved(recentered(qwerty)), 'repeated improved recentered qwerty')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So the starting layout of keys seems not to matter very much; you can start with a workload average of 3.2 or 2.1, but either way you end up with an improved version somewhere close to 1.9.\n",
    "\n",
    "Aside: Lower Bound on Best Keyboard\n",
    "---\n",
    "\n",
    "The best improvement on Qwerty that I've seen so far has workload average of 1.86.  But can we estimate a **lower bound** on the best possible permutation of letters?  Here's one approach:  \n",
    "\n",
    "1. Start with a recentered keyboard (that is, frequent keys are in the center).\n",
    "2. Now, for each letter `L` on the keyboard, figure out the best possible rearrangement of the other letters to minimize the workload on segments that involve `L`.  The best rearrangement will have the letter that appears in the most segments with `L` closest to `L`; the letter that appears in the next most number of segments with `L` the next closest to `L`, and so on.\n",
    "3. Do this independently for each letter.  That is, we're summing up the workloads segment by segment, but instead of doing this on one keyboard, we're actually doing it on 26 different keyboards, each one optimized  to give the absolute minimum possible distance for one letter.\n",
    "\n",
    "I can't quite prove this is a lower bound, because of the assumption that we hold one end of the segments fixed in the recentered-keyboard layout.  I'm pretty confident that the freedom to optimize the other end of each segment for each letter independently means that it is a lower bound, but I can't prove it. Still, here it is:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def workload_lower_bound(kbd=recentered(qwerty), workload=WORKLOAD):\n",
    "    \"Given a keyboard, find a lower bound on the workload average.\"\n",
    "    letters = list(kbd)\n",
    "    def distances(L):\n",
    "        \"Distances to L key, shortest distance first.\"\n",
    "        return sorted(abs(kbd[A] - kbd[L]) for A in letters if A != L)\n",
    "    def counts(L):\n",
    "        \"Workload counts for segments involving L, biggest count first.\"\n",
    "        return sorted([workload[segment(A, L)] for A in letters if A != L],\n",
    "                      reverse=True)\n",
    "    return (sum(dot_product(distances(L), counts(L)) for L in kbd)\n",
    "            / sum(workload.values()) / 2) # Divide by 2 because we counted each segment twice\n",
    "\n",
    "def dot_product(A, B):\n",
    "    \"Σ_i{A_i * B_i}\"\n",
    "    return sum(A[i] * B[i] for i in range(len(A)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4-by-7 1.5038079626\n",
      "qwerty 1.60656497359\n",
      "5-by-6 1.47094590247\n"
     ]
    }
   ],
   "source": [
    "for name in keyboards:\n",
    "    print name, workload_lower_bound(keyboards[name])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So there's a gap of around 0.2 or 0.3 of a key distance between the best keyboard we've found so far, and the lower bound we computed for each keyboard shape.  We don't know where within this gap the actual best keyboard lies. But rather than mind the gap, I'll turn my attention to other matters."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Question 5: How often are two words confused because they have similar paths?\n",
    "===\n",
    "\n",
    "When can one word be confused with another?  When their paths are similar (which means that their corresponding letters are in similar locations). For example, on a Qwerty keyboard, the paths for \"HELLO\" and \"JELLO\" are similar, because **H** and **J** are adjacent, and the other letters are the same.\n",
    "\n",
    "<img src=\"http://norvig.com/gesture.png\">\n",
    "\n",
    "We'd like to know, for a given keyboard, how confusing is it? How many words have paths on the keyboard that can be confused for other words? We have our work cut out for us:\n",
    "\n",
    "1. Determine what letters could be confused for each other.\n",
    "2. Determine what words/paths can be confused.\n",
    "3. Invent some metric for the overall confusingness of a keyboard. \n",
    "4. Try to find less-confusing keyboards.\n",
    "\n",
    "Letter Confusions\n",
    "---\n",
    "\n",
    "So, as a first step, we will make a mapping from each key to the keys that it can be confused with. I'll say that any key within a distance of 1.5 units on the keyboard is a **neighboring** key, and thus a potential confusion:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def neighboring_keys(kbd, radius=1.5):\n",
    "    \"Build a dict of {Letter:NeighboringLetters}, e.g. {'Q':'QAW', ...}.\"\n",
    "    def neighboring_letters(A): \n",
    "        return cat(B for B in kbd if distance(kbd[A], kbd[B]) <= radius)\n",
    "    return {A: neighboring_letters(A) for A in kbd}\n",
    "\n",
    "def neighboring_keys(kbd, radius=1.5):\n",
    "    \"Build a dict of {Letter:NeighboringLetters}, e.g. {'Q':'QAW', ...}.\"\n",
    "    def neighboring_letters(A): \n",
    "        return \n",
    "    return {A: cat(B for B in kbd if distance(kbd[A], kbd[B]) <= radius)\n",
    "            for A in kbd}\n",
    "\n",
    "cat = ''.join  ## Function to join letters (or strings) into one string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'A': 'AQSWZ',\n",
       " 'B': 'BGHJNV',\n",
       " 'C': 'CDGFVX',\n",
       " 'D': 'CEDFSRXZ',\n",
       " 'E': 'EDSRW',\n",
       " 'F': 'CDGFRTVX',\n",
       " 'G': 'CBGFHTVY',\n",
       " 'H': 'BGHJNUVY',\n",
       " 'I': 'IKJOU',\n",
       " 'J': 'BIHKJMNU',\n",
       " 'K': 'IKJMLON',\n",
       " 'L': 'KMLOP',\n",
       " 'M': 'KJMLN',\n",
       " 'N': 'BHKJMN',\n",
       " 'O': 'IKLOP',\n",
       " 'P': 'LOP',\n",
       " 'Q': 'AQW',\n",
       " 'R': 'EDFRT',\n",
       " 'S': 'AEDSWXZ',\n",
       " 'T': 'GFRTY',\n",
       " 'U': 'IHJUY',\n",
       " 'V': 'CBGFHV',\n",
       " 'W': 'AEQSW',\n",
       " 'X': 'CDFSXZ',\n",
       " 'Y': 'GHUTY',\n",
       " 'Z': 'ADSXZ'}"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "qwerty_neighbors = neighboring_keys(qwerty) \n",
    "qwerty_neighbors"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see, for example, that **Q**, off in the corner, has only **A**, **W**, and itself as neighbors, while **G**, in the middle of the keyboard, has 8 neighbors. \n",
    "\n",
    "Word Confusions\n",
    "---\n",
    "\n",
    "Consider each of the letters in the word \"HELLO,\" and all the possible choices for neighbors of each letter:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['BGHJNUVY', 'EDSRW', 'KMLOP', 'KMLOP', 'IKLOP']"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "choices = [qwerty_neighbors[L] for L in 'HELLO']\n",
    "choices"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Think of this as five \"columns\" of letters, and if we pick one letter from each column, we get a path that is formed by letters that are each confusions of letters in the original word, and so the whole path is a confusion for the original word.  So \"JELLO\" is a confusion for \"HELLO\", as would be \"BEKKI\" (formed by taking the first possible neighbor for each of the five letters) and \"YWPPP\" (formed by taking the last possible neighbors), except they are not words.\n",
    "\n",
    "It turns out that there is a library function, `itertools.product`, that will take an iterable of alternative choices, and generate all possible ways of assembling a sequence consisting of one selection (letter) from each alternative choice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "paths = {cat(letters) for letters in itertools.product(*choices)}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "How many paths are there?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5000"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(paths)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's look at a few of them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['JSMML', 'JRPML', 'VWMPP', 'JSPML', 'JDLML', 'VWPPK', 'GRLLK', 'BWOMK']"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "random.sample(paths, 8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And let's see all the paths that are also words:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'BROMO', 'BROOK', 'HELLO', 'JELLO'}"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "WORDS & paths"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Only 4 out of 5000!  That's pretty good, but it means \"HELLO\" is still a confusing word. We can wrap this up as the function  `confusions`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def confusions(word, neighbors=neighboring_keys(qwerty), words=WORDS):\n",
    "    \"All valid words whose paths could be confused with the path for the given word.\"\n",
    "    choices = [neighbors[L] for L in word]\n",
    "    return {cat(letters) for letters in itertools.product(*choices)} & words"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'BROMO', 'BROOK', 'HELLO', 'JELLO'}"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "confusions('HELLO')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'ALTOS', 'EIDOS', 'SIDLE', 'SKEPS', 'WIELD', 'WORKS', 'WORLD', 'WORMS'}"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "confusions('WORLD')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'FEARING',\n",
       " 'FRAYING',\n",
       " 'GEARING',\n",
       " 'GRATING',\n",
       " 'GRAYING',\n",
       " 'GREYING',\n",
       " 'REARING',\n",
       " 'REEFING',\n",
       " 'RESTING',\n",
       " 'TEARING',\n",
       " 'TESTING'}"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "confusions('TESTING')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So far, so good.  But I'm worried about the efficiency of `confusions` for longer words.  \n",
    "\n",
    "More Efficient `confusions`\n",
    "---\n",
    "\n",
    "Consider:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 3.71 s, sys: 623 ms, total: 4.33 s\n",
      "Wall time: 4.2 s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'SOMETHING'}"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time confusions('SOMETHING')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It took (on my computer) 4 seconds to compute this.  Why so long? Let's count:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[7, 5, 5, 5, 5, 8, 5, 6, 8]"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[len(neighboring_keys(qwerty)[L]) for L in 'SOMETHING']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are 7 &times; 5 &times; 5 &times; 5 &times; 5 &times; 8 &times; 5 &times; 6 &times; 8 = 8,400,000 paths for `confusions` to consider. Looking at them all takes 4 seconds, but for most of these paths, we're wasting our time.  For example, one choice for the first two neighboring letters of 'SOMETHING' is 'XP', but 'XP' does not start any word in the dictionary.  Nevertheless, `itertools.product` will generate 240,000 combinations that start with 'XP', and will then rule them out one at a time. It would be better to stop as soon as we see 'XP', and never consider continuations of this path.\n",
    "\n",
    "So that gives us the key idea for a more efficient version of `confusions`: *only follow paths that form a prefix of at least one word.*  \n",
    "\n",
    "So, first we need to define the set of prefixes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def prefixes(words):\n",
    "    \"Return a set of prefixes (1 to N characters) of this collection of words.\"\n",
    "    return {word[:i] for word in words for i in range(1, len(word)+1)}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'T', 'TH', 'THE', 'THES', 'THESE', 'THEY', 'THO', 'THOS', 'THOSE'}"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prefixes(['THESE', 'THEY', 'THOSE'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's generate the prefixes of all the words, and check how many there are:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(395184, 178691)"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "PREFIXES = prefixes(WORDS)\n",
    "\n",
    "len(PREFIXES), len(WORDS)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can describe the more efficient version of the `confusions` algorithm:\n",
    "\n",
    "1. Maintain a queue of partial paths, where a partial path is a string representing choices for neighboring letters for a prefix of the word. For example, 'HE' and 'JE' are both partial paths for the word 'HELLO'.  Initialize the queue to have just one partial path, the empty string.\n",
    "2. Remove a partial path from the queue. Find all possible ways to extend the path by adding a neighboring letter, but only if doing so creates a path that is a prefix of some word in the dictionary.  For example, given the word 'HELLO', and the partial path 'JE', consider all the neighbors of 'L' (namely, 'K', 'M', 'L', 'O', or 'P'), but only 'JEM', 'JEL', and 'JEO' are prefixes of words, so add just those to the queue.\n",
    "3. When a partial path reaches the same length as the word ('HELLO' in this example), then don't extend it any farther; instead check to see if the path is a word.  If it is, add it to the set of paths/words that form the result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def confusions(word, neighbors=neighboring_keys(qwerty), words=WORDS, prefixes=PREFIXES):\n",
    "    \"All valid words whose paths could be confused with the path for this word.\"\n",
    "    results = set() # A set of words that are confusions of 'word'\n",
    "    Q = ['']        # A queue of partial paths \n",
    "    while Q:\n",
    "        path = Q.pop()\n",
    "        if len(path) < len(word):\n",
    "            for L in neighbors[word[len(path)]]:\n",
    "                if path + L in prefixes:\n",
    "                    Q.append(path + L)\n",
    "        elif path in words:\n",
    "            results.add(path)\n",
    "    return results"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's do one quick check to see if we are getting the same answers as before:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'BROMO', 'BROOK', 'HELLO', 'JELLO'}"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "confusions('HELLO')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And let's see how much faster this version is:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 543 µs, sys: 155 µs, total: 698 µs\n",
      "Wall time: 586 µs\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'SOMETHING'}"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time confusions('SOMETHING')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We went from about 4 seconds to under a millisecond: that's more than 4000 times faster!  We can do a bigger test:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def words(text): \n",
    "    \"Extract a list of all words from a text. Make everything uppercase.\"\n",
    "    return re.findall('[A-Z]+', text.upper())\n",
    "\n",
    "test_words = words(\"\"\"Hello world! Testing something: confusion paths on \n",
    "multiple distinct inputs of various lengths. See if everything works \n",
    "perfect, or poorly, or somewhere between the extremes.\"\"\")\n",
    "\n",
    "def test_confusions(words=test_words):\n",
    "    \"Run through some test words, printing and summarizing the confusions.\"\n",
    "    total = 0\n",
    "    for word in sorted(set(words)):\n",
    "        others = sorted(set(confusions(word)) - {word})\n",
    "        total += len(others)\n",
    "        print('{}({}): {}'.format(word, len(others), ' '.join(others)))\n",
    "    print 'Total of {} confusions for {} words'.format(total, len(words))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BETWEEN(2): BERSEEM BERSERK\n",
      "CONFUSION(2): CONCISION CONTUSION\n",
      "DISTINCT(0): \n",
      "EVERYTHING(0): \n",
      "EXTREMES(3): ESTEEMED EXTERNES SCREENED\n",
      "HELLO(3): BROMO BROOK JELLO\n",
      "IF(7): ID IT OD OF OR OX UT\n",
      "INPUTS(6): IMPURE IMPUTE KNOUTS OBLIGE UMPIRE UNPURE\n",
      "LENGTHS(0): \n",
      "MULTIPLE(0): \n",
      "OF(6): ID IF IT OD OR OX\n",
      "ON(3): IN OH OM\n",
      "OR(7): ID IF IT OD OE OF PE\n",
      "PATHS(7): LARGE LARVA LATHE LATHS OATHS PARGE PARVE\n",
      "PERFECT(2): PREFECT PRETEXT\n",
      "POORLY(1): LIKELY\n",
      "SEE(20): ADD ADS ARE ARS ASS AWE DEE DEW EDS ERE ERR ERS ESS EWE SER SEW WED WEE ZED ZEE\n",
      "SOMETHING(0): \n",
      "SOMEWHERE(1): WINDSURFS\n",
      "TESTING(10): FEARING FRAYING GEARING GRATING GRAYING GREYING REARING REEFING RESTING TEARING\n",
      "THE(5): FUD FUR RUE RYE TYE\n",
      "VARIOUS(3): CARIOUS CAROLUS FATUOUS\n",
      "WORKS(13): AIRNS AKELA AKENE ALTOS EIDOS SIDLE SKENE SORNS SPEND WIDOW WIELD WORLD WORMS\n",
      "WORLD(7): ALTOS EIDOS SIDLE SKEPS WIELD WORKS WORMS\n",
      "Total of 108 confusions for 25 words\n",
      "CPU times: user 7.21 ms, sys: 2.06 ms, total: 9.27 ms\n",
      "Wall time: 7.84 ms\n"
     ]
    }
   ],
   "source": [
    "%time test_confusions()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, 108 confusions for 25 test words, or an average of 4 confusions per word.\n",
    "\n",
    "Visualizing Paths on a Keyboard\n",
    "----\n",
    "\n",
    "To determine if these `confusions` are good ones, let's build something to visualize paths on a keyboard.  \n",
    "\n",
    "\n",
    "I'll add functionality to `plot_kbd` to call the new function `plot_paths` and implement that:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def plot_kbd(kbd, K=20, words=()):\n",
    "    \"Plot the keyboard with square keys, K units on a side.\"\n",
    "    H = K / 2       ## (K is Key width/height; H is half K)\n",
    "    # Draw a square for each key, and plot paths for words\n",
    "    for L in kbd:\n",
    "        x, y = K * kbd[L].x, -K * kbd[L].y\n",
    "        plot_square(x, y, H, label=L)\n",
    "    plot_paths(kbd, K, words)\n",
    "    # Show the plot and print the workload average\n",
    "    plt.axis('equal'); plt.axis('off'); plt.show()\n",
    "    \n",
    "def plot_paths(kbd, K, words):\n",
    "    \"Plot paths for each word.\"\n",
    "    for (i, word) in enumerate(words):\n",
    "        Xs = [+K * kbd[L].x for L in word]\n",
    "        Ys = [-K * kbd[L].y for L in word]\n",
    "        plt.plot(Xs, Ys, '-o')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see how it works:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAGZ9JREFUeJzt3XtcVHX+x/H3wICCgItIpaK/wPtPUHFNU7w8st0HeSm3\nsjVtu9jWJqb98tbPXWtjtdVda72Utbba1m/VxbSyNhE1TTeNQS1nSxEwTfJCJdcSEFSY3x8GCaLO\nwDnDDL6ej8c89sGk5z1n58y8+ZzvnNHicDgcAgBc03wa+wEAABofZQAAoAwAAJQBAECUAQBAlAEA\nQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBA\nlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECU\nAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQB\nAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEA\nQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBAlAEAQJQBAECUAQBA\nkrWxH8C1olWrViosLHRLlsVikcPhIMsLspriPjXVLHfuU2hoqAoKCtySVcXicNfeXeOa4ouDLO/J\nIct7ctydVYXTRAAAygAAQBkAAEQZAABEGQAARBkAAEQZAABEGXikvLw83XvvverUqZM6d+6sp59+\nWhUVFYbnTJ06VUuWLKn+OT4+Xo8++mj1z9OnT9eiRYsMyfL19VVsbGz1bcGCBYZs90pZffr00bRp\n03T27FnTsvLz86v3qU2bNoqIiKjOPnfunGE5DodDgwcP1qZNm6rvW7dunYYPH25YRpXs7GzFxMTU\nuC8xMVF/+ctfDM8KCgoyfJtX4q7XVtUx+NOf/lQzZ8409FgwC2XggR566CF17txZdrtdmzdv1oED\nB2q8aRtl0KBBSk1NlSRVVlYqPz9fBw8erP7vNptNcXFxhmQFBgZq9D336njFdzrqKNSCVcuUOO/P\nhmy7riy73a49e/boyJEj2rJliyk5khQWFia73S673a6JEydq2rRpstvt2rdvn/z8/AzLsVgsWrZs\n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\nuSmiDNAghw4d0ty5c7Vy5UpTtj916lSlpaVp8eLFSk1NrfGbp5GKi4u1b98+zZ8/X+Hh4ZKkm2++\nWUOHDjU86+6771ZycrLOnz8vScrOzlZOTo4GDRpkeFYVh8OhvLw8NW/e3LQMPz8//eY3v9GiRYtM\ny6gyYsQIJScnS5KSkpI0btw4rihuIMoA9Xbu3DmNHz9eCxcuVEREhCkZVqtVCxYs0LRp07R48WL5\n+vqakrNx40YNHjzYlG3X1qpVK/Xr108bN26UJK1Zs0Zjx441JevMmTOKjY1VZGSkEhMTNW/ePFNy\nqkyaNEmrV6/W999/b2rO2LFjtWbNGpWXl2v//v3q37+/qXnXAsoA9fbMM88oJiZG99xzj6k5KSkp\natu2rfbv329ahrtPMVx8qujNN9/UuHHjTMkJCAiQ3W5Xdna2XnnlFY0ZM8aUnCrBwcF64IEH9OKL\nL5qaExMTo+zsbCUlJWnkyJGmZl0rKAPUy44dO7R+/XotXbrU1Jz//Oc/2rp1q2w2mxYtWqRvvvnG\nlJzhw4dr586dpmy7LnfccYe2bdsmu92u0tJSxcbGmp551113KSMjQ6WlpabmPPnkk3rttddUUlJi\nas4dd9yhGTNmcIrIIJQBXFb1qZt//OMfatGihWk5DodDCQkJWrJkidq3b6+ZM2eatmYQFBSkPn36\n6Omnn1Zubq4kae/evfroo49My7vllls0YcIEUxaO6/Lxxx+rc+fOCgwMNDUnNDRUv/zlL/Xaa6+Z\nOnE9/PDDSkxMVI8ePUzLuJZQBnDZsmXLlJubq4kTJ9b4eOm6desMzVm+fLluvPFG3XrrrZIunI/O\nyMgw7Tf4FStW6MSJE+rXr5+io6M1Z84ctWvXzpQs6cKpov3795t2ikj6cc2gV69eWrBggRYuXGha\n1sVv/NOnT1deXp6pOe3atdPkyZOr7+PTRA1jcTBf4Qd8bz1ZjZnVFPfJmzAZAAAoAwAAZQAAEGUA\nABBlAAAQZQAAEGUAABBlAACQZG3sBwDP4q6rON15tShZ3pPlrpzQ0FC35HgTygDVuCITuHZxmggA\nQBkAACgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAo\nAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgD\nAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMA\ngCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCA\nKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAo\nAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCAKAMAgCgD\nAIAoAwCAKAMAgCgDAIAoAwCApP8H0fl69wTQkGkAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1117fd650>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_kbd(qwerty, words=['HELLO', 'JELLO'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "OK, we're on the right track, but I see three problems: first, the letters are obscured by the circles.  Second, when the paths are the same (for the \"ELLO\" part), they overwrite each other.  And third, there is no indication what direction the path is going in (\"OLLEH\" would look the same as \"HELLO\").  \n",
    "\n",
    "I have an idea to fix the first two problems (and I will ignore the third for now).  Instead of plotting the circle in the center of the key, offset it to one of the corners of the key.  Have each path offset to a different corner; that way they won't overlap (at least for up to four paths).  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def plot_paths(kbd, K, words):\n",
    "    \"Plot paths for each word, each with a different offset (and color).\"\n",
    "    Q = K / 4  ## Q is a quarter of a key width\n",
    "    offsets = [Point(-Q, -Q), Point(-Q, +Q),  Point(Q, +Q), Point(Q, -Q)]\n",
    "    for (i, word) in enumerate(words):\n",
    "        Xs = [+K * kbd[L].x + offsets[i % 4].x for L in word]\n",
    "        Ys = [-K * kbd[L].y + offsets[i % 4].y for L in word]\n",
    "        plt.plot(Xs, Ys, '-o')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAHuBJREFUeJzt3XtcVHX+x/HXwKiIIF4yU6EUvFWijnaRvGWua17bMvNS\n2eWXWxLifdeyC2a37ZH3Sh+rlW21aFq2j13wUuY1sdLI0BCyTUXdChAVVEjg/P6YZgIEZeCc4eL7\n+XjMQznA+ZwznJn3fM73e2ZshmEYiIjIZc2nqjdARESqnsJAREQUBiIiojAQEREUBiIigsJARERQ\nGIiICAoDERFBYSAiIigMREQEhYGIiKAwEBERFAYiIoLCQEREUBiIiAgKAxERQWEgIiIoDEREBIWB\niIigMBARERQGIiKCwkBERFAYiIgICgMREUFhICIiKAxERASFgYiIoDAQEREUBiIigsJARERQGIiI\nCAoDERFBYSAiIigMREQEhYGIiKAwEBERFAYiIoLCQEREUBiIiAgKAxERQWEgIiIoDEREBIWBiIig\nMBARERQGIiKCwkBERFAYiIgICgMREUFhICIiKAxERASFgYiIoDAQEREUBiIigsJARERQGIiICAoD\nERFBYSAiIigMREQEhYGIiKAwEBERFAYiIoLCQEREUBiIiAgKAxERQWEgIiIoDEREBIWBiIigMBAR\nERQGIiKCwkBERFAYiIgICgMREUFhICIiKAxERASFgYiIoDAQEREUBiIiAtiregMuF02aNCErK8sr\ntWw2G4ZhqFYNqFUb96m21vLmPjVu3JgTJ054pZaLzfDW3l3mauODQ7VqTh3Vqjl1vF3LRaeJRERE\nYSAiIhozEAvEfRLHon8uIs/Io56tHtFjoxkyYEhVb5ZcJnT8VYzCQEwV90kck16fxA+OH9zLfnjd\n+X89IMVqOv4qTgPIXlJbB59K1hr40EA2tt54wc9FpEaw4a0NBNYLNK2WlTQoWTNrlXX8DTw8kPVv\nrTetjtWqYgBZnYGYKs/IK3V5wrEEGr7ckFaBrehwRQc6NO1Axys6uv8NCQrBx6YhLKmcso6/3MJc\nL29JzaMwEFPVs9UrdXm/a/oxefRkEtISSDiawDt73+Hs+bPu79e316dd03bFAqJD0w60b9q+Ut2E\nXF7KOv78fPy8vCU1j16KVUMZGRmMHj2atm3b0q5dO5566ikKCgpMrzNlyhQWLlzo/nrgwIGMHz/e\n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\nQ5YMHJfm888/p127dvj7+1tap3Hjxtxzzz28+eablnZcDz/8MDExMVx//fWW1bicKAzEY0uXLiU9\nPZ3HHnus2PTS1atXm1pn2bJltG7dmv79+wPO89HJycmWvYJfvnw5R48e5aabbqJTp04899xztGrV\nypJa4DxVlJSUZNkpIvh9zKBLly688sorzJs3z7JaRZ/4p02bRkZGhqV1WrVqRVRUlHuZZhNVjj4D\nWdz0vvWqVZW1auM+1STqDERERGEgIiIKAxERQWEgIiIoDEREBIWBiIigMBARERQGIiIC2Kt6A6R6\n8dZVnN68WlS1ak4tb9Vp3LixV+rUJAoDcdMVmSKXL50mEhERhYGIiCgMREQEhYGIiKAwEBERFAYi\nIoLCQEREUBiIiAgKAxERQWEgIiIoDEREBIWBiIigMBARERQGIiKCwkBERFAYiIgICgMREUFhICIi\nKAxERASFgYiIoDAQEREUBiIigsJARERQGIiICAoDERFBYSAiIigMREQEhYGIiKAwEBERFAYiIoLC\nQEREUBiIiAgKAxERQWEgIiIoDEREBIWBiIigMBARERQGIiKCwkBERFAYiIgICgMREUFhICIiKAxE\nRASFgYiIoDAQEREUBiIigsJARERQGIiICAoDERFBYSAiIigMREQEhYGIiKAwEBERFAYiIoLCQERE\nUBiIiAgKAxERQWEgIiIoDEREBIWBiIigMBARERQGIiKCwkBERFAYiIgICgMREUFhICIiKAxERASF\ngYiIoDAQEREUBiIigsJARERQGIiICAoDERFBYSAiIigMREQEhYGIiKAwEBERFAYiIoLCQEREUBiI\niAgKAxERQWEgIiIoDEREBIWBiIigMBARERQGIiKCwkBERFAYiIgICgMREUFhICIiKAxERAT4f5Cd\nb8FK/YFuAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1116f0c10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_kbd(qwerty, words=['HELLO', 'JELLO'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That looks much better!  Let's look at all four confusions of \"HELLO\":"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3XlYVGX/BvB7YJBFlhCXRNxARUVK3MVdU1JCbUHNFrPF\nLcSlfEtxwa16fV1JzdJK7TVcU3+KC5m4BVaab+KGiiGiWYCouIAOfH9/jIwsA8wMMzCD9+e65lIO\nM+d7nuHM3POc55lzFCIiICKiJ5pVRW8AERFVPIYBERExDIiIiGFARERgGBARERgGREQEhgEREYFh\nQEREYBgQEREYBkREBIYBERGBYUBERGAYEBERGAZERASGARERgWFARERgGBARERgGREQEhgEREYFh\nQEREYBgQEREYBkREBIYBERGBYUBERGAYEBERGAZERASGARERgWFARERgGBARERgGREQEhgEREYFh\nQEREYBgQEREYBkREBIYBERGBYUBERGAYEBERGAZERASGARERgWFARERgGBARERgGREQEhgEREYFh\nQEREYBgQEREYBkREBIYBERGBYUBERGAYEBERGAZERASGARERgWFARERgGBARERgGREQEhgEREYFh\nQEREYBgQEREYBkREBIYBERGBYUBERGAYEBERGAZERASGARERgWFARERgGBARERgGREQEhgEREYFh\nQEREYBgQEREYBkREBIYBERGBYUBERGAYEBERGAZERASGARERgWFARERgGBAREQBlRW/Ak6JatWrI\nyMgol1oKhQIiwloWUKsytqmy1irPNrm6uuLGjRvlUiuPQsqrdU+4yvjiYC3LqcNallOnvGvl4WEi\nIiJiGBAREccMyEwciopCdEQElNnZUNnaok9oKLoGBpb4mKgfoxDxfQSyJRu2CluEDg1FYO+SH0OV\nX/i8cCzdtBQqaxWUOUqEBIcg/F/hpT7OkH2wMmEYUIU7FBWFvePGYW5iomZZ2KP/F/dijPoxCuOW\njUOi3+PHJC5T/5+B8OQKnxeOuVvmQvWCSrNs7pa56t+VEAiG7IOVDQeQy0llHXwyRq2pAQGYEx1d\nZPm0tm0xe+tWwN0dUCgK1AoYHoDoBkUf4/KzC5oOalqm7QGAX375Be3bty/zesylzpNS61jEMeS8\nnFPkPm5Rbkj7Na3YdRS7DwYEYPaePQAs73WlL/YMqMIps7O1Lrf+7TfAwwNwdAS8vfFfAJg1C2ja\n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\nwDAgIiIwDIiICAwDIiICw4CIiMAwICIiMAyIiAgMAyIiAsOAiIjAMCAiIjAMiIgIDAMiIgLDgIiI\nwDAgIiIwDIiICAwDIiICw4CIiMAwICIiMAyIiAgMAyIiAsOAiIjAMCAiIjAMiIgIDAMiIgLDgIiI\nwDAgIiIwDIiICAwDIiICw4CIiMAwICIiMAyIiAgMAyIiAsOAiIjAMCAiIjAMiIgIDAMiIgLDgIiI\nwDAgIiIwDIiICAwDIiICw4CIiMAwICIiMAyIiAgMAyIiAsOAiIjAMCAiIjAMiIgIDAMiIgLDgIiI\nwDAgIiIwDIiICAwDIiICw4CIiMAwICIiMAyIiAgMAyIiAsOAiIjAMCAiIjAMiIgIDAMiIgLDgIiI\nwDAgIiIwDIiICAwDIiICw4CIiMAwICIiMAyIiAgMAyIiAsOAiIjAMCAiIjAMiIgIDAMiIgLDgIiI\nwDAgIiIwDIiICAwDIiICw4CIiMAwICIiMAyIiAgMAyIiAsOAiIjAMCAiIjAMiIgIDAMiIgLDgIiI\nwDAgIiIwDIiICAwDIiIC8P8UKRu3sOtPIgAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115da5150>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_kbd(qwerty, words=confusions('HELLO'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Not all the paths are very close to each other. This suggests that maybe they shouldn't all be confusions of 'HELLO'.  Let's see if we can get less confusion.\n",
    "\n",
    "Less Confusion\n",
    "---\n",
    "\n",
    "Let's concentrate on 'JELLO' instead of 'HELLO':"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'BROMO', 'BROOK', 'HELLO', 'IROKO', 'JELLO'}"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "confusions('JELLO')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Two confusions are 'JELLO' and 'BROOK':"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAHghJREFUeJzt3XtYVHXCB/DvwBADMhBeE+EpQVhJSQdTgfXuKimim3l/\nn2p1X/GygGL67qZWs5qba+YFL2umpbSthprbK2ihhqiBmpfdVVfDUFExN1HRQYUEfu8fvDNyGWQu\n58yN7+d55ikO4/meM5yZ7/zmXEYhhBAgIqImzc3eC0BERPbHMiAiIpYBERGxDIiICCwDIiICy4CI\niMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAMiIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CI\niMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAMiIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CI\niMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAMiIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CI\niMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAMiIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CI\niMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAMiIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CI\niMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAMiIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICoLT3\nAjQVzZs3x507d2ySpVAoIIRglhNkueI6uWqWLdfJ398ft2/ftkmWnkLYau2aOFd8cjDLeXKY5Tw5\nts7S48dERETEMiAiIpYBObHMvZmInRiLfr/ph9iJscjcm2nvRSIHoF2iRcvuLfF01NNo2b0ltEu0\n9l4kp8AdyOSUMvdmYsaaGSjQFBimFayp/v+4QXH2WiyyM+0SLRbtWISKYRWGaYt2LKr+3f9o7bRU\nzoE7kG3EVXc+2SsrdmIssp7Lqncfv2/90HFMR6uzjh49ip49e1o9H0fJaSpZx1OPo/KVynr3aZHZ\nAsXHiq3KcdXnlSGTZWAbrroh2Sur32/6Iad9Tr37aM5rsPb9tVZnRUdHIy8vz+r5OEpOU8n61chf\n4X7c/Xr38dvjh5IjJVbluOrzSo8fE5FT8lR4Gp2ue6hDj3Y94KawcnfYNSAqMMq6eThSThPJUgkV\n7qN+GSir+FLXGO5AdkDFxcUYN24cOnTogNDQUMyfPx+VlfWHvtZKSUnBypUrDT/HxsZi8uTJhp/f\neOMNLF++XJIsd3d3aDQaw23JkiVWzS95QjJCToXUmvbs8WehDFXC3d0dXbp2QWRkJGbNmoWff/7Z\nqqwnuXXrlmGd2rZti8DAQGg0GkRGRuLRo0eS5Qgh0Lt3b3z11VeGadu2bcOQIUMky9C7fPkyIiIi\nak3TarX44IMPJM/y8fGRdH6JoxOhyFDUmqbMVCJxVCIA2z239Nt7t27dMGfOHEm3BdkIsglzHuq4\nuDgxf/58ce/ePVFQUCBGjBghPvjgA8mztm/fLsaMGSOEEKKyslJ069ZNxMTEGH4fHR0tjh49KkmW\nj4+PSfczJysjK0PETowVfV/vK2InxoqMrAxR9qhMeHh5iPDV4eLsjbNi+PDhYteuXVZnmUKr1Zr1\ndzI358yZMyI8PFyUlZUJnU4nQkNDxcWLFyXPunTpkujcuXOtaVqtVixdulTyLKm3i2t3rwnPeE/R\n/MXmwq+nn2jRvYV458/vGH5vzXPLnL+Vfr1+/vlnMXToULO3QXu8NHPs5GB0Oh3Onj2LjIwMAIBa\nrcZ7772HyZMnY9asWZJmRUdHIyUlBQBw9uxZdO7cGTdu3EBJSQm8vLxw7tw5REZGSpoppbhBcUaP\nHPJ098TMqJnol9YPw58fjuzsbAwbNswmyyRk/Jy3U6dOiI+Px5///GeUlpbi9ddfR/v27WXLc0aL\nDy9GUmIS3h/8fr3f2fK5pefh4YEBAwbg8OHDNtsGLcUycDC7d+9G7969a00LDw/HtWvX8NNPP6F1\n69aSZQUEBECpVOLq1avIy8tDdHQ0ioqKkJeXB19fX0RERECplGYTefjwITQajeHnuXPnYvTo0ZLM\n25iEbglor2qP+JfjETc6DlWiyvr9CA7gnXfegUajgUqlwvHjx+29OA6l6F4RPjv9Gc4nnjf6e1s+\nt/RKSkqQkZGBt99+W/J5S41l4IAUCkW9aUII3L9ff8eYtWJiYpCbm4vc3FzMmjULRUVFyM3NhZ+f\nH3r16iVZjpeXF06dOiXZ/J5EXzw//PADontG48fgH/FK+itI+3Ua1J5qmyyDXLy9vTFu3Dio1Wp4\neHjIkmHsSBYhhNHt0pEsPrwYv9X8Fq2bNfyibmwd5DhyR78N+vr6YsSIEejbt6+k85eD879VcjFD\nhw7FwYMHa007d+4cysvLZflI4Je//CW+/fZbnD59GhEREYiKijKUQ0xMjOR5tqAvnqKiIuhKdJjT\ncg5ae7dGzw09kX8r396LZzU3NzdZX5gDAwNRUlJSa6fnv//971ojO0ejHxXM+eWcBu/T0HMrICAA\nbdq0kXR59NtgTk4OZs2aBTc3x3+pdfwlbGLUajU6deoErVYLnU6HixcvYt68eZg+fboseTExMcjI\nyECLFi2gUCjg7++PkpIS5OXlOW0Z6Pn6+uKjjz7CvDfnYd2wdZgZNRO9Pu6FjPwMey+aQ3N3d0f/\n/v2xZcsWAEB+fj7+9a9/OfS7W1NGBcaeW3PnzsXIkSNtuKSOi2XggDZt2oTz58+ja9eu6NixI555\n5hnZPnPs3Lkzbt26haiox8eEv/DCC3j66afRvHlzyXL0w2b9be7cuZLNu66a75o1Gg06dOiA9PR0\nJHRLwJfjvsTUjKlYmLMQVaJK1mw5yZ2zYMECnDx5EhqNBm+++SbWrFkjy7tbSdZDjUZHBXo1n1uD\nBw9GeHi44SAKKTn6R2rG8AxkG7H0c8m8vDxMnjwZ27ZtQ3h4uKxZljCWlZl5EKmpWSgvV8LTswLJ\nyYMRF9dHlixLXNddx6j0UWjj06bB/Qi8Rr4TZQ1VYPbM2YYjiBx9+3O0LAObH8zaRNnyobZnVkZG\njggJmSsAYbiFhMwVGRk5kmdZo+xRmUj43wQRvjpcfF/8vaxZT9JUtgu5XLt7TeD3EP8p/Y8Qwnm2\nP0fK0uPHRCSp1NQsFBQsqjWtoGARVq3aa6clMs5T6YkP4z/kfgQnt/jwYuAkDPsKnGX7c0Q8tJQk\nVV5ufJM6fNgd48YBHTsCv/hF9S0sDJD4agRmS+iWgIjWERi9bTSmdJuCeX3mucT5CE2B/ggi5D6e\n1tD2V1bmbqOlcl4sA5KUp2eF0ekBAZW4fx9YvRq4devx9MDA2gWh///AQMBWR+NFB0Xj2ORjGJU+\nCidvnETar9NsE0xW0R9BtPT+UsO0hrY/lUr66w+5GpYBSSo5eTAKCubVGqqHhMzF8uUvIS6u+lPc\nH34A8vKAI0eq//vNN8C+fbXn4+1dPXKoWRCABqWl8owmAtQByH49G8l7ktFzQ0+ghfQZJJ2aZxsv\nxeMyMLb9BQXNRVLSS/ZYTKfCo4lsxFWPRGjoaKJVq/airMwdKlUlkpIGPfFojtJS4LvvqotBf3v0\nCGje/PHNywvYtes0VKoItGwp72hi/Yn1mPL5FOz6710YFibv9WTs/bdy1qyk3UlQKVV4f/D79bJq\nbn9FRZUIChqEb77h0USNZrIMbMNVNyQ5soyNHvLzgQcPcjFzZgwCAgB//+oSOX8e+P776tvdu/VH\nE5bum1AEKdAupZ3s+xGc/W9lj6yie0WI+EsEzieeR+tmrZ+Ydfcu0KEDkJsLhIZal+sqj1+DmSwD\n23DVDclWWaWlgFrdH4sWZRtGD97eQFQUEB1dfQsJAS5frl0Q589XF4u5owmFQoGie0WNno9gLVf8\nW8mdVXNUYErWwoXV28Dmzdblusrj12Amy8A2XHVDsldWQ6OHLl0el0N0NNCuHVBZCRQW1i6IxkYT\n3br5QIhSlFeUI3lPMg5dOYS/j/s7wlqEybZOcnOFrLqjAlOypBoduMLj98RMloFtOOPp6c6nGYDu\nAKJr3B4CyKtxOwWg5jef+QIIA9ARwC/+/9YRQAcAxQDOA/ge6LYDGPAt8KUfkH8LAJ82djEEQAUA\ns08bmI/qv+lvJF4gefj7++P27ds2zWQZODhXfDdiqxwhADe3UGzefKHB0UNUVPVHRXUZG00c+zEP\n/wwbDfdTU/D8rXno+As3o6MJW2iK24WxUYGp6o4OXPHxsxbLwMG54kZrz3Wqe+TSkSOASlX7oyWN\nBvD0ND6/67rreHnLKHhVtcEErzRcuaA2lMWZMw8RGOhlk/MmmuJ2UXdfgblq7jtwxcfPWiwDB+eK\nG60jrZMQQEFB7cNaGxs9NLQfQaFwR0FBpVn7Jiw9C9uRHkNb5FgzKtCrOToIC3O9x89aLAMH19Se\n9I6QZeroYfOZ9Zj/zXx8POJjDAsb1ughjvn50hzpZOl6Wcqe20Xm3kyk/i0VZ4rPwEPhgTVJa4x+\n77Wp9KODtDTXe/ysxTJwcE3lSe/IWU8aPTzbKw9ZfqMx8YUp+GDE2xBV5mVVVgJXrjwuCVNHE2q1\ncz2GluRk7s3EjDUzUKApMEwLORWClb9baXEh6EcHxcWhEOKC1ctsCpYBScLZXjgdKUfOrJqjh+zj\n13Gg9ShUlPji19iGPj3Vje57MIV+NFGzJPSjibKyq/jVr4Jsck0ne20XsRNjkfVcVr37xRbG4quP\nv7I4Z+FC4O23N0OI1y2ehzlYBiQJV3jhtFeOLbPKHpXDa2RrBES3Q98f/45zh8NMPnLJXJWVgFLZ\nHrt3X5J93wRgv+2i32/6Iad9Tr379b3UFwc2HbA45+5d4OmnbyI/v5XVZyWbwlnKgBeqI5KAysMT\nyLiHd7TvY/43vfDx5x+jX8Aww+hh82Zg2jTzjlxqiLs7AFzGkCHAkCG1f1d3NPHFF9btm7AnT4Xx\nB0blprJqvn5+AJCKd99daPVZyS5FkEMz90+0c+dOoVAoxPnz52XNWr9+vejTp4+IiIgQXbt2FUeP\nHpU8x83NTXTt2lWEhoaKF198UWzcuFFUVVWZnGNJlv5WWFhoVk7NrNwruaLdB+3EggMLRGVVpeH3\nVVVCXLggRFqaEJMm3RbNm/9WKBTBwssrXDzzTE8xZ85OcfWqtOvVrFkzIYQQFRVCXLwoRErKJ6JX\nr0QxZYoQ/foJ0batEN7eQnTtKsTYsUK8844Qf/ubECdOCKHTmZdVN9NcdXMysjJEyIgQAS0Mt5Dh\nIUKlUhnuk5mZKcLCwsSVK1fMzPIVLVsKkZ//5PtZui61s5zjZZYfEzk4c4eYY8eOxcOHDxEZGQmt\nVitL1vXr1/HSSy/hyJEj8Pb2xu3bt1FeXo62bdtKmqNWq6HT6VBZWYl9+/ZBq9Vi7NixmDlzpkk5\nlmRZo2ZWY9+zPGrUKISFhWHSpCRcvdoWGRk/YM+ev+PmzdkmjR4sXa/Nmzfj+PHjWLVqlWHak/ZN\ntGwJXLu2F7/73SCTRxOWPpYNHU20assqlFWVQeWmQtL4JIwbOQ46nQ779+/H1KlTkZWVhfbt25ud\ntWCBaPSaRVJvFw7NXi1EpjHnT6TT6cSzzz4rCgsLRceOHWXLys7OFgMHDjR7/ubm+Pj41Pr5iy++\nEG3btrVJliXqZjX0PculpaXiueeeMzqPmqOHadOq37F7ewsRHS1ESooQ6elCXL1q+Xp98sknIjEx\n0aR/qx9NAC+J5cuFyaMJSx9Lc9YpJydHBAcHi++/r//91aZmlZSIRkcHcmwXjoojAwdnzruKzz77\nDIcOHcK6devQp08frFixApGRkZJnCSEwcOBAFBQUYMSIEUhOTkaHDh0kz6n7rqy0tBStWrXCzZs3\n4WPi3lBTs5RKJSIiIgAAwcHB2LFjh0nzNyVr/Yna5yOkp6dj165d+PTTT02ab80jl/SX1SguvorR\no4Ma3fdQc70A4Pbt2xgxYgRSU1OtWq8njSbKy9UYOFBn9r4JU/9WHh4e8PX1RU5ODjp37mzyehjL\nauyKpk1pZMAycHDmbEjDhg1DSkoKBg4ciFWrVuHKlSt4/33TT903d6P97rvvsH37dmzatAmffPIJ\nhg4dKmlO3SeiTqdDq1atcOvWLTRr1kzWLEs8KSvvap7he5bD/hOGjF0ZhjJITEzE4cOH8dRTT+HY\nsWON5lRfc6kD0tJ+qHfeQ81LegcGmvYxkTXrVVdlJeDnp8a2bTqzj3QyNadZs2YYOHAggoODsWLF\nCpPXw9g6NXZFU5YBOQxTN6Tbt28jKCgIrVq1gkKhQGVlJRQKBQoLCyXPqmvTpk3Yv3+/ye90LX2B\n3rFjB2bMmIFr166ZvGyOUgbA4/0ILZQt8M+3/onCwkLD1Wxv3bqFF198EZcuXbIoy9joQaUCbtxQ\nY8kSnWH0sGXLJpw4cUK2MgAafiyl2jehVqtx8+ZNDBgwAPHx8XjzzTdNXjZj6/Sk0UFTKgMeWuoi\ntm/fjtdeew1/+ctfDNP69euHQ4cOoXfv3pJm5efnQ6FQIDQ0FBUVFThy5AhiYmIkzaipsrIS2dnZ\nWLZsGebMmSNbjtxqfs9yTvMcTJs1DW/PeRsBAQG4f/++VfP28QH696++AY/Pmo6IAC5cANLSql+I\n27YF1Gpg27bHowdb8fMDunevvtWkPws7OHgZOnQYhLNnqw+JbWg0UVUFVFSokJmZid69e6NNmzaY\nNGmSxcuVnFw9OrhwwfpvQ3Nqcu+UIOuY+ifq37+/+Prrr2tNS01NFdOnT5c868SJEyImJkY8//zz\nokePHmLGjBni/v37kue4u7sbDi3t1q2brIeWqtVqs+ZrTZYQQiz7ZplQdVeJ1u1ai+7du4v+/fuL\n9PR0ybNqrpdOJ8Tvf79JREcnifj46p2ngYFCjB4txLJlQuTlCVFWZnmWEEI8ePBABAYGmnz/xnJK\nSoQ4dkyITz8VYt48IUaNEsLNTS1Uqupl79XrqvD1bS8SEnaJrCwhCguFqKw0MvNGshYsEOK11+rf\nz83NTQQGBhpuy5cvl2S9HBE/JnJwrni2riuukyVZNfcjmPs9y3Jfc6nmvoegoDrXDMo8iNTULJSX\nK+HpWYHk5MGIi6v+wvns7GysX78eW7ZsMXt5zN03Yck1nfTHHdTNunsXaNt2LZ56KgeAF5TKh0hM\n7AutdrrZ62HNetkTy8DBOfKLmaPnOENWY+cjSJllisaOXKqqOoi1a7/GxYuLDP8mJGQeVq6MxZUr\nZ7Fjxw68++67iIqKMjtbqnVqbN9Ex47Avn2rkZqaaCiLDRvW4t13/wUh1hnmo1ROxbx5L1hdCCwD\nkoSjv5g5co6zZFnyPcu2/ba4x0cu/fWv86HTvVvvfrGxb+GrrxZalSX3OtUcTQwdOhNTpqwwjCZ+\n/HEsgM/r/ZsWLcahuHirVbnOUgYOeEUSoqbFU+mJD+M/xMyomej1cS9k5GfYe5EMqg92KsCrrwJr\n1wKRkcaPOSkrc7fpclnC3R1o315/PaeVWLcOyM4Grl8HfH29jP6bigrrroPkTFgGRA4ioVsCvhz3\nJaZmTMXCnIWoElX2XqR6PD0rjE5XqSptvCTS8vB4aHS6Ullm4yWxH5YBkQOJDorGscnHsOeHPXgl\n/RXoyq07xl1qycmDERIyr9a0kJC5SEoaZKclkkZiYl8olVNrTVMqpyAxsY+dlsj2uM/AwTnDZ96O\nmuPMWY3tR7DnemVmHsSqVXtRVuYOlaoSSUmDDEcTSZkjJ2NZWu1arF59EBUVKiiVZUhM7MOjichx\n2PsJ4sw5rpBV97pGcmY1hNuF82RZg2Xg4Fxxo3XFdZIzy9j5CK6wXvbKceUsa7AMHJwrbrSuuE5y\nZ9U9H8FX5esS62WPHFfOsgavTeQE9Bczc6UsV1wn2bPcAQwBfA/5Ai1caL3skGPLLH9/f5vkWItl\n4OBs+Y6C7wCdI2v9ifWY4j0Fu/57V639CHLhdtE08GMiMuCT3omyghRol9LOousamZ3F7aJJYBmQ\nAZ/0zpVVdK/IsB9hgnoCNmzbgHJRDk+FJ5InJCNuUJxkWdwuXB8/JiJyUvrvRxixeAT+a99/4VH/\nR4bfFawpAADJCoFcH89AJnJinkpPiAJRqwgAoEBTgFVbTP82MyKWAZGTKxflRqeXVTWd6+qQ9VgG\nRE7OU+FpdLrKrelccZOsxzIgcnLJE5IRciqk1rSQkyFIGp9kpyUiZ8QyILPt3LkTGo2m1s3d3R1f\nf/21pDlXr15FcHAw7ty5AwC4c+cOgoODceXKFUlz9O7cuYOJEyciODgYnTp1QlxcHC5cuCB5zoAB\nA5CVlVVr2ooVKzB9umUXRYsbFIeVv1uJ2MJY9L3UF7GFsViZuBJxg+Lg7u4OjUaDLl26IC4uDmfO\nnJFiFYxyc3PD7NmzDT8vXboUf/zjH2XJefXVVw0/V1RUoFWrVoiPj5c8q0lp6MuRqemxdHP48MMP\nRb9+/WTJWbJkiUhISBBCCJGQkCAWL15s1rKZk/Xyyy+LP/zhD+Knn34SQgiRl5cnDhw4IHnW+vXr\nxcSJE2tNi4qKEocOHZI8y8fHx/D/6enpYsyYMSZnmJvl6ekpgoODRXFxsRBCiKVLlwqtVit5jo+P\nj9BoNOLhw4dCCCF2794tunbtKuLj4yXPako4MiCr5OfnY+HChfj0009lmX9KSgqOHDmCFStWIDc3\nt9Y7TymVlpbi5MmTeO+999CqVSsAQFRUFPr27St51iuvvILMzExUVFR/Uczly5dx/fp19OrVS/Is\nPSEEiouLoVLJtx/Bw8MDCQkJWL58uWwZekOHDkVmZiYAYMuWLRg/fjzPG7ASy4As9ujRI0yYMAHL\nli1DYGCgLBlKpRJLlizBrFmzsGLFCri7y/P1irt370bv3r1lmXddzZs3R48ePbB7924AwNatWzF2\n7FhZsh4+fAiNRoP27dtDq9XiT3/6kyw5etOnT8dnn32Ge/fuyZozduxYbN26FeXl5Th9+jR69uwp\na15TwDIgi7311luIiIjA6NGjZc3Zs2cPAgICcPr0adkybHmBNAAYP348tm6t/qL1zz//HOPHj5cl\nx8vLC6dOncLly5exdu1ajBo1SpYcPbVajddeew2pqamy5kRERODy5cvYsmUL4uJ4Yp0UWAZkkQMH\nDmDnzp1YvXq1rDn/+Mc/sG/fPuTl5WH58uW4ceOGLDlDhgzBoUOHZJm3McOHD8f+/ftx6tQpPHjw\nABqNRvbMkSNH4ty5c3jw4IGsOTNnzsTGjRtx//59WXOGDx+O2bNn8yMiibAMyGz6o27S0tLQrFkz\n2XKEEJg2bRpWrlyJoKAgzJkzR7Z9Bj4+PoiMjMT8+fNx8+ZNAMB3332HgwcPypbXv39/TJw4ERMm\nTJAlo65vv/0WoaGh8Pb2ljXH398fY8aMwcaNG2UdcU2aNAlarRadOnWSLaMpYRmQ2datW4ebN29i\n6tSptQ4v3bZtm6Q5H330EZ577jkMHDgQQPXn0efOnZPtHfyGDRtw7do19OjRA507d8aCBQvQrl07\nWbKA6o+KTp8+LdtHRMDjfQZdunTBkiVLsGzZMtmyar7wv/HGGyguLpY1p127dkhMTDRMs/VHfa6G\nVy0lA16dkln2zHLFdXImHBkQERHLgIiIWAZERASWARERgWVARERgGRAREVgGREQElgEREQFQ2nsB\nyLHY6ixOW54tyiznybJVjr+/v01ynAnLgAx4RiZR08WPiYiIiGVAREQsAyIiAsuAiIjAMiAiIrAM\niIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CIiMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAM\niIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CIiMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAM\niIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CIiMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAM\niIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CIiMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAM\niIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CIiMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAM\niIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CIiMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAM\niIgILAMiIgLLgIiIwDIgIiKwDIiICCwDIiICy4CIiMAyICIisAyIiAgsAyIiAsuAiIjAMiAiIrAM\niIgILAMiIgLwf2h2MyQ5X7XWAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1117fde50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_kbd(qwerty, words=['JELLO', 'BROOK'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I think these two paths should not be confusions of each other. To see why, I'll start with just the first segment of each path:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAGGRJREFUeJzt3X1wFPXhx/HPJcGEmKQGCpZAiiRAoRDhYtWEByM4SCVE\nq4AKHbU4JQiFQIL0p4CaAlabUSCgjAIigjYIPlFJeFAEAZOMArEFoQJRnmvlKWMC5Apkf3/QOwkN\nD8nt3sPyfs3cSBbZz35ze/e53b3ddRiGYQgAcFUL8fcCAAD8jzIAAFAGAADKAAAgygAAIMoAACDK\nAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoA\nACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAA\nIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAg\nygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDK\nAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAICnM\n3wtwtWjSpImOHz/ukyyHwyHDMMgKgiw7jsmuWb4cU2xsrI4dO+aTLDeH4avRXeXs+OIgK3hyyAqe\nHF9nubGbCABAGQAAOGaAIFb4UaFm/nWmXIZL4Y5wZQ3JUnqfdH8vFhCUKAMEpcKPCjXm5TEqd5Z7\nppW/fO7PFAJQf+wmQlCa+deZtYpAksqd5ZpVMMtPSwQEN8oAQclluOqcXl1T7eMlAeyBMkBQCneE\n1zn9YMVB1Rg1Pl4aIPhRBgHoyJEjevDBB9W2bVu1a9dOkyZN0tmzZ03Pyc7OVn5+vufnvn37atiw\nYZ6fx40bp+nTp5uSFRoaKqfT6Xnk5eV5Nb+sIVlKLEusNa31ptYKaxem0NBQdenaRcnJycrJydF/\n/vMfr7Iu5ejRo54xtWjRQq1atZLT6VRycrJOnz5tWo5hGOrZs6dWrlzpmbZ06VLdddddpmW47dmz\nR0lJSbWm5ebm6sUXXzQ9KyoqyvR5XoqvXlvu9f2mm27S+PHjTV0XrEIZBKDf/e53ateuncrKyrRq\n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\nPXv2aPbs2Ro4cKAlOW7R0dF6+OGHNXPmTEtzkpKStGfPHhUUFCg9nRPrzEAZoEHWrVun999/Xy+9\n9JKlOV9++aU+/vhjlZSUaPr06fruu+8sybnrrru0YcMGS+Zdl7vvvltr1qxRWVmZTp48KafTaXnm\nfffdpx07dujkyZOW5owdO1avvfaaTpw4YWnO3Xffrccff5xdRCahDFBv7m/dLFy4UNdee61lOYZh\naMSIEcrPz1d8fLzGjx9v2TGDqKgoJScna9KkSTp8+LAk6YsvvtD69esty+vVq5eGDh2qIUOGWJJx\noc8++0zt2rVTZGSkpTmxsbG6//779dprr1m6xfXoo48qNzdXnTp1sizjakIZoN5eeeUVHT58WI89\n9litr5cuXbrU1Jy5c+fqhhtu0B133CHp3P7oHTt2WPYJft68eTpw4IBuueUWde7cWZMnT1bLli0t\nyZLO7SraunWrZbuIpB+PGXTp0kV5eXmaNm2aZVnnv/GPGzdOR44csTSnZcuWGjVqlGear3f12Q1X\nLYUHV6cky59ZdhxTMGHLAABAGQAAKAMAgCgDAIAoAwCAKAMAgCgDAIAoAwCApDB/LwACi6/O4vTl\n2aJkBU+Wr3JiY2N9khNMKAN4cEYmcPViNxEAgDIAAFAGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEG\nAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYA\nAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAA\nUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABR\nBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEG\nAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYA\nAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAAUQYAAFEGAABRBgAASf8PqDYBlgVPduwA\nAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10da809d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_kbd(qwerty, words=['JE', 'BR'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that 'J' and 'B' are within 1.5 units of each other, as are 'E' and 'R' (as all neighboring letters must be).  The issue, as I see it, is that 'B' is offset  to the left from 'J' while 'R' is offset to the right.  Together, that seems like too much difference to allow these segments to be confusions of each other.  How much difference is it?  We can define `JE` and `BR` as the vectors for each segment, and find the difference (or distance) between these two vectors:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.23606797749979"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "JE = qwerty['E'] - qwerty['J']\n",
    "BR = qwerty['R'] - qwerty['B']\n",
    "distance(JE, BR)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That seems like too big a difference&mdash;maybe we should limit the difference.\n",
    "Let's experiment with a function, `similar_segments`, that decides whether the last segment in a path is similar to the corresponding segment in a word:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def similar_segments(kbd, path, word):\n",
    "    \"Do the last two letters of path form a similar segment to corresponding letters of word?\"\n",
    "    n = len(path)\n",
    "    if n < 2: return True\n",
    "    # Define endpoints (P1, P2), (W1, W2); then vectors P and W; then difference between.\n",
    "    (P1, P2), (W1, W2) = (path[n-2:n], word[n-2:n])\n",
    "    P = kbd[P2] - kbd[P1]\n",
    "    W = kbd[W2] - kbd[W1]\n",
    "    return distance(P, W) < 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can play with `similar_segments` to make sure it looks right:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "similar_segments(qwerty, 'BR', 'JELLO')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "similar_segments(qwerty, 'HE', 'JELLO')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "similar_segments(qwerty, 'HEP', 'JELLO')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "similar_segments(qwerty, 'J', 'JELLO')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we will modify `confusions` to call `similar_segments`, and then re-test:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def confusions(word, neighbors=neighboring_keys(qwerty), \n",
    "               words=WORDS, prefixes=PREFIXES, kbd=qwerty):\n",
    "    \"All valid words whose paths could be confused with the path for this word.\"\n",
    "    results = set() # A set of words that are confusions of 'word'\n",
    "    Q = ['']        # A queue of partial paths \n",
    "    while Q:\n",
    "        path = Q.pop()\n",
    "        if len(path) < len(word):\n",
    "            for L in neighbors[word[len(path)]]:\n",
    "                newpath = path + L\n",
    "                if (newpath in prefixes) and similar_segments(kbd, newpath, word):\n",
    "                    Q.append(newpath)\n",
    "        elif path in words:\n",
    "            results.add(path)\n",
    "    return results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BETWEEN(2): BERSEEM BERSERK\n",
      "CONFUSION(2): CONCISION CONTUSION\n",
      "DISTINCT(0): \n",
      "EVERYTHING(0): \n",
      "EXTREMES(1): ESTEEMED\n",
      "HELLO(1): JELLO\n",
      "IF(5): ID IT OF OR UT\n",
      "INPUTS(4): IMPURE IMPUTE KNOUTS UNPURE\n",
      "LENGTHS(0): \n",
      "MULTIPLE(0): \n",
      "OF(6): ID IF IT OD OR OX\n",
      "ON(3): IN OH OM\n",
      "OR(5): ID IF OD OE OF\n",
      "PATHS(6): LARGE LARVA LATHE LATHS OATHS PARGE\n",
      "PERFECT(0): \n",
      "POORLY(1): LIKELY\n",
      "SEE(16): ADD ADS ASS AWE DEE DEW ERE ERR ERS EWE SER SEW WED WEE ZED ZEE\n",
      "SOMETHING(0): \n",
      "SOMEWHERE(0): \n",
      "TESTING(6): FEARING GEARING GREYING REARING RESTING TEARING\n",
      "THE(3): RUE RYE TYE\n",
      "VARIOUS(2): CARIOUS CAROLUS\n",
      "WORKS(6): AIRNS ALTOS SIDLE SORNS WORLD WORMS\n",
      "WORLD(6): ALTOS SIDLE SKEPS WIELD WORKS WORMS\n",
      "Total of 75 confusions for 25 words\n"
     ]
    }
   ],
   "source": [
    "test_confusions()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We went from 108 down to 75 confusions.  Studying the results, this looks like an improvement. For \"HELLO\", only \"JELLO\" remains, which looks good. But there are still some confusions that don't look right. For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAFMVJREFUeJzt3XtwVOXhxvEnyYJJSEICxAqCSrjUCAEWhIaEQMURhCCM\nUuQyBaWVCpkEuQQHucgWpGiqgIDWKUipHX8JAiItiYgoAkrAWrYVKIIgKRGmU3PjmgQSz+8PZmOC\nEUn2nJPs8v3M5I8c4H3OCbv77HveczYBhmEYAgDc1AIbegcAAA2PMgAAUAYAAMoAACDKAAAgygAA\nIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAg\nygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDK\nAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoA\nACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAA\nIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAgygAAIMoAACDKAAAg\nydHQO3CzaNGihYqLi23JCggIkGEYZPlAlj8ek79m2XlMUVFRKioqsiXLI8Cw6+hucv745CDLd3LI\n8p0cu7M8OE0EAKAMAACUASzgynCpVe9WioyPVKvereTKcDX0LgH4ESwgw1SuDJcWb1qsimEVVdsW\nb1p89c+edjXQXgH4MSwg28RfF5+uzWrVu5UKhxV+7++1zG6pgk8LTM2yEouSZDVUjt1ZHpwmgqkq\ngipq3x5Y+3YAjQNlAFM5Kms/8+j4ljOSQGNGGTRCBQUFGjNmjDp27KhOnTpp3rx5qqysND1n+vTp\nevnll6u+Hzx4sCZNmlT1/cyZM7Vs2bI6jZk6KlWOnJov/I5sh4o+K5LT6az6ysjI8G7nryMoKEhO\np1M9e/bUjBkzdPnyZcuyCgsLq46pdevWatu2bVX2lStXTMsxDENJSUnatm1b1bYNGzZoyJAhpmV4\n5OXlKS4ursY2l8ull156yfSssLAw08e8HrueW57HYK9evTRr1ixTHwuWMWCLuvyok5OTjXnz5hnn\nzp0zTpw4YYwYMcJ46aWXTM/auHGj8eijjxqGYRiVlZVGr169jISEhKo/79u3r7F///46Zy14YYHR\nsndLI6JPhKGuMtJcaUZYWNgN739dsmrjybpy5YoxfPhw429/+5tlWdW5XK46/T/VNefQoUNGbGys\nUVZWZpw/f97o1KmT8dVXX5medfLkSaNr1641trlcLuPFF180PcvOx4VhePfcqkuO57guX75sDB06\ntM6PwYZ4aWZm0MicP39ehw8f1qJFixQeHq6YmBgtWbJEb7/9tulZffv2VW5uriTp8OHD6tq1q8LD\nw1VSUqLy8nIdOXJEPXv2rPO4rqddKvi0QGf3n9WClQt0occFs3f9hjgcDg0YMEA7d+60LdOwcNGv\nS5cueuihh/TCCy9o4cKFeuyxx9S+fXvL8vyNnc8tjyZNmmjgwIH6+OOPLcswCydyG5mcnBwlJSXV\n2BYbG6uvv/5a//vf/3TrrbealtWmTRs5HA7l5+crNzdXffv21enTp5Wbm6uIiAjFxcXJ4fDuITIt\nfpo6ruio0tJSOZ3Oqu1z5szRqFGjvD2E6zp79qzeffddTZgwwdIcOy1YsEBOp1PBwcH67LPPGnp3\nfIqdzy2PkpISbd26Vc8++6zpY5uNMmiEAgICvrfNMAxdvHjR9KyEhATt3btXe/fu1YwZM3T69Gnt\n3btXzZs3V79+/bwePzI4Uql9UrW4yWK53W4T9vjHeYrn+PHjSkxM1Pjx423JtUNoaKjGjBmj8PBw\nNWnSxJKM2i5rNAyj1selr6ntGKy4jNPzGIyIiNCIESM0YMAAU8e3AqeJGpmhQ4dq9+7dNbYdOXJE\n5eXllpwSSExM1CeffKKDBw8qLi5O8fHxVeWQkJBgSsa0+Gmq+LZCJ4pOmDLejwkJCZHb7dbp06dV\nUFCgrVu32pJrl8DAQEtfmNu2bauSkpIai57//ve/a8zsfNEPPbfatGmjn/zkJ6ZmeR6Du3bt0owZ\nMxQY2Phfahv/Ht5kwsPD1aVLF7lcLp0/f15fffWV5s6dq5SUFEvyEhIStHXrVrVs2VIBAQGKiopS\nSUmJcnNzTSuDyOBINQ1qqsV7Fpsy3o2KiIjQ6tWr9fTTT9t+A48vCwoK0n333afMzExJ0rFjx/T5\n55/7xLvb66ntuTVnzhw98sgjDb1rjQJl0AitW7dOX3zxhXr06KG7775bt912m2XnHLt27arCwkLF\nx8dXbevWrZsiIyPVokUL03IqL1fqjZQ3dE+3e+R0OjVnzhzTxr5W9XfNTqdTHTt21FtvvWVZ3g9l\n+3LOwoULdeDAATmdTj3zzDN65ZVXLHl3a/epp+rPrUGDBik2NlbTp083PccXT6nxcRQ2qe95ydzc\nXE2aNEkbNmxQbGyspVn1UVuWy/WqVq3apYqKEDkcpUpNHSCXK0Wuj1w6dfaU1o5Ya1qWVfiIA7Ia\nKsfurKpMysAe/vpAujbL5XpVixd/roqK16q2ORyTNXduN02bPU4dV3TU/if2q0OLDl5nWYkXGLIa\nKsfurKpMysAe/vpA+t4H1bUarcLC9d/7ey1bjlFBQZZXswN//Bn64zH5a5Y/HlN1rBnAVBUVIT+w\nPVjS1SuL/nr0r7ZdWQTgxlAGMJXDUfoD28skVbvvwOYriwBcH2UAU/3ylwMkTa6xzeF4Uqmp/au+\nZ3YAND6UAUx15UqKEhK6qWXLMWre/HG1bDlGc+d2l8v13X0SzA6AxocFZJv46+JT9az8fKlHD+mL\nL6To6Ov/u5KykjpfWeSPP0N/PCZ/zfLHY6qOmQFM8/zz0hNP/HgRSMwOgMaGmYFN/PVdhSerLrMC\nj7rODvzxZ+iPx+SvWf54TDUyKQN7+OLt6XWzStIFSbPr9s9+Lqm5pC2m7xDgs6KiolRUVGRrJmXQ\nyPnCu5G6zgqq59Rn7aAufOHnR5a9Of6c5Q3WDOC1uqwVXIu1A6BxYGbQyDX2dzD1WSu4NsfK2UFj\n//mRZX+OP2d5g990Bq94MyvwiAyO1KCgQeo/vr86RXfSLQG3aOq4qUp+INm8HQVwXZQB6i0/X8rK\nujor8Eb2+9nat2ufzvQ5ozM6I0k68crVu5MpBMAerBmg3syYFUjSiv9boZO9TtbYdsJ5QiszV3o3\nMIAbxswA9WLWrECSyo3yWreXfVvm/eAAbggzA9SLWbMCSbol4JZatwcHBns/OIAbQhn4mXfeeUeB\ngYE6evSoZRn5+dK6dau1Z88AdevWTU6nU59++mm9x5s6bqo6uGteRdThQAe9/+f35XQ61blzZ/Xu\n3Vtr16617KqMoKAgOZ3Oqq9Tp05ZkuNRXFysJ554Qh06dNA999yj+Ph4vfPOO6bnhIWF1fh+3bp1\nSktLMz3neplWjp+Tk6Of/vSnys/PtzzL33GayM9kZmZq2LBhyszMlMvlsiRj/vwzatZspXbs2KfQ\n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\nOR7h4eGaMGGCVqxYYWlOXFyc8vLylJmZqeTkZEuzbhaUAerlo48+0ubNm7Vq1SpLc/75z39qx44d\nys3N1bJly/Tf//7XkpwhQ4Zoz549loxdm+HDh+uDDz6Q2+3WpUuX5HQ6Lc985JFHdOTIEV26dMnS\nnGnTpun111/XxYsXLc0ZPny40tPTOUVkEsoAdea56uaNN95Qs2bNLMsxDENTpkzRyy+/rHbt2mnW\nrFmWrRmEhYWpZ8+emjdvnr755htJ0t///nft3r3bsrz77rtPEydOtGThuDaffPKJOnXqpNDQUEtz\noqKi9Oijj+r111+3dMb1q1/9Si6XS126dLEs42ZCGaDOXnvtNX3zzTeaPHlyjctLN2zYYGrO6tWr\nddddd+n++++XdPV89JEjRyx7B79mzRp9/fXX6tOnj7p27aqFCxfq9ttvtyRLunqq6ODBg5adIpK+\nWzPo3r27MjIytHTpUsuyqr/wz5w5UwUFBZbm3H777UpNTa3axtVE3uF3IKMKn1tPVkNm+eMx+RJm\nBgAAygAAQBkAAEQZAABEGQAARBkAAEQZAABEGQAAJDkaegfQuNh1F6edd4uS5TtZduVERUXZkuNL\nKANU4Y5M4ObFaSIAAGUAAKAMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACi\nDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIM\nAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwA\nAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAA\nogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACi\nDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAogwAAKIM\nAACiDAAAogwAAKIMAACiDAAAogwAAKIMAACiDAAAkv4fII+XWZrcQ4QAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x11180a6d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_kbd(qwerty, words=['SEE', 'DEE'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To me, these look different because the SE segment is going to the right while the DE segment is going to the left.  Yet they are considered similar:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "similar_segments(qwerty, 'DE', 'SEE')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's extend `similar_segments` to make it check that two corresponding segments aren't going in opposite directions.  If one goes right, the other can't go left; if one goes down, the other can't go up.\n",
    "\n",
    "How do I test that?  We have the vectors for each segment; we want to make sure that the `x` components of the two vectors don't have opposite signs, and that the `y` components don't have opposite signs.  One way to tell if two numbers have opposite sign is to multiply them together and see if the result is negative."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def similar_segments(kbd, path, word):\n",
    "    \"Do the last two letters of path form a similar segment to corresponding letters of word?\"\n",
    "    n = len(path)\n",
    "    if n < 2: return True\n",
    "    # Define endpoints (P1, P2), (W1, W2); then vectors P and W; then difference between.\n",
    "    (P1, P2), (W1, W2) = (path[n-2:n], word[n-2:n])\n",
    "    P = Point(kbd[P2] - kbd[P1])\n",
    "    W = Point(kbd[W2] - kbd[W1])\n",
    "    return (distance(P, W) < 2) and (P.x * W.x >= 0) and (P.y * W.y >= 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can re-test once more:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BETWEEN(2): BERSEEM BERSERK\n",
      "CONFUSION(2): CONCISION CONTUSION\n",
      "DISTINCT(0): \n",
      "EVERYTHING(0): \n",
      "EXTREMES(0): \n",
      "HELLO(1): JELLO\n",
      "IF(5): ID IT OF OR UT\n",
      "INPUTS(1): KNOUTS\n",
      "LENGTHS(0): \n",
      "MULTIPLE(0): \n",
      "OF(6): ID IF IT OD OR OX\n",
      "ON(3): IN OH OM\n",
      "OR(5): ID IF OD OE OF\n",
      "PATHS(6): LARGE LARVA LATHE LATHS OATHS PARGE\n",
      "PERFECT(0): \n",
      "POORLY(1): LIKELY\n",
      "SEE(13): ADD ADS ASS AWE ERE ERR ERS SER SEW WED WEE ZED ZEE\n",
      "SOMETHING(0): \n",
      "SOMEWHERE(0): \n",
      "TESTING(6): FEARING GEARING GREYING REARING RESTING TEARING\n",
      "THE(3): RUE RYE TYE\n",
      "VARIOUS(2): CARIOUS CAROLUS\n",
      "WORKS(6): AIRNS ALTOS SIDLE SORNS WORLD WORMS\n",
      "WORLD(6): ALTOS SIDLE SKEPS WIELD WORKS WORMS\n",
      "Total of 68 confusions for 25 words\n"
     ]
    }
   ],
   "source": [
    "test_confusions()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we're down to 68 confusions, from our initial 108.    I think I'll declare victory on  `confusions`, and move on to answer the question we *really* want to answer.\n",
    "\n",
    "Average Confusions of a Keyboard on a Workload\n",
    "===\n",
    "\n",
    "The question is: how confusing is a keyboard across a workload of words?  First we'll need a workload of words.  That's easy because we already have some `TEXT`, and the function `words` to extract words, `Counter` to otal them up, and `normalize` to make them sum to 1.0.  We'll call the workload of words `WORDLOAD`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "WORDLOAD = normalize(Counter(words(TEXT)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "12020"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(WORDLOAD)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This tells us there were 12,020 distinct words.  We can see the most common words:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('THE', 0.0704858090087631),\n",
       " ('OF', 0.03601344319907194),\n",
       " ('AND', 0.03006522567400811),\n",
       " ('TO', 0.026505393770862834),\n",
       " ('A', 0.02193902792705188),\n",
       " ('IN', 0.02065953562959551),\n",
       " ('I', 0.0174409016724386),\n",
       " ('WAS', 0.013062194698921246),\n",
       " ('THAT', 0.012993955109723572),\n",
       " ('IT', 0.012794922974563694)]"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "WORDLOAD.most_common(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And we can find all the confusing words:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "WORDLOAD_PREFIXES = prefixes(WORDLOAD)\n",
    "\n",
    "CONFUSING = [w for w in WORDLOAD \n",
    "             if len(confusions(w, words=WORDLOAD, prefixes=WORDLOAD_PREFIXES)) > 1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One metric for confusingness is the percentage of words that are confused with other words:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3164\n",
      "12020\n",
      "0.263227953411\n"
     ]
    }
   ],
   "source": [
    "print(len(CONFUSING))\n",
    "print(len(WORDLOAD))\n",
    "print(len(CONFUSING) / len(WORDLOAD))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So 26% of the words in the dictionary are confusing (on a Qwerty keyboard).  But it seems more important if \"THE\" is confusing than if \"REMONSTRATED\" is.  We can weight each word by its frequency in `WORDLOAD`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.5521549493605586"
      ]
     },
     "execution_count": 92,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(WORDLOAD[w] for w in WORDLOAD \n",
    "    if len(confusions(w, words=WORDLOAD, prefixes=WORDLOAD_PREFIXES)) > 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's unfortunate. A majority (55%) of words in running text are confusing. (Unfortunate&mdash;but it shouldn't be surprising. A long word is unlikely to be confused with other words, because a long word has many segments, and if just one segment differs, then there is no confusion.  A short word has only a few segments, and thus fewer chances to differentiate itself from other words.  Running text is mostly short words, while the dictionary is mostly long words; that's why running text has a higher confusingness percentage.)\n",
    "\n",
    "We can wrap this computation up in a function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def confusingness(kbd, wordload=WORDLOAD, prefixes=WORDLOAD_PREFIXES):\n",
    "    \"The proportion of words in wordload that are confused with other words.\"\n",
    "    neighbors = neighboring_keys(kbd)\n",
    "    return sum(WORDLOAD[w] for w in WORDLOAD \n",
    "               if len(confusions(w, neighbors, wordload, prefixes)) > 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And we can make it part of `show_kbd`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def show_kbd(kbd, name='keyboard'):\n",
    "    \"Plot a keyboard and print statistics about it.\"\n",
    "    plot_kbd(kbd)   \n",
    "    print('workload average = {:.1f}; confusingness = {:.0%} for {}'\n",
    "          .format(workload_average(kbd), confusingness(kbd), name))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see how our keyboards do on this new metric:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nhvwfGyro05uKM6QAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x113d851d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.7; confusingness = 37% for 4-by-7\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1117f6cd0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 3.2; confusingness = 55% for qwerty\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x114ffa810>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.5; confusingness = 46% for 5-by-6\n"
     ]
    }
   ],
   "source": [
    "for name in keyboards:\n",
    "    show_kbd(keyboards[name], name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It looks like Qwerty is the worst on all counts. Now we can try the improved keyboards.  Remember that we are still working to improve the workload average, so any change to confusingness will be incidental, not planned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAX0AAAD7CAYAAACG50QgAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEwlJREFUeJzt3X9sVfX9x/HXbQuh5dZZHJsDyq9Rt67UcXEySmFEl8mQ\nyYIMBRMdbMMJcz8osKngrGhmxI0fG8vMYIQtMS2iohktPzZ0QW2Rmd5EqHW4IQHSLUIproWiFM73\nD9Mrl96C+/ZcPuf0/XwkJNxjQl6eC8+ee9rcG/E8zxMAwIQM1wMAAJcP0QcAQ4g+ABhC9AHAEKIP\nAIYQfQAwhOgDgCFEHwAMIfoAYAjRBwBDiD4AGEL0AcAQog8AhhB9ADCE6AOAIUQfAAwh+gBgCNEH\nAEOIPgAYQvQBwBCiDwCGEH0AMIToA4AhRB8ADCH6AGAI0QcAQ4g+ABhC9AHAEKIPAIYQfQAwhOgD\ngCFEHwAMIfoAYEiW6wFB169fPzU3N7uecUmRSESe57mecUns9Bc7/ZOXl6fjx4+7npF2ES/oz4Rj\nYfjLKrHTb+z0Vxh2hmGjH7i9AwCGEH0AMIToA4AhRB8ADCH6AGAI0QcAQ4g+ABhC9AHAEKIPAIYQ\nfQAwhOgDgCFEHwAMIfoAYAjRBwBDiD4AGEL0AcAQog8AhhD9NMvMzFQsFtMXv/hFTZkyRfv27XM9\nqUvNzc363ve+p89+9rP6whe+oLFjx+r55593PStJx/m85pprdP3112v9+vWB/LSjaDTqesIleZ6n\nCRMmaNu2bYljmzZt0uTJkx2u6loYzmkoeLio7p6iaDSa+P3TTz/t3Xbbbd2dlJIfT+X06dO9+++/\n32tsbPQ8z/Pefvtt74knnuj2n3s+v85ne3u7t23bNm/s2LHeypUr/ZiWxM/nPZ26u3Pfvn1eYWGh\nd/r0aa+lpcUrKCjwDhw44NO6j/jx9zPd59RKDvmM3Evo7udm5ubmqqWlRZ7n6cknn9Tu3bv1xz/+\n0ceFH+ruzpMnT2rkyJF65513fFzVmV/ns8PmzZv1gx/8QI2NjX7MS/B7Z7r48bmuP/vZz9S3b1+1\ntrbqE5/4hJYsWeLTuo/4sTPd59TKZ+RmuR7Q07W1tSkWi6m5uVltbW2qq6tzPSmlqqoqjR8/3vWM\n/9nXvvY1NTc3q7W1lZf//08PPfSQYrGY+vTpo9dff931HKQZ0U+z7OxsxeNxSdKzzz6rb33rW6qt\nrXW8qrNIJJL0+N5779Urr7yi3r17a8+ePY5WXZrnefI8r9N+fHw5OTmaOXOmcnNz1atXL9dzkGZ8\nI/cyuvXWW9XQ0KBTp065ntLJ5MmTtWvXrsTL2zVr1mjnzp06evSo42UXt2PHDn3yk59U3759XU8J\ntYyMDL5wGkH0L6NXX31VBQUFysnJcT2lk2g0qi996UtasmRJ4v74yZMnHa/q2tmzZ/XXv/5VK1as\n0OLFi13PAUKD2ztp1nFP/9y5cxoyZIhWrFjhelKX1q1bp0WLFqm0tFT9+/dXNBrV8uXLXc9K0nE+\nT548qSuuuELz58/XnDlzXM/q5NSpU8rPz088XrhwoX7yk584XHRpQb7Sb2tr05VXXul6Ro/AT+9c\nQli+o89Of7HTX93d+dJLL+n3v/+9KioqfFyVLCznsru40gcQaL/73e/07LPP6tFHH3U9pUfgSv8S\nwvLVn53+Yqe/wrAzDBv9wDdyAcAQog8AhhB9ADCE6AOAIUQfAAwh+gBgCNEHAEOIPgAYQvQBwBCi\nDwCGEH0AMIToA4AhPeoN1/r166fm5mbXMwBAkpSXl6fjx4+7npGkR0U/LO+Sx05/sdNfYdgZho1S\nMHdyewcADCH6AGAI0QcAQ4g+ABhC9AHAEKIPAIYQfQAwhOgDgCFEHwAMIfoAYAjRBwBDiD4AGEL0\nAcAQog8AhhB9ADCE6AOAIUQfAAwh+ueJRqOJ31dXV+tzn/ucDh8+7HBRagcPHlRxcXHSsfLycv3q\nV79ytCi1jIwMLVq0KPH4l7/8pR5++GGHi1LLzMxULBbT6NGjVVZWpg8++MD1pC49//zzysjI0D/+\n8Q/XU1LqOJfXXXedFi9erDNnzrielNLhw4c1fPjwxMerNjc3a/jw4Tp06JDjZelH9M8TiUQkSTt3\n7tSPf/xjbdu2Tfn5+Y5XfTwd24Okd+/e2rx5s5qamiQFc6Mk5eTkKB6Pa8+ePfrXv/6lHTt2uJ7U\npYqKCn3jG99QRUWF6ykpdZzL3bt3680339T27dtdT0opPz9f8+bN03333SdJuu+++/T9739fgwcP\ndrws/Yj+BXbt2qW7775bVVVVGjZsmOs5odarVy/dfffdWrlypespH0tWVpYmTpyol156yfWUlFpb\nW/Xaa69pzZo12rhxo+s5F9WrVy/deOONeuWVV1xP6dKCBQu0e/durVq1SjU1NUmvSnsyon+e06dP\n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\nSUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x111713410>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.8; confusingness = 29% for repeated improved 4-by-7\n"
     ]
    },
    {
     "data": {
      "image/png": "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\nHmTne9aw1Xi3vPGYvHXLUzshISEe2bkRMfCQur6a0Btf9eiNx8RW49nx5q364GEiAAAxAAAQAwCA\niAEAQMQAACBiAAAQMQAAiBh4jeLiYkVERMjpdEqSnE6nIiIidObMGcu3Jk2apCVLlrg/79+/v8aN\nG+f+/K233tKiRYvqvRMUFHTT559//rlef/31ep/v7Zw/f14Oh0MOh0Nt27ZVaGioHA6HYmNjde3a\nNUu3/Pz83FsOh0Pz5s2z9PxvtxUbG6u0tDT9/ffftm0VFRUpJibmptMyMzP1/vvvW7rjOqYePXoo\nPT3d8q/P7dz6d9EbEQMvERYWpvHjx2vq1KmSpKlTp+rVV19Vhw4dLN/q3bu3cnNzJUk1NTU6f/68\njhw54v7veXl5SkxMrPfOra/2tPPVn61bt1ZBQYEKCgr02muvKS0tTQUFBcrPz1eTJk0s3QoICHBv\nFRQUaMqUKZae/+22Dhw4oFOnTmnnzp22bd2OHV8z1zHt379fR44c0Y4dOyzfuJUnX03dUIiBF5k0\naZL279+vxYsXKzc3V5MnT7Zlp1evXsrLy5MkHT58WNHR0QoODlZFRYWuXr2qo0ePKjY21vJdT76K\nszG8YrQ2/P39lZSUpOzs7Ia+KJZp0qSJ+vXrpx9++KGhL4pX4O0ovIi/v7/mzZunAQMGaNeuXfLz\n87Nlp127dvL391dxcbHy8vLUq1cvlZSUKC8vT82bN1dMTIz8/ev/V6uqqkoOh8P9eXl5uQYPHlzv\n821otx7X9OnTlZKSYuvmX3/9pW3btik1NdXWHU+qqKjQ5s2b9c477zT0RfEKxMDLbNu2Te3atVNh\nYaGeeuop23YSEhKUm5ur3NxcpaWlqaSkRLm5uWrRooV69+5tyUazZs1UUFDg/vyLL77Qzz//bMl5\nN6Rbj8tOrvCcPHlSiYmJGj16tG1bt3sPHmOM5Q+xuI6pefPmGjx4sJKSkiw9/3sVDxN5kV9++UW7\nd+9WXl6eFi1apN9//922rcTERO3bt0+FhYWKiYlRfHy8Ow4JCQm2bHrbQzee4ApPSUmJysrKtHnz\nZtu2QkNDVVFRcdMTukeOHLnpXpAVXMeUk5OjtLQ0+frybcwKXItewhij8ePHa8mSJQoLC1N6erpt\nzxlI/9wz2Lx5s1q3bi0fHx+FhISooqJCeXl5tsUAdde8eXMtW7ZMU6ZMsS2qfn5+6tu3r7KysiRJ\n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\nRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1140d81d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.9; confusingness = 35% for repeated improved qwerty\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x114fdf650>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 1.7; confusingness = 35% for repeated improved 5-by-6\n"
     ]
    }
   ],
   "source": [
    "report(keyboards)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Good to see that the \"improved\" keyboards actually improve confusingness, as well as workload average. Of course, we haven't yet explicitly searched for  a keyboard with a good confusingness  score. Could we?\n",
    "\n",
    "Question 6: Is there a Keyboard that Minimizes Confusion?\n",
    "===\n",
    "\n",
    "Unfortunately, we won't get very far towards solving this problem.  Consider:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 3.62 s, sys: 174 ms, total: 3.79 s\n",
      "Wall time: 3.68 s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.5521549493605586"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time confusingness(qwerty)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 375 µs, sys: 126 µs, total: 501 µs\n",
      "Wall time: 419 µs\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "3.2333097802127644"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time workload_average(qwerty)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Computing `confusingness` takes thousands of times longer than computing `workload_average`, so `confusingness`, as it stands, is not a good candidate for a scorer function in the inside loop of `improved`.  We'd need some way of factoring `confusingness` into pieces that can be recomputed incrementally to make things go faster.  So for now, I'll make a token effort of scoring with confusingness, but with a paltry 20 swaps (designed to take around a minute of computation):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 47.9 s, sys: 1.21 s, total: 49.1 s\n",
      "Wall time: 48.3 s\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n/R/Uap3YhyFm2AAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x111810290>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 3.1; confusingness = 16% for keyboard\n"
     ]
    }
   ],
   "source": [
    "%time kbd = improved(qwerty, swaps=20, scorer=confusingness)\n",
    "show_kbd(kbd)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This did indeed reduce confusingness; not bad for only 20 swaps. Too bad the workload average got worse."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Question 7: Is there a Keyboard that Maximizes User Satisfaction?\n",
    "===\n",
    "\n",
    "What is user satisfaction? I don't know, but for now I'll approximate satisfaction (or rather, *dissatisfaction*, since lower scores are better) with a combined score that is the average of workload average and 6 times confusingness. Why 6 times? Because our best scores are around 1.8 for workload average and 0.3 for confusingness, and I want both factors to weigh about the same.\n",
    "\n",
    "First we'll define the combined scorer function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def combined_scorer(kbd):\n",
    "    \"The average of workload average and 6 * confusingness.\"\n",
    "    return mean([workload_average(kbd), 6 * confusingness(kbd)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can incorporate the combined score into `show_kbd`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def show_kbd(kbd, name='keyboard'):\n",
    "    \"Plot a keyboard and print statistics about it.\"\n",
    "    plot_kbd(kbd)   \n",
    "    W = workload_average(kbd)\n",
    "    C = confusingness(kbd)\n",
    "    print('workload average = {:.1f}; confusingness = {:.0%}; combined = {:.1f} for {}'\n",
    "          .format(W, C, mean([W, 6 * C]), name))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's get a baseline for our keyboards to see which performs best on the combined metric."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nhvwfGyro05uKM6QAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x114fbd590>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.7; confusingness = 37%; combined = 2.5 for 4-by-7\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x11180abd0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 3.2; confusingness = 55%; combined = 3.3 for qwerty\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1140d18d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.5; confusingness = 46%; combined = 2.6 for 5-by-6\n"
     ]
    }
   ],
   "source": [
    "for name in keyboards:\n",
    "    show_kbd(keyboards[name], name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It looks like 4-by-7 gets the best score.  Let's try a few swaps to improve it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 42.3 s, sys: 725 ms, total: 43 s\n",
      "Wall time: 42.6 s\n"
     ]
    },
    {
     "data": {
      "image/png": "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\nBg0apPz8fEUiEZWWlqq9vd31vITjx4+roKAg8daQsVhMBQUFOnbsmONlyTIzMxOP3/z58/Wf//zH\n9aSUbr31Vv3+979Puu3xxx9XZWWlo0VdeZ6nsWPH6uWXX07ctn79ek2YMMHhqq5ycnIS/11bW6tP\nfepTOn78uMNFqW3atCnxvd7xkZmZqW3btrme1vu8q4iff5yqqipv5cqVvt1fZ37sXLFihTdnzhzP\n8zxvzpw53k9+8pMe3+elerozJyfH8zzPa29v9yZNmuT97ne/82NWFz3d+dRTT3kVFRVJt40cOdLb\nuXNnj+73Uj3defDgQa+4uNg7d+6cF4/HvaKiIu/IkSM+rfuvnuzseM63b9/uFRYW9so+z/P3e93z\nPO9Xv/qV98UvftHX+/Q8/3f6gffI7caDDz6onJwcLViwwJf768yPnefPn9ctt9yiiooKPf3003rj\njTeUmZnp08IP9HRnOBxWPB6XJK1atUonT57UypUr/ZqX0NOdzc3NKi4u1smTJ5WVlaXGxkaVl5fr\n7bff9nGlP8/7D37wAw0YMECtra269tprtWTJEp/W/VdPdobDYW3ZskUVFRXaunWrhgwZ4vO6D/j5\nvX748GHddtttikajys/P9+U+O6Tje+RmuR6A/5+srCytWLFCEyZM0CuvvOJ78P30r3/9S1u3btWM\nGTNcT0kpLy9PI0aMUG1trSZNmqSamhpNmTLF9ayUli1bpkgkouzsbO3bt8/1nC7OnTunu+66S3/+\n8597Lfh+am9v17333qtVq1b5Hvx0xZl+gG3dulU33HCDDhw44HpKSm1tbYpEIsrPz1dmZqamT5/u\nelK3pk2bppqaGknS888/r2nTpjlelFr//v01depUTZ8+XX369HE9p4u+ffuqrKxMa9eudT3lf/LA\nAw9o2LBhmjx5suspVwzRD6g33nhD27dvVzQa1WOPPaZ33nnH9aQu+vXrp7q6Op08eVJNTU3avHmz\n60ndmjRpknbs2KG6ujq99957ikQirid1KyMjQ6FQyPWMlDIyMvTCCy9o7969Wr58ues5H+pPf/qT\nNm3apNWrV7ueckUR/QDyPE/f/va39cQTT2jw4MFatGiRFi5c6HpWt6655hqtWbNGixcvTrvzzQ45\nOTkaN26cKioqdO+997qeE2jZ2dnasmWLnnvuOT3zzDOu56QUi8VUUVGhX//61xowYIDrOVcU0f8Q\n6Xo1tWbNGn384x/XbbfdJkmqrKxUQ0ODdu7c6XhZss6PXyQSUWFhoV544QWHiz7ctGnTdODAgbQ9\n2uksXb82O3bl5ubq5Zdf1iOPPJKWP+H98pe/1OnTp/Wtb30r6dc2169f73par+O3dxxgp7/Y6a8g\n7AzCRik9d3KlDwCGEH0AMIToA4AhRB8ADCH6AGAI0QcAQ4g+ABhC9AHAEKIPAIYQfQAwhOgDgCFE\nHwAMIfoAYAjRBwBDrrr3yE3X1xm/FDv9xU5/BWFnEDbm5ua6ntDFVRX9dHvdagBINxzvAIAhRB8A\nDCH6AGAI0QcAQ4g+ABhC9AHAEKIPAIYQfQAwhOgDgCFEHwAMIfoAYAjRBwBDiD4AGEL0AcAQog8A\nhhB9ADCE6AOAIUQfAAwh+gBgCNEHAEOIPgAYQvQBwBCiDwCGEH0AMIToA4AhRB8ADCH6AGAI0QcA\nQ4g+ABhC9AHAEKIPAIYQfQAwhOgDgCFEHwAMIfoAYAjRBwBDiD4AGEL0AcAQog8AhhB9ADCE6AOA\nIUQfAAwh+gBgCNEHAEOIPgAYQvQBwBCiDwCG/B/CI60K6hV8rQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115db02d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.9; confusingness = 6%; combined = 1.6 for keyboard\n"
     ]
    }
   ],
   "source": [
    "%time kbd = improved(keyboards['4-by-7'], swaps=30, scorer=combined_scorer)\n",
    "show_kbd(kbd)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Success!  We were in fact able to make progress on the combined metric.  If we could run 30,000 swaps instead of just 30, we could probably do even better. To do so would require either (1) more computers, (2) more patience, or (3) more efficient algorithms.  I'll leave it up to you to make more progress.\n",
    "\n",
    "**Note**: Each time this notebook is run, different random results can occur.  I'll record a keyboard found by one\n",
    "of the good runs (only 6% confusingness) here, just in case another run is not as good:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nBg0apPz8fEUiEZWWlqq9vd31vITjx4+roKAg8daQsVhMBQUFOnbsmONlyTIzMxOP3/z58/Wf//zH\n9aSUbr31Vv3+979Puu3xxx9XZWWlo0VdeZ6nsWPH6uWXX07ctn79ek2YMMHhqq5ycnIS/11bW6tP\nfepTOn78uMNFqW3atCnxvd7xkZmZqW3btrme1vu8q4iff5yqqipv5cqVvt1fZ37sXLFihTdnzhzP\n8zxvzpw53k9+8pMe3+elerozJyfH8zzPa29v9yZNmuT97ne/82NWFz3d+dRTT3kVFRVJt40cOdLb\nuXNnj+73Uj3defDgQa+4uNg7d+6cF4/HvaKiIu/IkSM+rfuvnuzseM63b9/uFRYW9so+z/P3e93z\nPO9Xv/qV98UvftHX+/Q8/3f6gffI7caDDz6onJwcLViwwJf768yPnefPn9ctt9yiiooKPf3003rj\njTeUmZnp08IP9HRnOBxWPB6XJK1atUonT57UypUr/ZqX0NOdzc3NKi4u1smTJ5WVlaXGxkaVl5fr\n7bff9nGlP8/7D37wAw0YMECtra269tprtWTJEp/W/VdPdobDYW3ZskUVFRXaunWrhgwZ4vO6D/j5\nvX748GHddtttikajys/P9+U+O6Tje+RmuR6A/5+srCytWLFCEyZM0CuvvOJ78P30r3/9S1u3btWM\nGTNcT0kpLy9PI0aMUG1trSZNmqSamhpNmTLF9ayUli1bpkgkouzsbO3bt8/1nC7OnTunu+66S3/+\n8597Lfh+am9v17333qtVq1b5Hvx0xZl+gG3dulU33HCDDhw44HpKSm1tbYpEIsrPz1dmZqamT5/u\nelK3pk2bppqaGknS888/r2nTpjlelFr//v01depUTZ8+XX369HE9p4u+ffuqrKxMa9eudT3lf/LA\nAw9o2LBhmjx5suspVwzRD6g33nhD27dvVzQa1WOPPaZ33nnH9aQu+vXrp7q6Op08eVJNTU3avHmz\n60ndmjRpknbs2KG6ujq99957ikQirid1KyMjQ6FQyPWMlDIyMvTCCy9o7969Wr58ues5H+pPf/qT\nNm3apNWrV7ueckUR/QDyPE/f/va39cQTT2jw4MFatGiRFi5c6HpWt6655hqtWbNGixcvTrvzzQ45\nOTkaN26cKioqdO+997qeE2jZ2dnasmWLnnvuOT3zzDOu56QUi8VUUVGhX//61xowYIDrOVcU0f8Q\n6Xo1tWbNGn384x/XbbfdJkmqrKxUQ0ODdu7c6XhZss6PXyQSUWFhoV544QWHiz7ctGnTdODAgbQ9\n2uksXb82O3bl5ubq5Zdf1iOPPJKWP+H98pe/1OnTp/Wtb30r6dc2169f73par+O3dxxgp7/Y6a8g\n7AzCRik9d3KlDwCGEH0AMIToA4AhRB8ADCH6AGAI0QcAQ4g+ABhC9AHAEKIPAIYQfQAwhOgDgCFE\nHwAMIfoAYAjRBwBDrrr3yE3X1xm/FDv9xU5/BWFnEDbm5ua6ntDFVRX9dHvdagBINxzvAIAhRB8A\nDCH6AGAI0QcAQ4g+ABhC9AHAEKIPAIYQfQAwhOgDgCFEHwAMIfoAYAjRBwBDiD4AGEL0AcAQog8A\nhhB9ADCE6AOAIUQfAAwh+gBgCNEHAEOIPgAYQvQBwBCiDwCGEH0AMIToA4AhRB8ADCH6AGAI0QcA\nQ4g+ABhC9AHAEKIPAIYQfQAwhOgDgCFEHwAMIfoAYAjRBwBDiD4AGEL0AcAQog8AhhB9ADCE6AOA\nIUQfAAwh+gBgCNEHAEOIPgAYQvQBwBCiDwCG/B/CI60K6hV8rQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1159ad550>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "workload average = 2.9; confusingness = 6%; combined = 1.6 for keyboard\n"
     ]
    }
   ],
   "source": [
    "show_kbd(Keyboard((' A B E D Q F ',\n",
    "                   'G U M J I L H',\n",
    "                   ' N O P W C S ',\n",
    "                   'T X R V Y K Z')))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Summary\n",
    "=====\n",
    "  \n",
    "  So where are we? Let's revisit our initial questions and see what answers we have:\n",
    "  \n",
    " 1. What words have the longest path length? <br>**Answered**: \"PALEOMAGNETISMS\" etc.\n",
    " 2. What words have the highest ratio of path length to word length? <br>**Answered**: \"PALAPA\" etc.\n",
    " 3. What is the average segment length, over a typical typing work load? <br>**Answered**: 3.23 keys, for Qwerty keyboard, on our sample workload.\n",
    " 4. Is there a better keyboard layout to minimize the average segment length over a work load?  <br>**Answered**: Yes, many layouts at around 1.9 on Qwerty; or 1.7 or 1.8 on more square keyboards.\n",
    " 5. How often are two words confused because they have similar paths? <br>**Answered**: On Qwerty, 26% of the words in a small dictionary, and 55% of the words in running text have at least one possible confusion. Other layouts are worse.\n",
    " 6. Is there a better keyboard layout to minimize confusion? <br>**Partially Answered**: We found a keyboard with less confusingness than Qwerty.  The computation of confusingness takes too long to do very much hillclimbing search.  \n",
    " 7. Is there a better keyboard layout to maximize overall user satisfaction? <br>**Partially Answered**: We defined a combined metric, and found\n",
    "a keyboard with a better score. There are no doubt better metrics, and better keyboards to be found.\n",
    " \n",
    "\n",
    "\n",
    "\n",
    "Going Beyond\n",
    "===\n",
    "\n",
    "Now it is your turn to answer the open questions, or make up some questions of your own.  Good luck! Here are a few ideas to get you started:\n",
    "\n",
    "* Hillclimbing just keeps the one best keyboard it has found so far.  Other optimization techniques such as\n",
    "[beam search](http://en.wikipedia.org/wiki/Beam_search) or [genetic algorithms](http://en.wikipedia.org/wiki/Genetic_algorithm)  or [ant colony optimization](http://en.wikipedia.org/wiki/Ant_colony_optimization_algorithms) maintain several candidates at a time. Is that a good idea?\n",
    "\n",
    "* The code in this notebook emphasises clarity, not efficiency.  Can you modify the code (or perhaps port it to another language) and make it twice as efficient? 10 times? 100 times?\n",
    "\n",
    "* What other factors do you think are important to user satisfaction with a keyboard.  Can you measure them?\n",
    "\n",
    "* Consider the 5 paths below. They all start at 'P', move in a straight line to 'T', and then go to 'S', but they all make different stops along the top row. In other words, the 5 paths all trace exacty the same lines, so they are very confusing, but our definition of  `confusions` makes most of them different. Can you think\n",
    "of a better way to handle confusions for paths like this?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD7CAYAAACIYvgKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAHPZJREFUeJzt3XlYVPXix/HPAAooLpBomriRpletcEVwKbuJSmFZVtov\nb2ba1Qcwt3JNtKybaSpa3cqyW9fQtEftirnkUpa23KTrkmnqtUzr5oK5AQJ+f38YxDLDMswZBny/\nnsfnkcOc85nvzMBnvuecOdiMMUYAgKuaV3nfAQBA+aMMAACUAQCAMgAAiDIAAIgyAACIMgAAiDIA\nAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAA\niDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACI\nMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgy\nAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIA\nAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAEjyKe87cLUI\nCgpSamqqW7JsNpuMMWRVgKzKOKbKmuXOMQUGBur06dNuycphM+4a3VWuMv5wkFVxcsiqODnuzsrB\nbiIAAGUAAOCYAVAiyRuTlfhuojJMhnxtvoofFK/o26OLXe+T5GRtSEyUT0aGsnx91Ss+Xt2ji18P\nVzjz+Dn7XDmjMr0uKAOgGMkbkzXqpVE6FHYod9mhl678v6gf/E+Sk7V+1CjNPPTHepN//395/+BX\nBM48fs4+V86obK8LDiC7SWU9+HQ1ZEUNidKGJhsK3abWZ7XU8r6WDrdRc84+bfjubKHlj/t76dFr\n/SVJ589fUEBAdRfd66JVtKzXf07T/PTLhZbnffwKZqWdTld2QHahdbzPe8s/yK9M96fgmBxl1bpU\nS62aOX5dTNm3T8+cLfy6mBoVpafXrZNUPgeQmRkADmRmZ2r1/tX68ucvpSaFv9/smmaa13teoeX+\n3x1S3XdX65Xvv7K73SrNrpeeHS9JevzRYVo0r/A2rFDRsqpOekHae6DQ8ryPX8GshQte0P7Ghde5\n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\n9yg5OVlZWVmSpCNHjuj48ePq2rWry7NyGGN08uRJ+flZ92eGqlSpouHDh2vu3LmWZeTo27evkpOT\nJUlJSUkaOHAgnyguI8oATsvMzNSgQYP04osvqmHDhpZk+Pj4aNasWRozZozmzZsnb++i/+yms9au\nXatu3bpZsu2CgoKC1KlTJ61du1aStHTpUt1///2WZKWlpSksLExNmzZVQkKCnn32WUtycowcOVJL\nlizR2bNnLc25//77tXTpUmVkZGj37t3q3LmzpXlXA8oATps6daratm2rAQMGWJrz4YcfqkGDBtq9\ne7dlGe7exZB3V9GyZcs0cOBAS3L8/f2VkpKiI0eO6OWXX9a9995rSU6OGjVqaPDgwUpMTCz+xmXQ\ntm1bHTlyRElJSYqOjrY062pBGcApW7du1cqVK7Vw4UJLc7755ht99NFH2rFjh+bOnatffvnFkpw+\nffpo27ZtlmzbnpiYGG3atEkpKSm6ePGiwsLCLM/s37+/9u3bp4sXL1qa8/jjj+uNN97QhQsXLM2J\niYnRuHHj2EXkIpQBSi3nrJu3335b1atXtyzHGKMRI0Zo/vz5CgkJ0fjx4y07ZhAQEKB27dppypQp\nOnHihCTpq6++0ieffGJZ3q233qohQ4ZYcuDYns8++0zNmzdXtWrVLM0JDAzUfffdpzfeeMPSGdcj\njzyihIQEtW7d2rKMqwllgFL7+9//rhMnTuivf/1rvtNLly9f7tKc119/XU2aNNFtt90m6cr+6H37\n9ln2Dn7RokX66aef1KlTJ7Vp00YzZszQddddZ0mWdGVX0e7duy3bRST9cczgpptu0qxZs/Tiiy9a\nlpX3F//YsWN18uRJS3Ouu+46xcbG5i7jbKKy4W8gIxfXrSerPLMq45gqEmYGAADKAABAGQAARBkA\nAEQZAABEGQAARBkAAEQZAAAk+ZT3HYBncdenON35aVGyKk6Wu3ICAwPdklORUAbIxScygasXu4kA\nAJQBAIAyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACI\nMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgy\nAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIA\nAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAA\niDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACI\nMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgyAACIMgAAiDIAAIgy\nAACIMgAAiDIAAIgyAACIMgAASPp/AVkqmw1ike4AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x115a44b90>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_kbd(qwerty, words=['PUTS', 'POTS', 'PITS', 'POUTS', 'PUTTS'])"
   ]
  }
 ],
 "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.5.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
