2016-11-01 7 views
6

Kiedy próbuję zakodować algorytm najkrótszej ścieżki, natknąłem się na dziwną rzecz. Po funkcji floydWarshall generuje macierz zależności w formie tablicowej, funkcja main próbuje wielokrotnie zapytać tablicę (w pętli replicateM_).Dlaczego wynik funkcji nie jest ponownie używany?

Okazało się, że mój kod jest strasznie powolny. Tak więc ustawiam traceShow "doing" na górze floydWarshall i ponownie uruchamiam, aby znaleźć, że każdy z nich res ! (start,end) dzwoni wielokrotnie.

Dlaczego tablica generuje ponownie za każdym razem?

Pełna źródło z wejścia próbki: https://gist.github.com/cwyang/27ab81bee731e6d01bb3a7483fdb748e

floydWarshall :: AdjMatrix (Maybe Int) -> AdjMatrix (Maybe Int) 
floydWarshall am = traceShow "doing" $ runST $ do 
    arr <- thaw am :: ST s (STArray s (Vertex,Vertex) (Maybe Int)) 
    sequence_ [ go arr k i j | k <- r, i <- r, j <- r] 
    freeze arr 
    where ((minb,_), (maxb,_)) = bounds am 
     r = [minb..maxb] 
     go :: STArray s (Vertex,Vertex) (Maybe Int) 
      -> Vertex -> Vertex -> Vertex -> ST s() 
     go arr k i j = do 
      ij <- readArray arr (i,j) 
      ik <- readArray arr (i,k) 
      kj <- readArray arr (k,j) 
      case (ik, kj) of 
      (Nothing, _) -> return() 
      (_, Nothing) -> return() 
      (Just a, Just b) -> case ij of 
       Nothing -> do 
       writeArray arr (i,j) $ Just (a+b) 
       (Just c) -> when (c > a+b) $ do 
       writeArray arr (i,j) $ Just (a+b) 
readInt :: B.ByteString -> Int 
readInt = fst . fromJust . B.readInt 

main :: IO() 
main = do 
    [n,m] <- rl 
    edges <- replicateM m $ do 
    [from,to,weight] <- rl 
    return (from,to,weight) 
    [q] <- rl 
    let am = buildAdjMatrix (1,n) edges 
     res= floydWarshall am 
    replicateM_ q $ do 
    [start,end] <- rl 
    putStrLn . show $ maybe (-1) id (res ! (start,end)) 
    where rl = map readInt . B.words <$> B.getLine 

run Próbka:

$ graph < floyd3.txt hs 
"doing"  <-- floydWarshall keeps calling 
1395 
"doing" 
975 
"doing" 
1593 
"doing" 
1023 
"doing" 
1521 
... 
+0

Masz szansę, że masz pełny kod gdzieś online? Optymalnie, chciałbyś spojrzeć na wyjściowy rdzeń, aby zobaczyć, co się dzieje, ale jest to możliwe tylko dla czegoś, co się kompiluje. Ponadto, jakie flagi używasz podczas kompilacji? – Alec

+1

Floyd-Warshall jest klasycznym algorytmem programowania dynamicznego. Możesz znaleźć lepsze podejście do rozwiązywania takich problemów za pomocą _Haskell_ w tym poście: http://jelv.is/blog/Lazy-Dynamic-Programming/ – Shersh

+0

Mam załadowany pełny kod i przykładowe dane wejściowe: https: //gist.github. com/cwyang/27ab81bee731e6d01bb3a7483fdb748e –

Odpowiedz

4

frustrująca, to wydaje się być spowodowane przez GHC emisji "Costly let binding gets duplicated in IO action value".

Używanie forM_ zamiast replicateM_ lub użycie BangPatterns rozwiązuje ten problem.

+1

@ Powyższy komentarz Shersha wydaje się całkiem dobry:" Możesz znaleźć lepsze podejście do rozwiązywania takich problemów za pomocą Haskella w tym wpisie: jelv.is/blog/Lazy-Dynamic-Programming "Generalnie powinieneś robić tak mało, jak to tylko możliwe, podczas gdy w Monadzie IO ... będziesz musiał zmienić nieco swoje myślenie podczas programowania Haskella, ale z czasem będziesz się nim cieszył coraz bardziej:) – mb21

+0

Och, wow, to jest paskudny grosz. Tego przynajmniej ja nie znałem i od ponad dekady używam Haskella jako mojego języka do nowych projektów, w tym kilku lat profesjonalnie hakującego projekty Haskell. Być może jest to niewielki dowód na to, że ten problem nie gryzie bardzo często. –

+1

@ mb21 Chociaż zgadzam się z duchem twojego komentarza, OP nie wykonuje swojej pracy wewnątrz monada 'IO', ale wewnątrz monady' ST' (błąd nie jest nawet w algorytmie, ale w kodzie, który wywołuje to). Ponadto, ponieważ ten konkretny algorytm wymaga wielu aktualizacji 'ST' wydaje się całkiem dobrym wyborem. – Alec