Seven Bridges to Cross
Programming Snapshot – Graph Theory
Pretty much any computer science lecture about graph theory covers the "Seven Bridges of Königsberg" problem. Mike Schilli puts a Python script to work on a solution, but finds that a new bridge must be built.
The task of crossing the seven bridges over the Pregola River on a city tour of Königsberg (nowadays known as Kaliningrad) without missing one or walking across one twice [1] is simply captivating.
The Swiss mathematician Leonhard Euler already proved that this was impossible as early as 1736, but the task is still useful as a mathematical brain teaser today because the network of bridges can be converted into a graph (Figure 1) and bombarded with graph-theory axioms and algorithms.
Euler found that Königsberg's land masses can be represented as nodes in a graph, connected by seven paths – referred to as edges by graph theorists – that represent the connecting bridges (a
to g
in Figure 2). The task for the Königsberg city guide is therefore to cover all the edges drawn in the graph without passing a segment more than once.
Leonhard Euler recognized that the number of edges leading to a node (he called it "degree" of the node) directly determines whether or not the problem of a repetition-free walk-through can be solved.
Odd or Even
If each node has an even number of edges, a tourist can walk across all the bridges in sequence without ever getting stuck. A similar situation occurs if exactly two of the nodes in the graph have an odd number of access points and the remaining nodes have an even number – in this case, the walker starts walking at the first of the odd nodes and ends the walk at the second odd node. In all other cases, for example also in the present Königsberg bridge problem, where all four nodes exhibit odd degrees, a repetition-free round trip is mathematically impossible.
Brute Force
For fun, you could now tackle the problem with a Python script that marches through the graph and only stops if it cannot continue at a node, because all adjacent segments have already been traversed. If a following check shows that the trip has covered all segments of the graph, the result is the correct solution to the problem. In the case of Königsberg, this is not possible. However, as we'll see later, if you add another connection to the graph, there is a way.
For the script to try all possible path combinations, a recursive depth-first algorithm works through the different bridge combinations. On the journey, it keeps track of the crossed bridges in a dictionary and does not take a route that leads over a bridge that previously has been passed.
If it gets stuck on a node, it resets and tries further combinations of previously chosen directions at branches. It stores the longest path found so far in an object attribute for later reference. If its length later matches the number of all defined bridges, the script has solved the problem.
Inserting the Graph
As a first task, the script in Listing 1 [2] models the graph from Figure 2 in the data structure starting on line 4. Node names ("1"
, "2"
, etc.) are used as keys into the dictionary, whose values consist of lists of connected nodes along with a list of path options, for example ["a", "b"]
in the first entry, because bridges a
and b
lead from node 1
to node 2
.
Listing 1
koenigsberg.py
01 #!/usr/bin/env python3 02 from bridgewalk import BridgeWalk 03 04 g = { "1" : [["2", ["a", "b"]], 05 ["3", ["d", "e"]], 06 ["4", ["c"]] 07 ], 08 "2" : [["1", ["a", "b"]], 09 ["4", ["f"]] 10 ], 11 "3" : [["1", ["d", "e"]], 12 ["4", ["g"]], 13 ], 14 "4" : [["2", ["f"]], 15 ["3", ["g"]], 16 ["1", ["c"]] 17 ] 18 } 19 20 trail = BridgeWalk(g) 21 trail.explore() 22 23 print(trail.maxpath) 24 25 if len(trail.bridges) == \ 26 len(trail.maxpath): 27 print("Solved!") 28 else: 29 print("Impossible!")
This puzzle's definition gets passed to the BridgeWalk
class' constructor in line 20; the class itself is defined further down in Listing 2. The explore()
method on the resulting object then shimmies through the graph to find the longest path without an overlap.
Listing 2
bridgewalk.py
01 #!/usr/bin/env python3 02 class BridgeWalk(object): 03 def __init__(self, graph): 04 self.graph = graph 05 self.bridges = {} 06 self.maxpath = [] 07 08 for node in self.graph: 09 for fork in self.graph[node]: 10 for bridge in fork[1]: 11 self.bridges[bridge] = 1 12 13 def explore(self): 14 # try different start nodes 15 for node in self.graph: 16 self.scan(node) 17 18 def scan(self, node, path=[], seen={}): 19 # try different connected nodes 20 for fork in self.graph[node]: 21 # try all bridges leading there 22 for bridge in fork[1]: 23 self.bridge_ok(bridge, fork[0], 24 path.copy(), seen.copy()) 25 26 def bridge_ok(self, bridge, 27 node, path, seen): 28 if not bridge in seen: 29 seen[bridge]=1 30 path.append(bridge) 31 32 if len(self.maxpath) < len(path): 33 self.maxpath = path.copy() 34 35 # recurse 36 self.scan(node, path, seen)
At any point in time during the run, the maxpath
object attribute contains a list of bridges in the order they were crossed. The print()
statement in line 23 in Listing 1 automatically converts the list into a string and outputs it. The bridges
attribute maintains a dictionary with the keys for all bridges defined in the graph for easy counting of unique bridges.
If the number of bridges in the graph determined with len()
matches the number of bridges on the longest path in maxpath
, the script in line 27 reports the successful solution of the puzzle.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
AlmaLinux OS Kitten 10 Gives Power Users a Sneak Preview
If you're looking to kick the tires of AlmaLinux's upstream version, the developers have a purrfect solution.
-
Gnome 47.1 Released with a Few Fixes
The latest release of the Gnome desktop is all about fixing a few nagging issues and not about bringing new features into the mix.
-
System76 Unveils an Ampere-Powered Thelio Desktop
If you're looking for a new desktop system for developing autonomous driving and software-defined vehicle solutions. System76 has you covered.
-
VirtualBox 7.1.4 Includes Initial Support for Linux kernel 6.12
The latest version of VirtualBox has arrived and it not only adds initial support for kernel 6.12 but another feature that will make using the virtual machine tool much easier.
-
New Slimbook EVO with Raw AMD Ryzen Power
If you're looking for serious power in a 14" ultrabook that is powered by Linux, Slimbook has just the thing for you.
-
The Gnome Foundation Struggling to Stay Afloat
The foundation behind the Gnome desktop environment is having to go through some serious belt-tightening due to continued financial problems.
-
Thousands of Linux Servers Infected with Stealth Malware Since 2021
Perfctl is capable of remaining undetected, which makes it dangerous and hard to mitigate.
-
Halcyon Creates Anti-Ransomware Protection for Linux
As more Linux systems are targeted by ransomware, Halcyon is stepping up its protection.
-
Valve and Arch Linux Announce Collaboration
Valve and Arch have come together for two projects that will have a serious impact on the Linux distribution.
-
Hacker Successfully Runs Linux on a CPU from the Early ‘70s
From the office of "Look what I can do," Dmitry Grinberg was able to get Linux running on a processor that was created in 1971.