-
Notifications
You must be signed in to change notification settings - Fork 0
/
HamiltonianCycle.hs
94 lines (83 loc) · 4.52 KB
/
HamiltonianCycle.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
module HamiltonianCycle (
hasHamiltonianCycle
) where
import Hypercube
import Data.List(delete)
import Debug.Trace
import qualified Data.Map as Map
import Data.Map (Map)
import Data.Maybe (fromJust)
import qualified Data.Set as Set
import Data.Set (Set)
-- Turns on debug mode
debugMode :: Bool
debugMode = False
-- Helper function to conditionally print trace messages
traceIf :: String -> a -> a
traceIf msg x = if debugMode then trace msg x else x
-- Creates a bidirectional map from a list of edges
createBidirectionalMap :: [Edge] -> Map Vertex Vertex
createBidirectionalMap edges = Map.fromList $ concatMap edgeToTuples edges
where
edgeToTuples (Edge v1 v2) = [(v1, v2), (v2, v1)]
-- Lookup in the bidirectional map
lookupVertex :: Vertex -> Map Vertex Vertex -> Maybe Vertex
lookupVertex = Map.lookup
-- Lookup function, True/False
lookupIsSuccessful :: Maybe a -> Bool
lookupIsSuccessful (Just _) = True
lookupIsSuccessful Nothing = False
-- Given an (Edge v1 v2), removes both (Edge v1 v2) and (Edge v2 v1) from given list of edges
removeUndirectedEdge :: [Edge] -> Edge -> [Edge]
removeUndirectedEdge edges (Edge v1 v2) = secondDel
where
firstDel = delete (Edge v1 v2) edges
secondDel = delete (Edge v2 v1) firstDel
-- Check if a Hamiltonian cycle exists
hasHamiltonianCycle :: [Vertex] -> [Edge] -> Bool
hasHamiltonianCycle vertices mandatoryEdges =
_hasHamiltonianCycle vertices (createBidirectionalMap mandatoryEdges) mandatoryEdges generateHypercubeNeighbors
-- Check if a Hamiltonian cycle exists, main algorithm
_hasHamiltonianCycle :: [Vertex] -- List of vertices
-> Map Vertex Vertex -- Mandatory edge lookup
-> [Edge] -- Mandatory edges
-> (Vertex -> [Vertex]) -- Function to get neighbors
-> Bool -- Result: True if Hamilton cycle exists, False otherwise
_hasHamiltonianCycle vertices mandatoryNeighbors mandatoryEdges neighbors =
traceIf (show mandatoryNeighbors) $ dfs startVertex (Set.insert startVertex Set.empty) mandatoryEdges
where
startVertex = head vertices
-- Depth-first search function to find Hamiltonian cycle
dfs :: Vertex -> Set Vertex -> [Edge] -> Bool
dfs currentVertex visitedVertices remainingMandatoryEdges
-- Debug print
|traceIf ("Current Vertex: " ++ show currentVertex ++ ", Path: " ++ show visitedVertices ++ ", Remaining Mandatory Edges: " ++ show remainingMandatoryEdges) False = undefined
-- All vertices and mandatory edges were visited
| Set.size visitedVertices == length vertices
&& null remainingMandatoryEdges
&& elem startVertex (neighbors currentVertex) =
traceIf ("Cycle found with path: " ++ show visitedVertices) True
-- There is a mandatory edge to visit (and the algorithm will go this way to save time)
| lookupIsSuccessful lookupResult
&& Set.notMember (fromJust lookupResult) visitedVertices =
let nextVertex = fromJust lookupResult
updatedMandatoryEdges = removeUndirectedEdge remainingMandatoryEdges (Edge currentVertex nextVertex)
in traceIf ("Following mandatory edge from " ++ show currentVertex ++ " to " ++ show nextVertex ++ ", Updated edges:" ++ show updatedMandatoryEdges) $
dfs nextVertex (Set.insert nextVertex visitedVertices) updatedMandatoryEdges
-- If there is no mandatory edge, try all possible neighbors
| otherwise =
let currentNeighbors = neighbors currentVertex
validNextVertices = filter (`notElem` visitedVertices) currentNeighbors
in traceIf ("Current vertex: " ++ show currentVertex ++ ", valid next vertices: " ++ show validNextVertices) $
any (\v -> dfs v (Set.insert v visitedVertices) remainingMandatoryEdges) validNextVertices
where
lookupResult = lookupVertex currentVertex mandatoryNeighbors
-- TESTS
removalTest :: Bool
removalTest = test edges (Edge (Vertex [1, 1, 0]) (Vertex [1, 1, 1])) [Edge (Vertex [0, 1, 1]) (Vertex [0, 0, 1])]
&& test edges (Edge (Vertex [1, 1, 1]) (Vertex [1, 1, 0])) [Edge (Vertex [0, 1, 1]) (Vertex [0, 0, 1])]
&& test edges (Edge (Vertex [2, 2, 2]) (Vertex [1, 1, 0])) edges
where
test :: [Edge] -> Edge -> [Edge] -> Bool
test allEdges toRemove result = removeUndirectedEdge allEdges toRemove == result
edges = [Edge (Vertex [1, 1, 0]) (Vertex [1, 1, 1]), Edge (Vertex [0, 1, 1]) (Vertex [0, 0, 1])]