Commit 2def36d5 authored by Jochen Schulz's avatar Jochen Schulz
Browse files

Merge branch 'master' of gitlab.gwdg.de:jschulz1/haskell_labcourse

parents eaa40ac1 6a34fd2e
%% Cell type:markdown id: tags:
floyd-warshall algorithm
# Floyd-Warshall-Algorithmus
%% Cell type:markdown id: tags:
Der Floyd-Warshall-Algorithmus berechnet die kürzeste Verbindungen zwischen allen Paaren von Vertizes in einem Graphen.
```haskell
shortestPath :: Graph -> Vertex -> Vertex -> Vertex -> Weight
shortestPath g i j 0 = weight g i j
shortestPath g i j k = min (shortestPath g i j (k-1))
(shortestPath g i k (k-1) + shortestPath g k j (k-1))
Ein *Graph* ist eine Menge von *Vertizes* zusammen mit einer Menge von *Kanten*. Jede Kante verbindet zwei Vertizes, und zwischen zwei gegebenen Vertizes existiert maximal eine Kante.
$w(i, j)$ ist Länge der Kante von Vertex $i$ nach Vertex $j$. Der Einfachheit halber werden die Vertizes mit $0, 1, \ldots, n-1$ numeriert.
Der Algorithmus berechnet Schrittweise die Funktion $s(i, j, k)$ für alle $i$ und $j$. Dabei ist $s(i, j, k)$ der kürzeste Weg von $i$ nach $j$, der nur Vertizes ${1, \ldots, k}$ als Zwischen-Vertex verwendet. Der Algorithmus erhöht iterativ die Anzahl der zugelassenen Zwischen-Vertizes, bis der ganze Graph abgedeckt ist:
1. Wenn man keine anderen Vertizes benutzen darf, dann ist der direkte Weg der Kürzeste.
$$s(i, j, 0) = w(i, j)$$
2. Wenn ein weiterer Vertex zugelassen wird, dann ist der kürzeste Weg entweder
- der bisher gefundene Weg, oder
- der bisherige kürzeste Weg vom Startpunkt zum neuen Punkt, gefolgt vom bisherigen kürzesten Weg vom neuen Punkt zum Endpunkt.
$$s(i, j, k+1) = \min\left( s(i, j, k), s(i, k+1, k) + s(k+1, j, k) \right).$$
%% Cell type:code id: tags:
``` haskell
import Data.Array.Repa
import Data.Array.Repa.Repr.Vector
type Graph a = Array V DIM2 a
shortestPaths :: (Num a, Ord a) => Graph a -> Graph a
shortestPaths graph = step graph 0
where _:.n = extent graph
step g k
| k == n = g
| otherwise = let g' = computeVectorS $ fromFunction (ix2 n n) $ \(Z:.i:.j) ->
min (g!(Z:.i:.j)) (g!(Z:.i:.k) + g!(Z:.k:.j))
in step g' (k+1)
```
%% Cell type:markdown id: tags:
## Graphen mit nicht-verbundenen Vertizes
... können modelliert werden, indem für $w(i, j)$ der Wert $\infty$ zugelassen wird.
%% Cell type:code id: tags:
``` haskell
import Data.Array.Repa as R
data Weight a = Weight a | Infinity deriving (Eq, Ord, Show)
```
%% Cell type:markdown id: tags:
type Weight = Int
type Graph r = Array r DIM2 Weight
`deriving Ord` funktioniert so, dass Datenkonstruktoren links vom `|` als kleiner betrachtet werden:
%% Cell type:code id: tags:
``` haskell
Weight 42 < Infinity
```
%%%% Output: display_data
%% Cell type:markdown id: tags:
Wir benötigen ausserdem eine `Num`-Instanz. Diese ist formal nur bedingt sinnvoll, praktisch aber recht einfach:
%% Cell type:code id: tags:
``` haskell
:ext BangPatterns
shortestPaths :: Graph U -> Graph U
shortestPaths g0 = go g0 0 -- 1
where
Z :. _ :. n = extent g0 -- 2
go !g !k | k == n = g -- 3
| otherwise =
let g' = computeS (fromFunction (Z:.n:.n) sp) -- 4
in go g' (k+1) -- 5
where
sp (Z:.i:.j) = min (g ! (Z:.i:.j)) (g ! (Z:.i:.k) + g ! (Z:.k:.j)) -- 6
-- Functor macht einige Definitionen unten einfacher
instance Functor Weight where
fmap f (Weight i) = Weight $ f i
fmap f Infinity = Infinity
instance Num a => Num (Weight a) where
Weight i + Weight j = Weight $ i+j
_ + _ = Infinity
Weight i * Weight j = Weight $ i*j
_ * _ = Infinity
fromInteger = Weight . fromInteger
negate = fmap negate
abs = fmap abs
signum = fmap signum
```
%% Cell type:markdown id: tags:
`(*)`, `abs` und `signum` werden nicht benötigt, sollte aber trotzdem deklariert werden. Eine andere Möglichkeit wäre, sie als `undefined` zu deklarieren.
`negate` ist mathematisch nicht ganz sauber, da
```haskell
negate Infinity == Infinity
```
reicht für unsere Zwecke aber aus.
Der Algorithmus funktioniert auch für gerichtete Kanten und sogar für negative Kanten-Gewichte, solange es keine *negativen Zyklen* gibt.
#### Beispiel (von Wikipedia)
![](https://upload.wikimedia.org/wikipedia/commons/thumb/2/2e/Floyd-Warshall_example.svg/1324px-Floyd-Warshall_example.svg.png)
%% Cell type:code id: tags:
``` haskell
shortestPaths $ fromListVector (ix2 4 4)
[ 0, Infinity, -2, Infinity
, 4, 0, 3, Infinity
, Infinity, Infinity, 0, 2
, Infinity, -1, Infinity, 0
]
```
%%%% Output: display_data
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment