From 74f5dbb620ef6f03af5b6515920b49661909b77c Mon Sep 17 00:00:00 2001 From: nihonium Date: Thu, 6 Oct 2022 20:45:41 +0300 Subject: [PATCH 1/2] working dijkstra --- 03_dijkstra/LeastReachableCity.hs | 65 +++++++++++++++++++++++++++++++ 03_dijkstra/Types.hs | 13 +++++++ 03_dijkstra/main.hs | 51 ++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 03_dijkstra/LeastReachableCity.hs create mode 100644 03_dijkstra/Types.hs create mode 100644 03_dijkstra/main.hs diff --git a/03_dijkstra/LeastReachableCity.hs b/03_dijkstra/LeastReachableCity.hs new file mode 100644 index 0000000..a57afd2 --- /dev/null +++ b/03_dijkstra/LeastReachableCity.hs @@ -0,0 +1,65 @@ +module LeastReachableCity where + +import Types +import qualified Data.Map as Map +import qualified Data.Set as Set + +infinity :: Len +infinity = 999999 :: Len + +dijkstra :: Point -> Graph -> Distances +dijkstra start graph = dijkstra' start graph visited_init to_visit_init distances_init where + -- Helper + dijkstra' :: Point -> Graph -> Vertices -> Vertices -> Distances -> Distances + dijkstra' start graph visited to_visit distances = if Set.null to_visit + then + distances + else + dijkstra' + (findMinNotVisited to_visit distances) + graph + (Set.insert start visited) + (Set.union (Set.delete start to_visit) $ findNotVisitedNeighbours graph visited start) + (updateDistances graph start distances) + -- Other + to_visit_init = (Set.insert start $ Set.empty :: Set.Set Point) + distances_init = (infinityDistances start $ countVertices graph) + visited_init = (Set.empty :: Set.Set Point) + +updateDistances :: Graph -> Point -> Distances -> Distances +updateDistances graph point distances = snd $ Map.foldrWithKey decideMin ((point, Map.findWithDefault (-1) point distances), Map.empty :: Distances) distances where + decideMin :: Point -> Len -> ((Point, Len), Distances) -> ((Point, Len), Distances) + decideMin p' l' ((p, l), dist) = if findDistance graph p p' + l < l' then + ((p, l), Map.insert p' (l + findDistance graph p p') dist) else + ((p, l), Map.insert p' l' dist) + +findDistance :: Graph -> Point -> Point -> Len +findDistance ((Node (a, b, len)) : graph) p p' = if ((a == p) && (b == p') || (b == p) && (a == p')) then len else findDistance graph p p' +findDistance _ _ _ = infinity + +findMinNotVisited :: Vertices -> Distances -> Point +findMinNotVisited to_visit distances = fst $ Set.foldl (\(p, l) p' -> case Map.lookup p' distances of + (Just l') -> if l' < l then + (p', l') + else (p,l)) + (-1, maxBound) to_visit + +infinityDistances :: Point -> Int -> Distances +infinityDistances point n = Map.fromList $ (point, 0) : [(x, infinity) | x <- [1..n], x /= point] + +countVertices :: Graph -> Int +countVertices graph = snd $ foldl f (Set.empty :: Set.Set Point, 0) graph where + f :: (Vertices, Int) -> Node -> (Vertices, Int) + f (vertices, n) (Node (a, b, _)) = + let aIsNotMember = not $ Set.member a vertices + bIsNotMember = not $ Set.member b vertices in if (aIsNotMember && bIsNotMember) then (Set.insert a $ Set.insert b vertices, n + 2) + else if (aIsNotMember) then (Set.insert a vertices, n + 1) + else if (bIsNotMember) then (Set.insert b vertices, n + 1) + else (vertices, n) + +findNotVisitedNeighbours :: Graph -> Vertices -> Point -> Vertices +findNotVisitedNeighbours graph visited point = foldl f (Set.empty :: Vertices) graph where + f :: Vertices -> Node -> Vertices + f vertices (Node (a, b, _)) = if ((a == point) && (not $ Set.member b visited)) then Set.insert b vertices + else if ((b == point) && (not $ Set.member a visited)) then Set.insert a vertices + else vertices diff --git a/03_dijkstra/Types.hs b/03_dijkstra/Types.hs new file mode 100644 index 0000000..533feda --- /dev/null +++ b/03_dijkstra/Types.hs @@ -0,0 +1,13 @@ +module Types where + +import qualified Data.Map as Map +import qualified Data.Set as Set + +type Point = Int +type Len = Int + +data Node = Node (Point, Point, Len) deriving Show +type Graph = [Node] + +type Distances = Map.Map Point Len +type Vertices = Set.Set Point diff --git a/03_dijkstra/main.hs b/03_dijkstra/main.hs new file mode 100644 index 0000000..190befa --- /dev/null +++ b/03_dijkstra/main.hs @@ -0,0 +1,51 @@ +module Main where + +import System.IO +import System.Environment +import System.Directory +import qualified Data.Map as Map +import qualified Data.Set as Set + +import LeastReachableCity +import Types + +{- format of input file is: + - mileage + - from to len + - from to len + - ... + - where "from" and "to" are numbers of graph vertices, "len" is distance between them + - -} +main :: IO() +main = do + argv <- getArgs + + filename <- if not $ null argv then do + putStrLn $ "Trying to read file " ++ "\"" ++ head argv ++ "\"" + head <$> getArgs + else do + error "Not enough arguments,\nusage: main [filename]" + + content <- readFile filename + let mileage = read $ head $ lines content :: Len + + let graph = readGraph $ tail content + --let dist = findNeighbours 1 graph + --print $ findNotVisitedNeighbours graph (Set.fromList [1,3]) 2 + print $ dijkstra 1 graph + --print $ updateDistances graph 2 (Map.fromList [(1, 100), (2, 1), (3, 200)]) + --print $ infinityDistances 1 4 + --print $ findMinNotVisited (Set.fromList [1, 2, 3] :: Set.Set Int) (Map.fromList [(1, 123), (2, 114), (3, 115)]) + +{- For parsing graph using content of the file -} +parseStrNode :: [String] -> Node +parseStrNode [a, b, c] = Node (p a, p b, l c) where + p x = read x :: Point + l x = read x :: Len + +readGraph :: String -> Graph +readGraph content = parse lst where + parse (x:xs) = if length ws == 3 then (parseStrNode ws) : (parse xs) else parse xs where + ws = words x + parse _ = [] + lst = lines content From a31509d093b1e2bab4d4d17eca729c22d49bfce4 Mon Sep 17 00:00:00 2001 From: nihonium Date: Thu, 6 Oct 2022 21:28:51 +0300 Subject: [PATCH 2/2] least reachable city --- 03_dijkstra/LeastReachableCity.hs | 26 ++++++++++++++++++-------- 03_dijkstra/data | 9 +++++++++ 03_dijkstra/main.hs | 15 +++++---------- 3 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 03_dijkstra/data diff --git a/03_dijkstra/LeastReachableCity.hs b/03_dijkstra/LeastReachableCity.hs index a57afd2..3c6abaa 100644 --- a/03_dijkstra/LeastReachableCity.hs +++ b/03_dijkstra/LeastReachableCity.hs @@ -1,15 +1,25 @@ -module LeastReachableCity where +module LeastReachableCity(dijkstra, leastReachableCity) where import Types import qualified Data.Map as Map import qualified Data.Set as Set +-- Well, it's just a magic number, because it's too much hassle to implement type like "Len val | Infinity" +-- TODO: get graph radius and use this number as infinity infinity :: Len infinity = 999999 :: Len +leastReachableCity :: Graph -> Len -> (Point, Int) +leastReachableCity graph mileage = foldl (\(p, n) (p', n') -> + if n' < n then (p',n') + else (p, n)) + (-1, infinity) [(city, countReachableCities $ dijkstra city graph) | city <- Set.toList $ fst $ countVertices graph] where + countReachableCities :: Distances -> Int + countReachableCities distances = Map.foldl (\n val -> if (val <= mileage) && (val /= 0) then n + 1 else n) 0 distances + dijkstra :: Point -> Graph -> Distances dijkstra start graph = dijkstra' start graph visited_init to_visit_init distances_init where - -- Helper + -- Helper (so we don' need to call dijkstra initializing empty visited, to_visit etc) dijkstra' :: Point -> Graph -> Vertices -> Vertices -> Distances -> Distances dijkstra' start graph visited to_visit distances = if Set.null to_visit then @@ -22,9 +32,9 @@ dijkstra start graph = dijkstra' start graph visited_init to_visit_init distance (Set.union (Set.delete start to_visit) $ findNotVisitedNeighbours graph visited start) (updateDistances graph start distances) -- Other - to_visit_init = (Set.insert start $ Set.empty :: Set.Set Point) - distances_init = (infinityDistances start $ countVertices graph) - visited_init = (Set.empty :: Set.Set Point) + to_visit_init = (Set.insert start $ Set.empty :: Vertices) + distances_init = (infinityDistances start $ snd $ countVertices graph) + visited_init = (Set.empty :: Vertices) updateDistances :: Graph -> Point -> Distances -> Distances updateDistances graph point distances = snd $ Map.foldrWithKey decideMin ((point, Map.findWithDefault (-1) point distances), Map.empty :: Distances) distances where @@ -42,13 +52,13 @@ findMinNotVisited to_visit distances = fst $ Set.foldl (\(p, l) p' -> case Map.l (Just l') -> if l' < l then (p', l') else (p,l)) - (-1, maxBound) to_visit + (-1, infinity) to_visit infinityDistances :: Point -> Int -> Distances infinityDistances point n = Map.fromList $ (point, 0) : [(x, infinity) | x <- [1..n], x /= point] -countVertices :: Graph -> Int -countVertices graph = snd $ foldl f (Set.empty :: Set.Set Point, 0) graph where +countVertices :: Graph -> (Vertices, Int) +countVertices graph = foldl f (Set.empty :: Vertices, 0) graph where f :: (Vertices, Int) -> Node -> (Vertices, Int) f (vertices, n) (Node (a, b, _)) = let aIsNotMember = not $ Set.member a vertices diff --git a/03_dijkstra/data b/03_dijkstra/data new file mode 100644 index 0000000..9903946 --- /dev/null +++ b/03_dijkstra/data @@ -0,0 +1,9 @@ +4 + +1 2 10 +1 4 2 +1 3 3 +3 2 5 +2 4 6 +4 5 4 +5 3 1 diff --git a/03_dijkstra/main.hs b/03_dijkstra/main.hs index 190befa..8936dc7 100644 --- a/03_dijkstra/main.hs +++ b/03_dijkstra/main.hs @@ -2,7 +2,7 @@ module Main where import System.IO import System.Environment -import System.Directory + import qualified Data.Map as Map import qualified Data.Set as Set @@ -21,21 +21,16 @@ main = do argv <- getArgs filename <- if not $ null argv then do - putStrLn $ "Trying to read file " ++ "\"" ++ head argv ++ "\"" head <$> getArgs else do - error "Not enough arguments,\nusage: main [filename]" + putStrLn "Usage: ./lrc [filename]\nNow defaulting to file \"data\"" + return "data" content <- readFile filename let mileage = read $ head $ lines content :: Len - let graph = readGraph $ tail content - --let dist = findNeighbours 1 graph - --print $ findNotVisitedNeighbours graph (Set.fromList [1,3]) 2 - print $ dijkstra 1 graph - --print $ updateDistances graph 2 (Map.fromList [(1, 100), (2, 1), (3, 200)]) - --print $ infinityDistances 1 4 - --print $ findMinNotVisited (Set.fromList [1, 2, 3] :: Set.Set Int) (Map.fromList [(1, 123), (2, 114), (3, 115)]) + let (city, n) = leastReachableCity graph mileage + putStrLn $ "The least reachable city is " ++ (show city) ++ " (" ++ (show n) ++ " city/ies is/are reachable)" {- For parsing graph using content of the file -} parseStrNode :: [String] -> Node