{
 "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",
    "* **Length**: Paths and Segments have lengths; the distance travelled along them.\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 (or each segment) 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 `import`s I will need throughout this notebook and gathered them together here:\n",
    "\n",
    "\n",
    "             "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from collections import Counter\n",
    "from statistics import mean\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": true
   },
   "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": true
   },
   "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": [
    "Alternative representations for points include an `(x, y)` tuple or namedtuple, or a NumPy two-element array, or a class. \n",
    "\n",
    "Alternatives for `Keyboard` include a subclass of `dict`, or a class that contains a `dict`.  \n",
    "\n",
    "\n",
    "Now we can check that `Keyboard` works:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "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": {},
   "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": true
   },
   "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": {},
   "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": {},
   "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": {},
   "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": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "! [ -e TWL06.txt ] || curl -O http://norvig.com/ngrams/TWL06.txt\n",
    "    \n",
    "WORDS = set(open('TWL06.txt').read().split())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "178691"
      ]
     },
     "execution_count": 11,
     "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": {},
   "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 ten longest 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": {},
   "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, formatter='{:.1f} {}'.format):\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(formatter(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": true
   },
   "outputs": [],
   "source": [
    "def path_length_ratio(word, kbd=qwerty): return path_length(word, kbd) / len(word)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "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": [
    "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. We will read a file of \"typical\" text, and count how many times each segment is used. A `Workload` is a `dict` of the form `{segment: proportion, ...},` e.g. `{'AB': 0.02}`, where each key is a two-letter string (or *bigram*) 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 can combine them together into one count;\n",
    "I'll arbitrarily choose count them both under the alphabetically first one. I make a `Counter` of all two-letter segments, and `normalize` it so that the counts sum to 1 (and are thus probabilities)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "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",
    "    segments = (min(AB, AB[::-1]) for AB in bigrams(text))\n",
    "    return normalize(Counter(segments))\n",
    "\n",
    "def bigrams(text): return re.findall(r'(?=([A-Z][A-Z]))', text)\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": [
    "*Note:* Some `re` trickiness here: `([A-Z][A-Z])` means a group of two consecutive letters. But if I only looked for that, then in the text `'FOUR'` I would find `['FO', 'UR']`. So I use the `?=` operator, which says to check for a match, but don't consume the matched characters. So I can find `['FO', 'OU', 'UR']`, which is what I want.\n",
    "\n",
    "Let's see what a workload looks like for a tiny text:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({'AL': 0.05,\n",
       "         'AO': 0.05,\n",
       "         'DO': 0.05,\n",
       "         'GO': 0.1,\n",
       "         'HO': 0.05,\n",
       "         'HS': 0.05,\n",
       "         'IS': 0.05,\n",
       "         'OO': 0.55,\n",
       "         'OT': 0.05})"
      ]
     },
     "execution_count": 17,
     "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": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "! [ -e smaller.txt ] || curl -O http://norvig.com/ngrams/smaller.txt\n",
    "    \n",
    "WORKLOAD = Workload(open('smaller.txt').read().upper())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's peek at the most common segments:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "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": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "WORKLOAD.most_common(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The most popular segments, at about 4% each are `HT/TH` and `ER/RE`. Now we can compute the workload average:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "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": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.2333097802127653"
      ]
     },
     "execution_count": 21,
     "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 `matplotlib` module. In the function `show_kbd` we'll draw a square around the center point of each key, and annotate the square with the key letter. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def show_kbd(kbd, name='keyboard',  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",
    "    for L in kbd:\n",
    "        x, y = K * kbd[L].x, -K * kbd[L].y\n",
    "        plot_square(x, y, H, label=L)\n",
    "    plt.axis('equal'); plt.axis('off')\n",
    "    plt.title(title(kbd, name));\n",
    "    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 title(kbd, name): return '{}: path length = {:.1f}'.format(name, workload_average(kbd))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n+DNG2sdQVZcAOyd54dxpSR6b5OgRsjYAl9Jdj2M/qN3/TDrJo+nu/98eK6efKrsQeOGWL77tWXTX\n3xur6toRs7YbFoO2xYuAA4BzBi9XXZXklGkHJflPwI39gzbAOcCjkzx52lm9k4HjkqxJshp4M/DN\nkbLOp5sbH7sYdpn7P/VZp9U4n7s/XOY7gP0mTpt6VlV9o6r+fKSM7Y7fx6DGcv0sfLOWRs5yzlpK\n3GKQJDUsBklSw2KQJDUsBklSw2KQJDUsBklSw2KQJDUsBklSwze4qZHEG4S2J+uqahbfG7GkWAyS\npIZTSZKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpY\nDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKk\nhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUg\nSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpY\nDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKk\nhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUg\nSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpYDJKkhsUgSWpY\nDJKkhsUgSWpYDJKkxv8HwqVDBjxNM0gAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10a0b8e10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_kbd(qwerty, '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": 24,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def improved(kbd, swaps=1000, scorer=workload_average):\n",
    "    \"Minimize scorer(kbd) by swapping keys and keeping improvements.\"\n",
    "    kbd = kbd.copy()\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`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see how well we can do:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1044fc8d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_kbd(improved(qwerty, 3000))"
   ]
  },
  {
   "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 curve. We will repeat that 10 times (because each run has random variation).\n",
    "\n",
    "I'll add another parameter, `scores`, to `improved`, If it is not `None`, then it should be a list into which we can accumulate all the scores (after each time step). Then I'll add a new function, `plot_improvements`, that plots the scores that `improved` accumulates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "def improved(kbd, swaps=1000, scorer=workload_average, scores=[]):\n",
    "    \"Minimize scorer(kbd) by swapping keys and keeping improvements.\"\n",
    "    kbd = kbd.copy()\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",
    "        scores.append(score)               # <<< NEW\n",
    "    return kbd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10a20f5f8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_improvements(kbd, swaps, scorer=workload_average, repeats=10):\n",
    "    plt.ylabel('Workload average segment length') \n",
    "    plt.xlabel('Number of swaps');  \n",
    "    for _ in range(repeats):\n",
    "        scores = []\n",
    "        improved(kbd.copy(), swaps, scorer, scores)\n",
    "        plt.plot(scores)\n",
    "    plt.show()\n",
    "    \n",
    "plot_improvements(qwerty, 2500)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This plot is interesting. I note the following:\n",
    "\n",
    "* Each curve follows a slightly different path (because the swaps are random).\n",
    "* The curves are grouped very tightly together; the variance is small.  Almost everywhere, the difference between the best and the worst is about 0.2 or less. By the end, almost all the curves 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, even slower."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I can't say we've found the best possible keyboard, but it looks like progress will be slow in improving over about 1.9. \n",
    "\n",
    "Keys in Different Physical 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": 28,
   "metadata": {
    "collapsed": true
   },
   "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 K',\n",
    "                        'L M N O P Q',\n",
    "                        ' R S T U V',\n",
    "                        '  W X Y Z '))\n",
    "    }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is a function to report on a collection of keyboards such as this (after improving them):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYYAAAEKCAYAAAAW8vJGAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAFqxJREFUeJzt3Xm4JXV95/H3B5BNbAUxLiBEo0ZH3IjbaAQBiREyKjEY\nMAw+4xKMOjJxi+goxiRoxgWNC2oMRIyK4Iia6KgTcQtEJ7GRgMQASlgEFOxWkQaH5Zs/qm57fte+\n3bel6V/dvu/X89ynT506p+pz65xbn9r6nFQVkiTN2ap3AEnStFgMkqSGxSBJalgMkqSGxSBJalgM\nkqSGxTAxSc5Lsk/vHJtSkj2T3JJkk7zfklycZP9NMa0pSfLMJF/ZiMd3WQ6b+vXU9PjCTkxV7VVV\nX+6d4zaw4H+YSfLFJNcn+XGSa5P86+YMluQJSb6e5CdJLk3yO5thngutXCf3H4sWKKDbPGeSQ5Oc\nmeS6JGds4LHHjO+dH48/a5LclGSX2zrnlshiWKaSbN07w4wCnl9VK6rqDlX1gM014yT/CfggcAyw\nAngI8PXNMWuG3zubYV5L1Q+A44HXb+iBVfX68b2zoqpWAH8OfLGqVt3WIbdEFsPEzG6dJTk2yalJ\nPjBuBZ2T5L5JXpHke0kuSXLgzHO/kOS4JF9L8qMkpye50zhubgv1WUkuAT4/3v/k8fDVqiRnJLn/\neP/Lk5w2L9vbkrx1vL0iyfuSXJHksiR/kiTjuK2SvCnJ1UkuAg5ezK++kYvqkUm+meQHSU5Msu04\n73OTrJ1fkm3GHA9ZYDqvAt5dVZ+rqluqanVVXbyYAEn2HX/3Y8Z5fCfJM2bGH5Rk5fhaXJLk2Jmn\nf2n894fja/uonz0tbxxfj28n+c1FZsn4vrhozHLKOl77I8cc30/yypnnbp/k/eM8v5nkZUkuG8ed\nDOwB/O2Y86VzTwOOWNf0NpWqOqOqPgpc+Qs8/UjgrzdtomWkqvyZ0A9wMbD/ePtYYA3wBIYSfz/w\nHYat262B5wDfmXnuF4DLgAcAOwAfBT4wjtsTuIXhj2UHYDvgvsBPgP3H6b0MuBDYhmFl8BPg9uPz\ntwKuAB4xDp8OvAvYHtgV+Crw3HHc84DzgXsAdwLOAG4Gtlrgd/4C8D3g+8BXgH0XsYz+ZWb6/wC8\nbhz3MuCUmcc+BThnPdP6NvC6cXrfBU4Gdp4Zfw5w2ALP3Re4EXgjcDtgn3GZ3Xccvw/wwPH2Xgwr\nuCfPvB43A5mZ3jOB/w88i2HF+zzgu4t8rxwNnAXcfcxyAvChea/9e4BtgQcDNwC/Oo5/w/garBiX\n6TnApfPms9/M8Hqnt46cfwSsBlaN/87eXrWIv4lnA2dsxN/QPsCPgR17/z0v1Z/uAfyZ94L8fDF8\ndmbcb41v+IzDO41/oCvG4S8Ax808/gHAT8eVzNyKaM+Z8f9z3ko0wOXAPuPwl4EjxtsHAheOt+86\nrgi2m3nuYcDnx9ufB35/ZtyBrL8YHgHcflyhHTn+jvfawDJ67szwk2ay3R34EbDTOHwa8NL1TOun\nDGX7K8CODGX6N4t8rfZlWJFvP3PfR4BXLfD444E3j7fnXo+tZsY/E7hgZniH8TG/tIj3yvm0K++7\nj9m2mpnX3WfGfw14+nj728ATZsY9m58vhv1nhtc7vdvgb2Jji+F9wIm3RZbl8uOhpOn73szt64Fr\nanz3j8MwFMScy2ZuX8Kwst115r7LZ27fY3wMAON0LwN2G+/6MHD4ePtw4EPj7T3G6V45Hn5YDbwb\nuMvMdOfnWFBV/VNVXVdVN1bVycCZwEEAST49c1Lx8Jmnzf4el4zzpKquHJ//tCR3ZCiNvxmndcLM\ntF4xPvd6hpXIt6tqDXDc+JzFWl1VN6wrS5JHjYfnvp/kh8BRtK/Fulw1d6Oqrmco650WfvhaewKn\nj6/HKoaiuJGhxOfMvpfWzEz3HrTLc/a1W5+FptdNkh2AQ/Ew0q2yTe8A2uTuOXN7T4atxmsYVubQ\nXk1yBcMhjvnP/+54+zTgTUl2Aw4BHj3efxnDHsOdZ0pq1pXryLEx1p6UraqDFnjM/OlfMTN8MsNh\nttsBZ1XVVeO0/gD4g3nT+ZeNzDbfzkl2GFfiMCznc8fbHwT+AnhiVd2Y5HjgzuO4TX1Vz6XAs6rq\nH+ePSLKh5X8lsDvwrXF4j3njb1XWJMcAr1zHdMKwPbLi1kx/nt8GflBb5pV9m417DFueI5LcP8mO\nwB8Dp82svOef4D0VODjJfuNJ2pcyrPDPAqiqaxhOkp7EcC7j38b7rwI+Bxyf5A7jic9752f//+JU\n4EVJdkuyM8Mx5nVKcsckv5FkuyRbJ/k94HHAZzbwe75gnP4uDCudU2bGfRzYG3gRQ0msz0nAf0ty\nr3GZ/RHwtxt4TvMrAH+c5HZJHsdwov3UcdxODHsUNyZ5JPCMmeddzXAY8Fc2Yl7r8x7guCR7ACS5\nS5Inz8u5kFOBY5LcadwIeMG88VcB955336IvFqh5VwzN/NxhfaUwXsSwHUPBbz2+Rza0MXskG37N\ntQEWw/Rs7NbZ/Md/gOEk9RUMJwaPXuixVXUBcATwDoYV1cHAf6mqm2Ye9iHgAIat31lHjtM/n+FE\n4mnA3cZxfwl8luEk5j8D/3s9+W8H/CnDieerGVZKT6mqi9bznBpzfQ64iOGE+Z/N/F43jPO8F/Cx\n9UyHqjqJYUXyNYZj6dczs8wyXLF1+AJPh2FrezXD8v4AcFRVXTiOez7wJ0l+xHA+5yMz871+zHzm\nePjnkev5XReMP3P7bcAngM+N8zsLeOQCj50//DqGvcSLGZbpaQznXua8AXj1mPPFi5jepvJfGV6P\ndwK/znC46r1zI8fDgo+dGb4HsB8Ww62WdR8J0FKU5AsMVyGd2DtLb0lezXB10JG34Tz2ZVje8w+9\nLGlJngf8blXt1zuL+nCPQVuc8fDSsxkOr2gDktwtyWPGQ4K/CryEDexpactmMWxZlv3uX5LnMJyI\n/VRVndk7zxKxLUOJ/hj4e4b/o3JC10TqykNJkqSGewySpIbFIElqWAySpIbFIElqWAySpIbFIElq\nWAySpIbFIElqWAySpIbFIElqWAySpIbFIElqWAySpIbFIElqWAySpIbFIElqWAySpIbFIElqWAyS\npMY2vQPo1kuyCti5d455CkjvEOswxVxmWpzVVbVL7xDLQaqqdwbdSkmqqib1RzzFTDDNXGZanClm\n2lJ5KEmS1LAYJEkNi0GS1LAYJEkNi0GS1LAYJEkNi0GS1LAYJEkNi0GS1LAYJEkNi0GS1LAYJEkN\ni0GS1LAYJEkNi2GZS3JzkpVJvpHkn5M8egKZvpLkN2eGD03y6c6Zbkly8szw1kmuTvLJzrnmXr9z\nk3wkyfY984yZru2dQbeOxaDrqmrvqnoo8ErgDb0DAc8D3pJk2yQ7AX8GPL9zpuuAvZJsNw4fCFzW\nMc+cudfvQcCNDMuuN7/kZYmzGDT7xSd3BFb1CjKnqr4JfBJ4BfBq4P1V9e9dQw0+DRw83j4c+HDH\nLOvyFeA+vUNo6fOrPbVDkpXADsDdgP0755nzOmAl8FPg4Z2zwLAVfApwbJJPAQ8G/gp4XNdUY7En\n2QZ4EvB/+sbRlsBi0Jqq2htgPL/wAWCvvpGgqtYk+QhwbVXd2DsPQFWdl+SXGfYWPsU0vhN5rthh\n2GP4q55htGWwGLRWVX01ya5Jdq2qa3rnAW4Zf6bkk8AbgccDu/aNAswUu7SpWAxau9Wb5P4M551+\n0C/OZM0tpxOB1VX1zST79gw0msJei7YwFoO2Hw9FzK1gjqwqryr5eQVQVd8F3tE5y6xJvVZJtmY4\nL6QlLK4Dlr4kVVWT2nKcYiaYZq4tKVOShwDvqapN/v9hprictlRerippk0hyFPBB4FW9s+jWcY9h\nCzDFLakpZoJp5jLT4kwx05bKPQZJUsNikCQ1LAZJUsNikCQ1LAZJUsNikCQ1LAZJUsNikCQ1LAZJ\nUsNikCQ1/EiMjpKsAnbunUNahlZX1S69Q0yVxdDRFD/7xUyLY6bFm2KuKWaaEg8lSZIaFoMkqWEx\nSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExLEFJ\nbk6yMsnZ47979M4EkOTa3hnmm1lW5yb5RJIVvTMBJHlqkluS3K93FoAkb0nyopnhzyR578zwm5L8\nj82c6akz7/G59/vNSZ64OXMsRxbD0nRdVe1dVQ8b/720d6DRFL/cY25ZPQhYDbygd6DRYcBXgMN7\nBxmdCTwGIEmAXYEHzox/DHDW5gxUVR+feY/vDbwL+HJVfXZz5liOLIalyS8Y+cX8I7Bb7xBJbg88\nFng20ymGsxiLgaEQzgOuTXLHJNsC9wdW9go37lm9BjiiV4blZJveAfQL2SHJSoaC+E5VPa13oAkL\nQJKtgQOA9/WNA8BTgM9U1UVJrknysKo6u2egqroyyY1Jdudnewe7Af8Z+DFwblXd1CNbkm2ADwJ/\nWFXf7ZFhubEYlqY14661NmyuRHcHzgf+b+c8MOwlvHW8/RHgGUDXYhidxbAn8xjgzQzL7LHAjxgO\nNfXyp8B5VfXRjhmWFYtBW7o1VbV3ku2BzwIvBN7eK0ySnYH9gb2SFLA1w7mZl/XKNGPucNJeDIeS\nLgdewlAMJ/UIlOTxwCHAw3rMf7nyHMPSNNVzDFPMFYCqugE4GnhJkp7v+0OBk6vqXlV176raE7g4\nya93zDTnLOC3gFU1WA3cieFw0mY98QxrS/RE4MiqWrO557+cWQxL0xSv/oHhsM2lSS4b/92slzcu\nYO2yqqpvAOfQ94Tv7wKnz7vvY0zjJPS5wJ0ZTtLP3vfDqlrVIc9RwF2AE+Zdnn1ohyzLSqqmuo7Z\n8iWpqprUVraZFsdMizfFXFPMNCXuMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlh\nMUiSGhaDJKlhMUiSGhaDJKlhMUiSGn66akfjF7VI2vyqqtwwXoDf4NbZ1D76d4ofR2ymxZliJphm\nLjfK1s/GlCQ1LAZJUsNikCQ1LAZJUsNikCQ1LAZJUsNikCQ1LAZJUsNikCQ1LAZJUsNikCQ1LAZJ\nUsNikCQ1LIYlKMmeSc6dd9+xSV7cK9OYYbckH09yQZILkxyfpOsn+Ca5OcnKJGeP/768c55dZrJc\nmeTymeFuyyrJtfOGn5nk7b3yjBl2T/KdJHcah3ceh/fomWs58GO3l64pfmzwx4B3VtVTkwT4S+A4\noOfK+Lqq2rvj/BtVtQp4GECS1wA/qaq39E0FrPv91PU9VlWXJ3kX8OfAUcAbgHdX1aU9cy0H7jFo\nk0iyP3B9VZ0Mw7egAH8IPCvJ9j2jdZz3hkw521S8FXhUkqOBxwBv7pxnWXCPQZvKA4Gvz95RVdcm\nuQS4D3Bel1SwQ5KVDCvhAl5fVad1yjJlO47LCYZltTPwyY55AKiqm8bDf58BnlBVN/fOtBxYDEvT\nQrv4Uzy81HureM2UDiVNWLOckjwT+LWOeWYdBFwBPAg4o3OWZcFDSUvTD4Bd5t23C3BNhyxzzgce\nPntHkhXAPYGLuiTSkpfkocABwKOBFye5a+dIy4LFsARV1XXAFUn2g+FKF+CJwD90zPR5hsM2R4yZ\ntgbeBJxUVTf0ykX/PZalYqrL6V3A0VV1OfC/8BzDZmExLF1HAq9Ocjbw98Brq+rizpkOAZ6e5ALg\nW8D1wKv6RmL7eZerHtc5z1RN7jBkkucCl1TV3OGjE4D7J3lcx1jLQoaLR9RDkqqqSW2pmWlxzLR4\nU8w1xUxT4h6DJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlh\nMUiSGhaDJKnhN7h1lmRyH29rpsUx0+JNMNfq3gGmzI/d3gJM8SOEp5gJppnLTIszxUxbKg8lSZIa\nFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMk\nqWExLHNJbk6yMsl5Sc5O8uIkXT/aOMktSd44M/ySJK/pnOnanvNfSJJXja/dOePr+IgJZLp25vZB\nSb6V5J49M2nj+EU9uq6q9gZIsivwYWAF8NqOmX4K/HaS11fVqo45Zk3ui0uSPBo4CHhoVd2UZBdg\n286xYFxWSQ4A3gr8RlVd1jeSNoZ7DFqrqq4Bfh94YecoNwHvBV7cOcfU3R24pqpuAqiqVVV1VedM\nAEnyOOA9wMFV9e+d82gjWQxqVNXFwFZJ7tIzBvBO4PeS3KFjjqn7HLDHeKjmnUn26R1otB1wOvDU\nqrqwdxhtPItB69L96xOr6ifA+4Gje2eZqqq6DtibYS/vauCUJEf2TQXAjcBZwHN6B9EvxmJQI8m9\ngZuq6ureWYC3Ac8GduwdZKpq8OWqei3w34GndY4EcDPwdOCRSY7pHUYbz2LQ2r2D8fDRCcDb+8UZ\nogBU1WrgVKax5dl9L2q+JPdLcp+Zux4KXNIrz4xU1Q3AwcAzkjyrdyBtHK9K0vZJVjJczXIjcHJV\nHd850+wVQG8GXkD/q4J6z39ddgLenuSODCfsL2I4rNRbwVDsSZ4EfCnJ96vq7zrn0iKlaorvd22M\nJFVVk9qinWImmGYuMy3OFDNtqTyUJElqWAySpIbFIElqWAySpIbFIElqWAySpIbFIElqWAySpIbF\nIElqWAySpIbFIElqWAySpIafrrqFSDK5T0OcYiaYZi4zLcrq3gGWCz9dVY0pfoKlmRZniplgurm0\nMA8lSZIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWEx\nSJIaFoMkqWExaJNJ8ktJPpjkoiT/lOTMJE/pnOmuST6c5MIx098luU/HPGckOXDefUcneWevTDM5\ndkvy8SQXjK/hXyS5Xe9c2vwsBm1KHwe+WFX3qapHAIcBu3fOdDpwRlXdd8x0DHDXjnk+BBw+777D\nxvt7+xjwsaq6H3BfYEfgjX0jqQe/qEeNX/RLVZLsD7y6qvabUKb9gGOr6vETyrQz8K/A7lV1U5I9\ngS9V1S/3yjQ+d3/gNbPLKskdgEvGrGt65FIf7jFoU3kgsLJ3iHn2Ar7eO8SsqloN/D/gSeNdhwGn\n9ku01gOZt6yq6lrgYqDboTf1YTHoNpHkHUm+keRrvbNM0CkMhcD474c7ZtkQt/SXIYtBm8o3gV+b\nG6iqFwIHAHfplmjI9PCO81/IJ4ADkjwM2KGqzu4dCDifecsqyQqG8zH/1iWRurEYtElU1RnAdkmO\nmrn79r3ywNpM2yZ5ztx9SR6U5LEdY1FV1wFfBE5kInsLVfV5YIckRwAk2Rp4E/D2qvpp13Da7CwG\nbUpPBR6f5NtJvgqcBLy8c6ZDgAPHyy/PBY4DruqcCYZCeDATKYbRIcChSS4ArgFurqo3dM6kDrwq\nSY0pXkFipsXZlJmSPJqhtA6pqm9MJZc2D4tBjSn+EZtpcaaYCaabSwvzUJIkqWExSJIaFoMkqWEx\nSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqWExSJIaFoMkqbFN7wCaniST+wAtMy3OFDMBq3sH0Mbx\nQ/QkSQ0PJUmSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaD\nJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlh\nMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiSGhaDJKlhMUiS\nGhaDJKnxH5X1NnZZb1gBAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10a252908>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\npetOpHpPa06nOa3i8s/nlR5lNV/+fKoSe4DqzbjD2102Iu4EDqA6xfIRquL654h4vnaxC4HdqY5i\n6yan7d9G9ebdpUDjJfb3gaup3jj8A/CTtuEjno2IhxtfVG+YPhsRi5bxM19I9QQ0l+pN6q/Vt5lu\n8010dtR3ADA9IuY1r1B1JtSkFtdpeJDqjcsHqPb9oRFxV1r3CeCrkh6nen/l4lrGZ1Lm6WmoZpsB\nftZ26uu+AVwJXJNubwbVm5/ttlOfP4Hq1d48qn16KdV7RQ0nA8emnEd0sL1u+QjV0OaZwI5UQ0vf\na6xMQ3g7AETEFKoDgF+nn/8Mqved7hyEXMOaouWreetlkq6jOrvnnNxZcpN0LNVZN5MH8TZ2ptrf\nzcMkfU3VOfIfjohdc2exoecjf+tbaSjoYKqhEFsGSa+TtH0avnsL8Hk8Tl4sl39/Kv7lWjrr417g\n5xExPXeePrES1RPlE8CvqP6G46ysiSwbD/uYmRXIR/5mZgVy+ZuZFcjlb2ZWIJe/mVmBXP5mZgVy\n+ZuZFcjlb2ZWIJe/mVmBXP5mZgVy+ZuZFcjlb2ZWIJe/mVmBXP5mZgVy+ZuZFcjlb2ZWIJe/mVmB\nXP5mZgVy+ZuZFcjlb2ZWIJe/mVmBXP5mZgVy+ZuZFcjlb2ZWIJe/mVmBXP5mZgVy+ZuZFcjlb2ZW\nIJe/mVmBRuYO0C8kLQLG5M7RgQCUO0QHnLO7+iFnP2QEWBwRY3OHGGyKiNwZ+oKkiIief+A6Z3c5\nZ/f0Q0bon5zLy8M+ZmYFcvmbmRXI5W9mViCXv5lZgVz+ZmYFcvmbmRXI5W9mViCXv5lZgVz+ZmYF\ncvmbmRXI5W9mViCXv5lZgVz+ZmYFcvmbmRXI5W9mViCXv5lZgVz+ZmYFcvkPEUlfl/SZ2vwvJX2v\nNn+qpM/mSfdSkl4r6QJJcyXdJGm6pPfnztVM0guSTqnNf17Sl3NmaiZpqaSZkuZImiXpCEk99ylR\ntZyz0vdxuTO1I+nJ3BmGA5f/0JkObA+QfvnXBDatrd8emJEhVytXAL+OiA0i4u3AfsB6mTO18hzw\nL5J6+fNWn4qICRGxGTAR2BM4LnOmVho5t0rf780daAD+7NkucPkPnRmk8qcq/TnAk5JeLWklYGNg\nZq5wDZJ2A56LiO83lkXEgog4M2Osdp4HvgcckTtIJyJiIfAx4FO5s7TQc69GbHC5/IdIRDwILJG0\n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\nDNV9Pjm9UimCy78FSR8F5kfEtLToLGBjSe/MGGsgF1GNT/dk+QOrAeelUz1vBjYBvpI3UksBEBH3\nR8S3c4cZQOQOMMwcCqwFnFU71XOmpH1zBxtMql6NDy+SIiJ6/ujIObvLOburH3L2Q0bozZw+8jcz\nK5DL38ysQC5/M7MCufzNzArk8jczK5DL38ysQC5/M7MCufzNzArk8jczK5DL38ysQC5/M7MCufzN\nzArk8jczK5DL38ysQMP2XzrnzmBmVrM4IsbmDlE3LMvfzMwG5mEfM7MCufzNzArk8jczK5DL38ys\nQC5/M7MCufzNzArk8jczK5DL38ysQC5/M7MCufzNzArk8jczK5DL38ysQC5/M7MCufzNzArk8jcz\nK5DL38ysQC5/M7MCufzNzArk8jczK5DL38ysQC5/M7MCufzNzArk8jczK5DL38ysQC5/M7MCufzN\nzArk8jczK5DL38ysQC5/M7MCufzNzArk8jczK5DL38ysQC5/M7MCufzNzArk8jczK5DL38ysQC5/\nM7MCufzNzArk8jczK5DL38ysQC5/M7MCufzNzArk8jczK5DL38ysQC5/M7MCufzNzArk8jczK9D/\nA0XbOuVPdisIAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10c24f2b0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\nSY3/D8A5ahkgRRcLAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10a1ad400>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def report(keyboards=keyboards, swaps=3000, 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(improved(kbd, swaps=swaps, scorer=scorer), \n",
    "                 ('improved ' if swaps else '') + name)\n",
    "        \n",
    "report()"
   ]
  },
  {
   "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.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "So the more compact keyboards with a smaller diameter (`4-by-7` and `5-by-6`) seem to perform slightly better than `qwerty`. \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Question 5: How often are two words confused because of 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": 30,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def neighboring_keys(kbd, radius=1.5):\n",
    "    \"Build a dict of {Letter:NeighboringLetters}, e.g. {'Q':'AQW', ...}.\"\n",
    "    return {A: cat(sorted(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": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'A': 'AQSWZ',\n",
       " 'B': 'BGHJNV',\n",
       " 'C': 'CDFGVX',\n",
       " 'D': 'CDEFRSXZ',\n",
       " 'E': 'DERSW',\n",
       " 'F': 'CDFGRTVX',\n",
       " 'G': 'BCFGHTVY',\n",
       " 'H': 'BGHJNUVY',\n",
       " 'I': 'IJKOU',\n",
       " 'J': 'BHIJKMNU',\n",
       " 'K': 'IJKLMNO',\n",
       " 'L': 'KLMOP',\n",
       " 'M': 'JKLMN',\n",
       " 'N': 'BHJKMN',\n",
       " 'O': 'IKLOP',\n",
       " 'P': 'LOP',\n",
       " 'Q': 'AQW',\n",
       " 'R': 'DEFRT',\n",
       " 'S': 'ADESWXZ',\n",
       " 'T': 'FGRTY',\n",
       " 'U': 'HIJUY',\n",
       " 'V': 'BCFGHV',\n",
       " 'W': 'AEQSW',\n",
       " 'X': 'CDFSXZ',\n",
       " 'Y': 'GHTUY',\n",
       " 'Z': 'ADSXZ'}"
      ]
     },
     "execution_count": 31,
     "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": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['BGHJNUVY', 'DERSW', 'KLMOP', 'KLMOP', 'IKLOP']"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "columns = [qwerty_neighbors[L] for L in 'HELLO']\n",
    "columns"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These are 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 \"BDKKI\" and \"YWPPP\", except those are not words.\n",
    "\n",
    "It turns out that there is a library function, `itertools.product`, that will take 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": 33,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "paths = {cat(picks) for picks in itertools.product(*columns)}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "How many paths are there?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5000"
      ]
     },
     "execution_count": 34,
     "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": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['NWPOO', 'HEOOP', 'JRMLI', 'HWPKL', 'VEMKO', 'UWOPI', 'VDOMK', 'HELLI']"
      ]
     },
     "execution_count": 35,
     "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": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'BROMO', 'BROOK', 'HELLO', 'JELLO'}"
      ]
     },
     "execution_count": 36,
     "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 package this as the function  `confusions`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def confusions(word, neighbors=neighboring_keys(qwerty)):\n",
    "    \"All valid words whose paths could be confused with the path for the given word.\"\n",
    "    columns = [neighbors[L] for L in word]\n",
    "    return {cat(picks) for picks in itertools.product(*columns)} & WORDS"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'BROMO', 'BROOK', 'HELLO', 'JELLO'}"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "confusions('HELLO')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'ALTOS', 'EIDOS', 'SIDLE', 'SKEPS', 'WIELD', 'WORKS', 'WORLD', 'WORMS'}"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "confusions('WORLD')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "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": 40,
     "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`.\n",
    "\n",
    "My first concern is that `WORDS` has 178,691 words, including many unusual words, so there might be too many confusions. I will read in a smaller word set, with only common words:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "! [ -e words.js ] || curl -O https://xkcd.com/simplewriter/words.js\n",
    "    \n",
    "COMMON = set(re.findall('[A-Z]+', open('words.js').read().upper()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3614, 'SPECIALS', 'UNDERSTANDINGS')"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(COMMON), max(COMMON, key=path_length), max(COMMON, key=len)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "More Efficient `confusions`\n",
    "---\n",
    "\n",
    "Another issue is that `confusions` is really slow:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 3.14 s, sys: 306 ms, total: 3.44 s\n",
      "Wall time: 3.45 s\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'SOMETHING'}"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time confusions('SOMETHING')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It took (on my computer) 3 seconds to compute this.  Why so long? Let's count:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[7, 5, 5, 5, 5, 8, 5, 6, 8]"
      ]
     },
     "execution_count": 44,
     "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. `'PALEOMAGNETISMS'` would have about 10,000 times more paths. Looking at every path is slow, but for most paths, we're wasting our time.  For example, one choice for the first two neighboring letters of 'SO' 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 some word.*  I'll make a set of all the prefixes of the `COMMON` words:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": true
   },
   "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)}\n",
    "\n",
    "PREFIXES = prefixes(COMMON)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'T', 'TH', 'THE', 'THES', 'THESE', 'THEY', 'THO', 'THOS', 'THOSE'}"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prefixes(['THESE', 'THEY', 'THOSE'])"
   ]
  },
  {
   "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, yield it as a result.\n",
    "\n",
    "A word is `confusable` if it has a confusion word (other than itself)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def confusions(word, neighbors=neighboring_keys(qwerty)):\n",
    "    \"All valid words whose paths could be confused with the path for this 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 COMMON:\n",
    "            yield path\n",
    "\n",
    "def confusable(word, neighbors=neighboring_keys(qwerty)) -> bool: \n",
    "    \"Is this word confusable with another, given this keyboard's neighboring keys?\"\n",
    "    return any(c != word for c in confusions(word, neighbors))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's check the speed (remember the old version took over 3 seconds):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 94 µs, sys: 0 ns, total: 94 µs\n",
      "Wall time: 95.8 µs\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'SOMETHING'}"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time set(confusions('SOMETHING'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We went from about 3 seconds to about 100 microseconds: that's 30,000 times faster!  We can look at some more confusions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'BELIEVED': 'BELIEVES BELIEVER BELIEVED',\n",
       " 'BENDS': 'BENDS',\n",
       " 'BITEY': 'HURRY BITEY',\n",
       " 'CEILINGS': 'CEILINGS',\n",
       " 'COATING': 'COATING',\n",
       " 'COLORERS': 'COLORERS',\n",
       " 'COUPLES': 'COUPLES',\n",
       " 'DUSTING': 'SHARING DUSTING',\n",
       " 'DYING': 'EYING DYING',\n",
       " 'ENJOYS': 'ENJOYS',\n",
       " 'FREED': 'TREES TREED FREES FREER FREED FEWER FEEDS DRESS',\n",
       " 'GET': 'YET TRY HEY HER GET CRY',\n",
       " 'GUESSES': 'GUESSES GUESSED',\n",
       " 'HOSPITAL': 'HOSPITAL',\n",
       " 'IMAGING': 'OKAYING IMAGING',\n",
       " 'LETTER': 'LETTER',\n",
       " 'LUCKY': 'LUCKY',\n",
       " 'MAY': 'MAY LAY',\n",
       " 'ONCE': 'ONCE',\n",
       " 'PALE': 'PALE',\n",
       " 'PARTIES': 'PARTIES PARTIED',\n",
       " 'PLAYER': 'PLAYER PLAYED PLATED OKAYED',\n",
       " 'REPEAT': 'REPEAT',\n",
       " 'RUSHES': 'RUSHES RUSHED FISHES FISHED',\n",
       " 'SAMES': 'WAKES WAKED SANDS SAMES ASKED',\n",
       " 'SHARP': 'SHARP',\n",
       " 'SINGE': 'WINGS SONGS SINGS SINGE SINCE AUNTS',\n",
       " 'SPEND': 'WORKS SPEND DOWNS',\n",
       " 'TAUGHT': 'TAUGHT',\n",
       " 'WALLED': 'WALLED WALKER WALKED'}"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "{w: ' '.join(confusions(w))\n",
    " for w in random.sample(COMMON, 30)}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visualizing Paths on a Keyboard\n",
    "----\n",
    "\n",
    "It would be nice to see potentially confusable word paths on a keyboard.\n",
    "I'll add functionality to `show_kbd` to call the new function `plot_paths` if any `words` are passed to `show_kbd`:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def show_kbd(kbd, name='keyboard',  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",
    "    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",
    "    plt.axis('equal'); plt.axis('off')\n",
    "    plt.title(title(kbd, name));\n",
    "    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 on three similar paths:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n1bj7RX1QDFcBT7v7bDP7DbAAOFTF0HsqBgn05oUS77AfAf7b3X9X4KyjgXuAo9z9/tSy4pI4B361\nE+y6Arg8vqkRmAY3zXM/I9+soCgW37sHbzQb+wG7AHOBt4BFtYt99ZsTU1mv8L6twD+AIcDWwFR3\nf6YAOQ3AtkR/9mpP4DMUrhgagIOJDmmeATwKzAQuUDH0XkV/L4CUhO8AC3IphTwcR3SYYg8g62LI\nKHqH9Pgas1W7wa7Jm6qBraMdXf4x9313IfAV4Cs2YUQ9+zSPYAFRMSwAPgysLKtLI6sLG9x9HwAz\nOxC4Adi9EEHu/lb87n0msLEQGYmsBWY2HjgNuJvoXJfkoay/F0CKm5kdAZwIZHWyNM+svYEjgQOB\nL8UniFO1ApZPJDpe1aERWBmVUboa29cxEXglCqYFGBVfX2DxSeetzGyrAsZcBXwKGFrAjA5/AC4D\nbu6DrJKnYpBeiz8Z9Etgurtv6IPInwIz3X0Z8AM2H+1JzQK46BZYtAm4js3nGJ6AG8zsvamG1W+c\nzl3VLewIzCEap8yubqF+4/RUczZ7+520mU0hev2/Uagcd19LdGL/7AJkBFlE2+El7v7PAmZtMVQM\nko8ZwGjgGjN72syeib+fknZQ/ImnfyfOK1wDTDGzQ9PMaXBfMgeOaoE7LoTVI+CtG6G1FT5P9DHI\n1Hhr81xeap7KsiGrWAksqF7GS81Tsz3x3AuDO54nonfW070wJxmT87ycaBxUqJOZDuDur7n7jwuU\nscXRyWcJlOovFymrOHJKOauYaMQgIiIBFYOIiARUDCIiElAxiIhIQMUgIiIBFYOIiARUDCIiElAx\niIhIQL/gJgEz0wYhW5K17j6yvxdioFExiIhIQIeSREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQ\nEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagY\nREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIq\nBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGA\nikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQk\noGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhER\nCagYRERKp2HJAAAApUlEQVQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagY\nREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIq\nBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGAikFERAIqBhERCagYREQkoGIQEZGA\nikFERAL/D9dUkOSX0vFjAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10c1cdac8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_kbd(qwerty, words=['VALUE', 'VALOR', 'CAKE'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " OK, we're on the right track, but I see three problems, which I think I can solve: \n",
    "\n",
    "- The letters are obscured by the circles.  Solution: offset paths away from the center.\n",
    "- When the paths are the same, they overwrite each other.  Solution: offset each path towards a different corner.\n",
    "- There is no indication what direction the path is going in. Solution: put a diamond on the start position.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def plot_paths(kbd, K, words):\n",
    "    \"Plot paths for each word, each with a different offset (and color).\"\n",
    "    Q = K / 5  # Q originally meant a quarter of a key width; but 1/5 looks nicer.\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')\n",
    "        plt.plot(Xs[:1], Ys[:1], 'kd')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nATWc5UXqYBs1AAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10a1a2da0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_kbd(qwerty, words=['VALUE', 'VALOR', 'CAKE'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That looks busier, but it is  easier to follow the paths. Another example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nfkKV+hjcfRrQ08xOyk0zszFmtl8VYq0C/kl4Hau9U2v8JW1mowjf//xRpJnFSZrKbgJOan32ymMR\nXr/J7v5CFWNtNJQYpBKnAIOBK1LDVZ82s89nHcjMTgZeS3baAFcAo8xs/6xjJY4EDjGzV8zseeCH\nwMIqxbqe0DZe7cTQK/c+JbGO9+qc+iC9zJ8CW+RNyzyWu7/p7r+uUoyNjk6JIZGuejoCxeoccbpy\nrM5EFYOIiESUGEREJKLEICIiESUGERGJKDGIiEhEiUFERCJKDCIiElFiEBGRiA5wk4iZ6QMhG5Nl\n7j6w7dk2LkoMIiISUVOSiIhElBhERCSixCAiIhElBhERiSgxiIhIRIlBREQiSgwiIhJRYhARkYgS\ng4iIRJQYREQkosQgIiIRJQYREYkoMYiISESJQUREIkoMIiISUWIQEZGIEoOIiESUGEREJKLEICIi\nESUGERGJKDGIiEhEiUFERCJKDCIiElFiEBGRiBKDiIhElBhERCSixCAiIhElBhERiSgxiIhIRIlB\nREQiSgwiIhJRYhARkYgSg4iIRJQYREQkosQgIiIRJQYREYkoMYiISESJQUREIkoMIiISUWIQEZGI\nEoOIiESUGEREJKLEICIiESUGERGJKDGIiEhEiUFERCJKDCIiElFiEBGRiBKDiIhElBhERCSixCAi\nIhElBhERiSgxiIhIRIlBREQiSgwiIhJRYhARkYgSg4iIRJQYREQkosQgIiIRJQYREYkoMYiISESJ\nQUREIkoMIiISUWIQEZGIEoOIiESUGEREJKLEICIiESUGERGJKDGIiEhEiUFERCJKDCIiElFiEBGR\niBKDiIhElBhERCSixCAiIhElBhERiSgxiIhIRIlBREQiSgwiIhJRYhARkYgSg4iIRJQYREQkosQg\nIiIRJQYREYkoMYiISESJQUREIkoMIiISUWIQEZGIEoOIiESUGEREJKLEICIikf8P3Lvmwv17SsgA\nAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10c228ac8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_kbd(qwerty, words={'HOUSED', 'HOUSES', 'NOISES'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Average Confusions of a Keyboard\n",
    "===\n",
    "\n",
    "The question is: how confusing is a keyboard? One metric for confusingness is the percentage of words that are confused with other words:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def confusingness(kbd):\n",
    "    \"The proportion of words in COMMON that are confused with other words on this keyboard.\"\n",
    "    neighbors = neighboring_keys(kbd)\n",
    "    return mean([confusable(w, neighbors) for w in COMMON])\n",
    "\n",
    "def title(kbd, name): \n",
    "    return ('{}: path length = {:.1f}, confuse = {:.0%}'\n",
    "            .format(name, workload_average(kbd), confusingness(kbd)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'4-by-7': 0.4571112340896514,\n",
       " '5-by-6': 0.5282235749861649,\n",
       " 'qwerty': 0.5398450470392916}"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "{name: confusingness(kbd) for (name, kbd) in keyboards.items()}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "About half the common words are confusable, with a little bit of variation between keyboards."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Question 6: Is there a Keyboard that Minimizes Confusion?\n",
    "===\n",
    "\n",
    "Consider this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 112 ms, sys: 990 µs, total: 113 ms\n",
      "Wall time: 112 ms\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.5398450470392916"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time confusingness(qwerty)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 168 µs, sys: 0 ns, total: 168 µs\n",
      "Wall time: 171 µs\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "3.2333097802127653"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%time workload_average(qwerty)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Computing `confusingness` takes about 500 times longer than computing `workload_average`, so if we want to use `confusingness` as a scoring function, we will have to settle for fewer swaps:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYYAAAEKCAYAAAAW8vJGAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAF/NJREFUeJzt3Xm4JGWZpvH7oUoBNwQXtNncGwUUSrQVl0YcRkRtRQVB\nbZh2aR1HRdHpjVFEW23brbtdcGcE2VxQbHTUUVQUxFYLbBYdRFCWQgWrkJJNqHrnj4iD+R3qVJ2i\nMvJUnbp/15XXyYjIiDe2zCfii8g8qSokSZqyyVzPgCRp/WIwSJIaBoMkqWEwSJIaBoMkqWEwSJIa\nBsMaJLkkyV5jnuYhSb4zzmmuZf1vJnnRHNU+OsmbZ/naHZKsTDLx/XSut9GGIskeSS5Mcm2Sv5jr\n+dF4GAxzZ95/gWRMH66Dr6fVBNAkap+W5DdJrkly9uo+XJO8JsnPk/wuyeVJ3j0XoTnNm4F/q6q7\nVdUX53heVivJnv36vibJxasY/ogkp/fDL03yv0aGPTzJef22eu1I/4VJzkqyzaSWYxLmeqfSgJIs\nmOtZYMMIwKn5zBzUPhTYpqruDrwM+FSSrWd47SnA7lW1BbAzsCvw6snM5ox2AC6Y43mYreuAjwOv\nn2H48cC3+m2xJ/CKJE/vh70dOAx4BHB4knv3/Q8DPltVVww213PAYFgLSR6a5OIkz+u775vks/1R\nxM+TvKrvv3WS65JsOTLuov51Ux/WmyR5X390csFoc1U/3VOS/LY/TX/JyLBHJTkzybIkV/TTWDgy\nfGWSVyS5ELiw77d3kp/047yPtfgATHJEks8kObFvLvhhkoePDP/bJBf1w85L8qy+/47AUcBjkyxP\nsnRkslslObUf53tJ7j/Leblbko8lWZLksiRvSZJ+2CFJvpPknUmW9ttjn5Fx75fk2/3R9teSvD/J\nMf3gb/d/r+nn6c/+ONqqpzcuVXVuVd080mshsN0Mr72kqpb1nQuAlcCDZlsryUv7fW1qW+3a998x\nXfPisiTnJnnGyDhH9+vqNtsryUXA/YGpYXfMtKbXfv85tn++aZJjk1zd1/p+knv1w2bctuNSVT+o\nquOAS2Z4yQ504UBVXQx8F9ipH3Z/4JtVdSXwM2D7JDsAzwbeO875XC9UlY/VPOh2or2ARcAvgaf2\n/QP8EDic7k16P+AiYO9++KnAy0am8x7gX/vnhwA30x3tLQAOAK4B7t4PPx14H3AHuiOU3wB79sMW\n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\nAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10a58b550>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 10.4 s, sys: 19.7 ms, total: 10.4 s\n",
      "Wall time: 10.4 s\n"
     ]
    }
   ],
   "source": [
    "%time show_kbd(improved(qwerty, swaps=100, scorer=confusingness))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This did indeed reduce confusingness (which was 54%); not bad for only 100 swaps."
   ]
  },
  {
   "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 product of workload average and confusingness. Then I (rather arbitrarily) scale the result by 5, just because I think a number like \"2.1\" looks better than \"0.42\".\n",
    "\n",
    "First we'll define the combined scorer function and incorporate it into `title`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def dissatisfaction(kbd):\n",
    "    \"The product of workload average and confusingness, scaled by 5.\"\n",
    "    return 5 * workload_average(kbd) * confusingness(kbd)\n",
    "\n",
    "def title(kbd, name): \n",
    "    return ('{}: path length = {:.1f}, confuse = {:.0%}, overall = {:.1f}'\n",
    "            .format(name, workload_average(kbd), confusingness(kbd), \n",
    "                    dissatisfaction(kbd)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYz\nMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPB\nzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBg\nMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4\nGMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwK\nDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOz\ngoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzM\nrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYzMys4GMzMrOBgMDOzgoPBzMwKDgYz\nMys4GMzMrOBgMDOzwv8HncS9VgcZunsAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10c066278>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_kbd(qwerty)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let'stry to minimize confusion. This should take around 2 minutes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10a2fb198>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10c164e80>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\nyVbnVgAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10a2cde10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 2min 30s, sys: 482 ms, total: 2min 31s\n",
      "Wall time: 2min 31s\n"
     ]
    }
   ],
   "source": [
    "%time report(swaps=500, scorer=dissatisfaction)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Success!  We were in fact able to make progress on the combined metric.  If we could run 50,000 swaps instead of just 500, 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 11% confusingness) here, just in case another run is not as good:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYAAAAEKCAYAAAAb7IIBAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAGuFJREFUeJzt3Xm8HGWd7/HPNwn7mrBEhSTKdlEWISwii4NwucPiAqMg\nm2EGdeAyKKIO48gIiuOKgg4IoygIEQiyCAzwUu8IAoK4ENAAegFBCARkSZAYFiH85o/n6Vj06T7n\nJJycpzvP9/16nRenq3r5dlV1faueanIUEZiZWX3GlA5gZmZluADMzCrlAjAzq5QLwMysUi4AM7NK\nuQDMzCq1VAtA0v2Sdhvh5zxM0o0j+ZyL+frXSTq80GufI+mkYd53iqSXJI16yZdeR/1C0o6S7pb0\ntKR3lM5jw9f++Sq5X3gl+vUMYJn/nxdGaCe61JfTIEWzVF9b0jqSLpD0sKR5km6UtP0g979G0vy8\ns31a0vOSfr00Mw7DScB/RMTqEXFl4SyDkrSrpGslPSXpvg7zT5L0G0kvSDqhbd6Wku6Q9JikYxvT\nx0m6RdJ6o/EeloKlvY0Puszb7tv6HD7d2M6PH+o1+rUAljpJY0tHoD+KrpVTo/y6qwK/ALYGJgDn\nAVdLWrnTnSNi74hYLe9sVwduBr43amk7mwLcVTjDcC0Avg18rMv8e4B/Bq7qMO/zwEeANwLHS1o3\nT/8IcElEPDzCWV+xEmfOHQy1zNsFsEZjO//sUA8YtTcp6fWS7pP0nnz71ZIuyUcFv5f0wTx9oqQF\nksY3Hjs136+1Ux4j6bTcjHc1h5ny814h6cl8ev3+xrztJN2cjxgfzs8xrjH/JUlHSbobuDtP20PS\nb/NjTmMxdnSSTpR0saQZuZF/JWnLxvx/kXRvnneHpH3z9E2BM4E35zaf23jaCZKuyo/5maTXDTPL\n6pK+JWmOpNmSPiNJed5h+Qj6ZElz8/rYs/HY10q6XtKfJP1I0umSzsuzr8//fSpnetNfH9b5+UZC\nRNwfEV+NiMciOQtYHvhfQz1W0muBXYDpw309SR/I21prXW2Vp2+qdPo/T9IsSW9vPOacvKwGrC9J\n9wKvA1rzllfbkGnefqbn31eQNF3SE/m1fi5pnTyv67odKRHxy4g4H7i/y/zpEfFD4M8dZr8OuC4i\nHiEVxWRJU4C/A05dnBxK/k3SHyQ9Kuk7klbL866RdFTb/W9vfq7y9vtk/kzv37jfOZLOkHS1pPnA\nrpL2ljQzb/cPSDpxcbK+UkMt8w7E4u7TI2Kp/eTguwFTgQeAvfJ0Ab8CjgfGAq8F7gX2yPOvAo5o\nPM8pwNfy74cBLwAfyo89AHgKWDPPvwE4DViOdMTxGLBrnjcV2D6//mTgTuBDjdd5CfghsAawArAW\n8DSwX36tD+fXPjzffxIwF1i/y/s/EXi+8fiPAvcBY/P8dwET8+/7kz48Exvv84a25zsHeBzYJq/o\n7wIXdHntKcBCYEy+/X3gDGBFYG3gFuADjdd6Hjg8L5sjgYcbz3Uz8EVgHLAT8CfgvLbXUeP+hwF/\n6fZ8HbL+FzAvL8v2/145zG1tK+AZYLVh3PcE4NrF2I73B2YDU/PtDfK6H0faof1L/v2teXvZeDjr\ni/T5eGv756Vt+2kt538ErsjbpUhnPqsOtW47vJeD2pZx+/LuuC03Hr87cN8g86cDJ7RNuwjYB1gf\nmAOMz5l3XoJ9yuGkg7MpwMrApcC5ed57gZ827vuG/J7G5fs+CEzLy++Ned1s2lhX84Ad8u3lgbcA\nm+XbmwOPAO/o8vm6jrxfGO1l3pZndn6fZwNrDbk8F3cFLObKuh/4VA61S2P69sAf2u77ceDb+fcD\nWisyf3AeAbbJtw8DHmp77M+BQ/IG9gKwcmPe54Czu+Q7Bri0cfsl4G8at98L3Nz2mNndVnSH5z+x\n+fi84c0Bdupy/9uAtzfeZ6cC+Gbj9l7AXUNsEGOAicBzwAqN+QeSd4L5te5uzFspL4t1STu6vwAr\nNuZPZ2ABjGnM7/R8C4F1l9J2tjrwG+C4Yd7/HuC9i/H8PwA+2GH6zsCctmkXkHeAQ60vBu7wByuA\nfwB+CmzR9nrrDrZul8KyXpICmAxcTTroOwB4O3Bu3rYuJ+083z3M1/9v4MjG7U3y9jmGNCw4H5iU\n5/078K38+wHA9W3P9Z/AJxvr6jtDvPapwFc6bfcMUgBLe5nn+6xCOsAdA6wDXAz8YKjnXjT8sRQd\nkRd884LmFGC9xtBG69Tlhnz7CuDMfJr4euCpiLi18fj2McMHgNfkn7kR8UzbvG0AJG1MOpvYlrRT\nGgfc+vKn4qHG768h7fCb2m8PZdH9IyIkPZSfF0nTgGNJZ0CQVuLaQzzfo43fnyFt9EOZTDojeqQ1\n6pN/Huz0vBHxbL7fqqSNaW5EPNf2ntYfbs78fMrP99gw8g6bpBWBK0lF+6Vh3H9nUiFeuhgvMwn4\nfYfpnbaPB4DmRc0lWV+dTCct8xmS1iCdTRxP+iwNtW6LiogHSWcASFqJdEb5f4DTgQuBa4A7Jf13\nRDw1xNO9hrSMWx4gfY4nRsQjkq4hFeDJpCPv9+X7TQF2aNvnjCVdO2p52bpU+lLBF0hH/8vnn4uH\n+bZHVUQsAGbmm49LOpq0TayS53U0GtcAjiSN+Z3SmDab1GgT8s/4iFgjIt4OEBHPky7QvRc4lIFj\nte3fGphMOrKeQxojX6VtXqswzgR+C2wYEWuSPkDtY6XR+P2R/PimSYO+24EW3T/vBNcH5kiaDHwT\nOCq///GkIalWnhjwTEtuNukoca3G8l4zIrYc6oGkZTAh72hbmsvgFefUwG/oNH+uHuRxy5OOIB+M\niCOH+XLTgMvaDhKGMhvYsMP0OQzcHprb2+JaQBqqaHlV65eIeDEiPhMRmwE7ko6ip7GY61bSwV2W\ndWvaUMX+Sp1AOit6HNgCuDUi5pMOvDYaxuPnkHbmLVNIZ/1/zLcvBA6WtAPprOgnefps4Cdt+5zV\nI+LoxnO1b8sXkLav9fL+4hsswZcdCi7zYIh9/GgUwHxgT+Atkj6fp/0CmC/pOEkrShoraTNJ2zYe\nNx34e9KG3l4AEyV9UOlrZPsDmwJXR8RDpKOLz+eLZluSjgBaj18NeDoinlG60Pp/h8h+NfAGSfvm\njMeQjh4Xxzatx5OO9p8jjdGuQhpmeULSGEn/QDrSaPkjsL6k5Rbz9ZoEEBGPAj8CTpW0Wr6QtoGk\ntwz1BPno7VfApyQtJ+nNpHXS8nh+H512kMMSbd/QafvZp+MbSxfvLyUdVf/9cF4nl9gBpNP99nnX\nqe3riw3fAj4maWq+74aSJpGGHp/J2/E4SbsCbyPthJbE7cCB+bm2Bd7dyLerpM2Vvp3yZ9JOb+Hi\nrtuIuKDLsm5Ne6jT4/LzrkA6Ch6TP1/LNeaPy8t3DLBcnj+m7TneAPwNaegF0vWw3SRNJO38H8z3\nG2xdXAgcq/TFhFWBzwIzIuKlPP8aUimcRLr20HIVsImkQ3PW5SRtK2mwLw2sCsyLiBfy2cDB7Ytl\nkMcusrSWedt9t5e0SX7MWsDXSBfe5w+WbWkXQABExNPAHsCekj6dV9bbSBfu7icNC5xFGsslP+Zm\n0o5lZkS0n2bfAmwMPAF8BnhX49TxINK3DuaQdhCfjIjr8ryPAYdIeprU5jM65W1keJJ0AfCL+bU2\nBG5qzZc0aRgNfgXwHtIFn0OA/SJiYUT8FvhKfi+PApuRxnhbriWdETwqaUmHTZrvZxppQ7qLdOHp\nYhpHmEM89hDSUecTpA/WDNJFYyLiWdKH8Calb/x0+y7+SJ7RkPPsTRpK+FPjaGonSEM9eT037Uv6\nQF/PQJN4+fJfJCIuIb3HC/Jzfh+YEBEvkMpwb9KyOZ10beGe1kOHeA/t8z9J2hHOJY3/n9+Y9yrg\nEtIF+DtJY87fzfMWd90uibcAz5J2pJNIxfvDxvyz8rQDgU/k3w9te47TSV+6aL3vT5Cuw80CPhsR\nre2867ogXdycThou/n1+nQ+1ZkbEX4DLSOPmFzSm/5m0rRzIX0cLvkC6qN7NUcBnJP0J+DdeXijw\n8vU30ts3DLHMlb6NdlC+uQHpWtXTpOthzzGwsAbQX9dF75H0Y+D8iDi7dJYlofS1sQ0jYlrpLCNJ\n0gzgtxHx6dJZRoLS/4h0UUTsXDpL7bwuRlcv/M8OHUnajvRVt/bWtVGWT5U3yKeXewLvII2NLhMi\n4mHvcHqD18XoGo1vAS02Sd8B3kk6Xex6BdtGzatIp9UTSBfrjoyI0v+Mgpm9Qj09BGRmZktPzw4B\nmZnZ0uUCMDOrlAvAzKxSLgAzs0q5AMzMKuUCMDOrlAvAzKxSLgAzs0q5AMzMKuUCMDOrlAvAzKxS\nLgAzs0q5AMzMKuUCMDOrlAvAzKxSLgAzs0q5AMzMKuUCMDOrlAvAzKxSLgAzs0q5AMzMKuUCMDOr\nlAvAzKxSLgAzs0q5AMzMKuUCMDOrlAvAzKxSLgAzs0qNKx2gX0iaC4wvnWMYAlDpEMPgnCOrH3L2\nQ0aAeRExoXSI0aCIKJ2hL0iKiOj5jdc5R5Zzjpx+yAj9k3MkeAjIzKxSLgAzs0q5AMzMKuUCMDOr\nlAvAzKxSLgAzs0q5AMzMKuUCMDOrlAvAzKxSLgAzs0q5AMzMKuUCMDOrlAvAzKxSLgAzs0q5AMzM\nKuUCMDOrlAvAzKxSLoBRJGmKpFlt006U9JFSmTqRtFDSTEl3SLpN0kck9dxfSJI0v3SGoTQzStpb\n0u8kTSqZqRNJL0k6uXH7o5JOKJmpnaRTJH2ocfsHkr7ZuP1lSR8uk64/uQBGXz/8Dc4FETE1IjYH\n9gD2Ak4snKmTfliWASBpd+CrwJ4RMbtspI6eB/5OUi//LdybgB0B8gHJ2sBmjfk7AjcXyNW3XAA2\nqIh4AvhH4OjSWfqUJO0CfAPYJyL+UDhPNy8C3wR66my0zc3kAiDt+O8A5ktaQ9LywKbAzFLh+pEL\nwIYUEfcDYyStUzpLH1oB+D6wb0TcUzrMIAL4OnCIpNVKh+kkIh4BXpC0Pn892v858GZgW2BWRLxY\nMGLfcQGMrm5DFv0wlNFz1wD6xAukHdX7SwcZSkT8GTgXOKZ0lkHcDOxEKoCfAbc0bt9UMFdfcgGM\nrieB9jHWCcATBbIMm6QNgBcj4vHSWfrQQuAAYHtJ/1o6zDB8DXgfsHLpIF20hoE2Jw0B3UI6A3gz\nHv9fbC6AURQRC4A5kt4KkC+4/S3w06LBBlp0tJ+Hfc4ETisXp68pIp4D9gEOlnR46UBdCCAi5gHf\no3fPWG4G3gbMjWQesCYugCUyrnSACk0DzpB0Cmno51N5jL2XrChpJrA8aQjjvIg4tXCml5E0lvTN\nlV4XkHaskvYCrpf0WERcVThXu+Yw5FeAf6I3hyZnAWsB322btnJEzC0TqX8pohfXce+RFBHR8+Pg\nteSU9EbgGxGxwwjG6vQ6VSzP0dAPGaF/co4EDwFZ35F0BHA+cHzpLGb9zGcAw9QvRwXOObKcc+T0\nQ0bon5wjwWcAZmaVcgGYmVXKBWBmVikXgJlZpVwAZmaVcgGYmVXKBWBmVikXgJlZpVwAZmaVcgGY\nmVXKBWBmVikXgJlZpZbJfwxO0lxgfOkcZmbZvIho/2uAxS2rBdAX/5qfc44s5xxZ/ZCzHzJC7+b0\nEJCZWaVcAGZmlXIBmJlVygVgZlYpF4CZWaVcAGZmlXIBmJlVygVgZlYpF4CZWaVcAGZmlXIBmJlV\nygVgZlYpF4CZWaVcAGZmlXIBmJlVygVgZlYpF4CZWaVcAB1ImiDpNkkzJT0i6aHG7XGl87VIulbS\nHm3TjpH09VKZupE0v+32YZJOK5WnE0kL8zpurevjSmfqpJHzdkm/krRD6UzdSFpP0uWS7pZ0r6T/\nkLRc6VxNjeU5S9IVklYvnWm0uAA6iIi5EbF1REwFzgROad2OiBdL52u4ADiobdqBeXqv6fS3R3vt\n75EuyOu4ta6/VDpQF62cWwGfAL5QOtAgLgMui4hNgI2BlYGTy0YaoLU8twDmAf9UOtBocQEMref+\njmfDpcDerbMSSVOAV0fETWVj9a1eXtdNzZxrAHNLBRmMpN2AZyPiPIBIf4D8WGCapJWLhuvuZ8B6\npUOMlp4ZzrDFFxHzJP0C2Av4L9LR//fKpupqZUkz8+8CxgNXFszTyUo5o0hnJ5+PiIsLZ+qklXMl\n4FXAboXzdLMZcGtzQkTMl3Q/sBHwmyKpBhKApLHA7sC3ysYZPS6A/jeDtONvFcDhZeN09UweUgPS\nNQBgm4J5OnlZxh62KGce/58ObF420mLptTOtVqGuD9wF/L/CeUaNh4D63xXA7pK2BlaKiNtKB7LR\nExG3AGtLWrt0lg7uArZtTsgXWCcC/79Ios5ahTqZVE5HF84zalwAfS4iFgA/Ac4GLiybZlC9dtTX\nST9khEZOSZuSPsdPlovTWUT8mHR0fSgsGmL5MnBaRDxfNNzLCSAingOOAT4qqYp9YxVvsgIXAlvS\n2wXQa9/46WTFtq+Bfq50oC4W5SSt82n5Amsv2g/YX9LdwBPAwojotW8tLVp2EXE78GsGfrtumaTe\n3W6WnKSIiJ4/mnPOkeWcI2ukc+brFRcC++Ud7Ug8Z5XLcqS4AApyzpHlnCOrH3L2Q0bo3ZweAjIz\nq5QLwMysUi4AM7NKuQDMzCrlAjAzq5QLwMysUi4AM7NKuQDMzCrlAjAzq5QLwMysUi4AM7NKuQDM\nzCrlAjAzq9Qy+6+Bls5gZtYwLyImlA7RbpksgKWhV/8513bOObKcc+T0Q0bon5wjwUNAZmaVcgGY\nmVXKBWBmVikXgJlZpVwAZmaVcgGYmVXKBWBmVikXgJlZpVwAZmaVcgGYmVXKBWBmVikXgJlZpVwA\nZmaVcgGYmVXKBWBmVikXgJlZpVwAZmaVcgGMEkn7SrpN0sz8c5ukhZL+tnS2dpLml84wHJJeknRe\n4/ZYSY9LurJkriZJ60u6T9Ka+fb4fHty6Wzt8jb6kqRNSmcZjKSJki6UdI+kX0q6StJGpXP1IxfA\nKImIyyNi64iYGhFTgTOAGyLih6WzddAvfyd0AbC5pBXy7T2A2QXzDBARD5HW9RfzpC8A/xkRD5ZL\n1dWBwI3AQaWDDOH7wLURsXFEbAf8KzCxcKa+5AIoIB9hnQAcWjrLMuAaYJ/8+0HAhQWzdPNV4E2S\njgF2BL5SOM8AklYBdgLeRw8XgKS3An+JiLNa0yJiVkTcVDBW33IBjDJJ44DzgWMj4uHSefpcADOA\ng/JZwJbAz8tGGigiXgSOA04FjomIhYUjdfJO4AcRcS/whKStSwfqYnPg1tIhlhUugNH378AdEXFJ\n6SDLgoi4A3gt6aj1akBFA3W3NzAH2KJ0kC4OIpUpwEXAwQWz2CgZVzpATSTtCuwH9OrRVb+6EjgZ\n2BVYu2yUgSRtBewO7ADcJGlGRPyxcKxFJI0HdiNdTwlgLOns6p+LBuvsTuDdpUMsK3wGMEryh+xs\nYFpEPFM6zxB69Si6XSvn2cCnI+LOkmEGcQZp6Och4Ev03jWA/YHzIuJ1EbFBREwB7pe0c+lg7SLi\nWmB5Se9vTZO0haSdCsbqWy6A0XMEsA5wZuNroDMl7V86WAf98i2gAIiIhyPi9NJhOpH0AeCBvOMC\nOBPYVNIuBWO1ew/pmzVNl9G7F4P3A/aQdK+kWcDngEcLZ+pLiuiXz3pZkiIiev7I2DlHlnOOnH7I\nCP2TcyT4DMDMrFIuADOzSrkAzMwq5QIwM6uUC8DMrFIuADOzSrkAzMwq5QIwM6uUC8DMrFIuADOz\nSrkAzMwq5QIwM6uUC8DMrFIuADOzSi2T/xy0pLnA+NI5zMyyeRExoXSIdstqAfTFv+ftnCPLOUdW\nP+Tsh4zQuzk9BGRmVikXgJlZpVwAZmaVcgGYmVXKBWBmVikXgJlZpVwAZmaVcgGYmVXKBWBmVikX\ngJlZpVwAZmaVcgGYmVXKBWBmVikXgJlZpVwAZmaVcgGYmVXKBWBmVikXwCAkHS/pDkm/ljRT0nal\nM7WTtDBnmyXpIkkrls7UiaR1JZ0v6V5Jv5R0k6R3ls7VTtJ6ki6XdLekeySdKmlc6VxNjXV+W/7v\n5NKZOpF0o6Q9G7f3l3RNyUydSJpfOkMpLoAuJO0A7A1sFRFvBP43MLtsqo4WRMTUiNgCeAE4snSg\nLi4HfhIRG0XEdsCBwPqFM3VyGXBZRGwCbAKsBnyubKQBWut86/zfB0sH6uJI4BRJy0taFfgscFTh\nTJ0se38Xd5h66simx7waeCIiXgSIiLmF8wzHjcAWpUO0k7Qb8HxEnNWaFhGzga+XSzVQzvlsRJwH\nEBEh6VjgfkknRMRzZRMu0nN/W7aTiLhT0pXAx4FVgHMj4g9lU1mTzwC6+xEwWdLvJH1d0ltKB+pC\nAHmYYi9gVtk4HW0GzCwdYhg2A25tToiI+cADwEZFEnW2UmMI6NLSYYZwEnAwsCfwpcJZrI3PALqI\niAWSpgK7ALsBMyR9vHV02ENWktTaud4IfLtkmOGQdDqwM+ms4E2l8wxDrx1xPxMRU0uHGI6IeEbS\nRcD8iHihdB57ORfAICIigBuAGyTNAqYBvVYA/bAzuBN4V+tGRBwtaS3gl+UidXQX8O7mBEmrA5OA\ne4skWja8lH+sx3gIqAtJm0hqnvZvRRoK6DW9dnQ6QERcC6wg6YjG5FVK5ekmIn5MOqM6FEDSWODL\nwDk9NP4PfbDO+0y1y9MF0N2qwLn5a6C3A68HPlU2Ukf98g2GfYFdJf1e0i3AOcBxhTN1sh9wgKS7\ngd8BzwLHl400QL+s836xkqQHJc3O//1w6UCjRWmUY9kiKSKi51vdOUeWc46sfsjZDxmhd3P6DMDM\nrFIuADOzSrkAzMwq5QIwM6uUC8DMrFIuADOzSrkAzMwq5QIwM6uUC8DMrFIuADOzSrkAzMwq5QIw\nM6uUC8DMrFIuADOzSi2z/xx06QxmZg3zImJC6RDtlskCMDOzoXkIyMysUi4AM7NKuQDMzCrlAjAz\nq5QLwMysUi4AM7NKuQDMzCrlAjAzq5QLwMysUi4AM7NKuQDMzCrlAjAzq5QLwMysUi4AM7NKuQDM\nzCrlAjAzq5QLwMysUi4AM7NKuQDMzCrlAjAzq5QLwMysUi4AM7NKuQDMzCrlAjAzq5QLwMysUi4A\nM7NKuQDMzCrlAjAzq5QLwMysUi4AM7NKuQDMzCrlAjAzq5QLwMysUi4AM7NKuQDMzCrlAjAzq5QL\nwMysUi4AM7NKuQDMzCrlAjAzq5QLwMysUi4AM7NKuQDMzCrlAjAzq5QLwMysUi4AM7NKuQDMzCr1\nP/K3c1luY46PAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10c12cbe0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_kbd(Keyboard((' U D J K N W ',\n",
    "                   'T V H E B Q R',\n",
    "                   ' Z I M X A C ',\n",
    "                   'S P G O F Y L')))"
   ]
  },
  {
   "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": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nfLXp0dsei7D9fuTurxcx1seGEoO0xVnAVsCtqe6qL5nZsYUOZGZnAG8nF22AW4FxZvaZQsdKHA1M\nNLO5ZvYacDWwrEixfkmoGy92YuiT2U9JrMlenOfup+f5U2CLrGEFj+Xui9395iLF+NjR7zFIpLs+\nC1+xukac7hyrK1GJQUREIkoMIiISUWIQEZGIEoOIiESUGEREJKLEICIiESUGERGJKDGIiEhEX3CT\niJnpgJCPk2p3b4/fjehSlBhERCSiqiQREYkoMYiISESJQUREIkoMIiISUWIQEZGIEoOIiESUGERE\nJKLEICIiESUGERGJKDGIiEhEiUFERCJKDCIiElFiEBGRiBKDiIhElBhERCSixCAiIhElBhERiSgx\niIhIRIlBREQiSgwiIhJRYhARkYgSg4iIRJQYREQkosQgIiIRJQYREYkoMYiISESJQUREIkoMIiIS\nUWIQEZGIEoOIiESUGEREJKLEICIiESUGERGJKDGIiEhEiUFERCJKDCIiElFiEBGRiBKDiIhElBhE\nRCSixCAiIhElBhERiSgxiIhIRIlBREQiSgwiIhJRYhARkYgSg4iIRJQYREQkosQgIiIRJQYREYko\nMYiISESJQUREIkoMIiISUWIQEZGIEoOIiESUGEREJKLEICIiESUGERGJKDGIiEhEiUFERCJKDCIi\nElFiEBGRiBKDiIhElBhERCSixCAiIhElBhERiSgxiIhIRIlBREQiSgwiIhJRYhARkYgSg4iIRJQY\nREQkosQgIiIRJQYREYkoMYiISESJQUREIkoMIiISUWIQEZGIEoOIiESUGEREJKLEICIiESUGERGJ\nKDGIiEhEiUFERCJKDCIiElFiEBGRiBKDiIhElBhERCSixCAiIhElBhERiSgxiIhIRIlBREQi/x8t\nGVDmodqb0gAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10c05ae48>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "show_kbd(qwerty, words=['POUTS', 'POIUYTS', 'PUTTS', 'PUTS', 'POTS', 'PITS'])"
   ]
  }
 ],
 "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.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
