{"id":507,"date":"2007-09-10T09:00:00","date_gmt":"2007-09-10T09:00:00","guid":{"rendered":"http:\/\/scientopia.org\/blogs\/goodmath\/2007\/09\/10\/puzzling-graphs-problem-modeling-with-graphs\/"},"modified":"2007-09-10T09:00:00","modified_gmt":"2007-09-10T09:00:00","slug":"puzzling-graphs-problem-modeling-with-graphs","status":"publish","type":"post","link":"http:\/\/www.goodmath.org\/blog\/2007\/09\/10\/puzzling-graphs-problem-modeling-with-graphs\/","title":{"rendered":"Puzzling Graphs: Problem Modeling with Graphs"},"content":{"rendered":"<p> As I&#8217;ve mentioned before, the real use of graphs is as <em>models<\/em>. Many real problems can be<br \/>\ndescribed using graphs as models &#8211; that is, to translate the problem into a graph, solve some problem on the graph, and then translate the result back from the graph to the original problem. This kind of<br \/>\nsolution is extremely common, and can come up in some unexpected places.<\/p>\n<p> For example, there&#8217;s a classic chess puzzle called the <a href=\"http:\/\/www.borderschess.org\/KnightTour.htm\">Knight&#8217;s tour.<\/a><br \/>\nIn the Knight&#8217;s tour, you have a chessboard, completely empty except for a knight on one square. You can<br \/>\nmove the knight the way you normally can in chess, and you want to find a sequence of moves in which is<br \/>\nvisits every square on the chessboard exactly once. There are variations of the puzzle for non-standard<br \/>\nchessboards &#8211; boards larger or smaller than normal, toroidal boards (where you can wrap around the left edge to the right, or the top to the botton), etc. So &#8211; given a particular board, how can you<br \/>\n(1) figure out if it&#8217;s possible to do a knights tour, and (2) find the sequence of moves in a tour<br \/>\nif one exists?<\/p>\n<p><!--more--><\/p>\n<p> As a reminder, t<img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"knights-moves.png\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_267.png?resize=160%2C160\" width=\"160\" height=\"160\" class=\"inset right\" \/><br \/>\nhe basic knight&#8217;s move on a chessboard is illustrated in the diagram to the side. \tThe way it works is, a knight can move one two squares in one direction, and one square\tperpendicular to that direction. From a given spot far from the edges of the board, a knight can move to 8 different destination squares. <\/p>\n<p> Trying to solve the moves for a knight&#8217;s tour is frustrating &#8211; as it often is for puzzles with so many possible solutions. You need to figure out what the real underlying constraints of the puzzle are &#8211; trial and error isn&#8217;t going to get you very far unless you&#8217;re awfully lucky. Writing a program to solve it<br \/>\nisn&#8217;t too hard &#8211; but it&#8217;s very error prone, unless you&#8217;re playing on a toroidal board. The edge cases &#8211; the places where some of the 8 possible moves are cut off by the edge of the board &#8211; are classic examples<br \/>\nof the kind of place where people make off-by-one errors. And the tangling of finding possible<br \/>\nmoves with the logic of searching the possible solution space makes it extremely messy &#8211; increasing the<br \/>\nodds of an error creeping in. And even if you get it right, most programs search for a solution even<br \/>\nin cases where it should be obvious that there is no solution. For example, on a 3&#215;3, there&#8217;s definitely<br \/>\nno possible solution.<\/p>\n<p> For example, here&#8217;s a basic Knight&#8217;s tour in Haskell:<\/p>\n<pre>\nmodule Knights where\nimport Data.List\ntype Square = (Int, Int)\n-- Possible knight moves from a given square on an nxn board\nknightMoves :: Int -&gt; Square -&gt; [Square]\nknightMoves n (x, y) = filter (onBoard n)\n[(x+2, y+1), (x+2, y-1), (x+1, y+2), (x+1, y-2),\n(x-1, y+2), (x-1, y-2), (x-2, y+1), (x-2, y-1)]\n-- Is the square within an nxn board?\nonBoard :: Int -&gt; Square -&gt; Bool\nonBoard n (x, y) = 1 &lt;= x &amp;&amp; x &lt;= n &amp;&amp; 1 &lt;= y &amp;&amp; y &lt;= n\n-- Knight's tours on an nxn board ending at the given square\nknightsTo :: Int -&gt; Square -&gt; [[Square]]\nknightsTo n finish = [pos:path | (pos, path) &lt;- tour (n*n)]\nwhere tour 1 = [(finish, [])]\ntour k = [(pos', pos:path) |\n(pos, path) &lt;- tour (k-1),\npos' &lt;- sortImage (entrances path)\n(filter (`notElem` path) (knightMoves n pos))]\nentrances path pos =\nlength (filter (`notElem` path) (knightMoves n pos))\n-- Closed knight's tours on an nxn board\nclosedKnights :: Int -&gt; [[Square]]\nclosedKnights n = [pos:path | (pos, path) &lt;- tour (n*n), pos == start]\nwhere tour 1 = [(finish, [])]\ntour k = [(pos', pos:path) |\n(pos, path) &lt;- tour (k-1),\npos' &lt;- sortImage (entrances path)\n(filter (`notElem` path) (knightMoves n pos))]\nentrances path pos\n| pos == start = 100  -- don't visit start until there are no others\n| otherwise = length (filter (`notElem` path) (knightMoves n pos))\nstart = (1,1)\nfinish = (2,3)\n-- Sort by comparing the image of list elements under a function f.\n-- These images are saved to avoid recomputation.\nsortImage :: Ord b =&gt; (a -&gt; b) -&gt; [a] -&gt; [a]\nsortImage f xs = map snd (sortBy cmpFst [(f x, x) | x &lt;- xs])\nwhere cmpFst x y = compare (fst x) (fst y)\n<\/pre>\n<p> The better solution is to use a graph to solve the problem. This separates the problem of understanding the structure of the search space from actually searching that space. What we do is take the chessboard, and name each square. We create a graph where there is a one-to-one mapping between vertices of the graph, and squares on the chessboard. Then we put edges between two vertices A and B if and only if there is a knight&#8217;s move between the squares A and B on the chessboard. For example, see the picture below for an example of doing this on a 4&times;4 chessboard.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"knights-graph.png\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_268.png?resize=447%2C212\" width=\"447\" height=\"212\" \/><\/p>\n<p> Once you&#8217;ve got the graph, then finding a Knight&#8217;s tour is just finding a Hamiltonian path through the graph. That&#8217;s not quite trivial &#8211; but generating the graph from the chessboard is easy, and Hamiltonian searches are common &#8211; you can find Hamilton path code in almost any standard graph library. Even if you need to write it yourself &#8211; there&#8217;s no worry about off-by-one errors in the search. The search<br \/>\nis much easier to write. For contrast, the following is a mathematica program for Hamiltonian paths. (I couldn&#8217;t find a Haskell example, but the Mathematica is close in style, so it&#8217;s a reasonable<br \/>\ncomparison.)<\/p>\n<pre>\nHamiltonianPath[g_Graph] := HamiltonianPath[g, One]\nHamiltonianPath[g_Graph, One] :=\nModule[{c = HamiltonianCycle[g], nonEdges, p, q},\nIf[c != {},\nDrop[c, -1],\nnonEdges = Complement[Flatten[Table[{i, j}, {i, V[g]-1}, {j, i+1, V[g]}],1], Edges[g]];\nDo[h = AddEdges[g, nonEdges[[i]]];\nIf[((BiconnectedQ[h]) &amp;&amp; ((c = HamiltonianCycle[h]) != {})),\np = Position[c = Drop[c,-1], nonEdges[[i, 1]]][[1, 1]];\nc = RotateLeft[c, p-1];\nIf[nonEdges[[i, 2]] == c[[2]], c = RotateLeft[c, 1]];\nBreak[]\n],\n{i, Length[nonEdges]}\n];\nc\n]\n]\n<\/pre>\n<p> The problem has basically become easier, because you&#8217;ve separated<br \/>\nthe problem into two disjoint pieces, each of which is easy to solve, and you&#8217;ve made the logic of<br \/>\nthe search much clearer. And if you look at the graphical form, you can see the structure much more clearly &#8211; which means you can more easily search it to see if there&#8217;s anything making a path impossible. It&#8217;s much easire to understand in graph form. For example, go back to the 3&times;3 grid. If you try to turn that into a graph, you&#8217;ll find that it&#8217;s not connected. The middle square is isolated. You can&#8217;t have a Hamiltonian path over a disconnected graph.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As I&#8217;ve mentioned before, the real use of graphs is as models. Many real problems can be described using graphs as models &#8211; that is, to translate the problem into a graph, solve some problem on the graph, and then translate the result back from the graph to the original problem. This kind of solution [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[25],"tags":[],"class_list":["post-507","post","type-post","status-publish","format-standard","hentry","category-graph-theory"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4lzZS-8b","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/507","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/comments?post=507"}],"version-history":[{"count":0,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/507\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/media?parent=507"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/categories?post=507"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/tags?post=507"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}