2016-11-01 3 views
6

Wenn ich versuche, einen Algorithmus für den kürzesten Pfad zu programmieren, stoße ich auf eine seltsame Sache. Nach floydWarshall Funktion generiert Adjecency Matrix in Array-Form, main Funktion versucht, das Array mehrmals (in replicateM_ Schleife) abzufragen.Warum wird das Ergebnis der Funktion nicht wiederverwendet?

Was ich fand, ist, dass mein Code schrecklich langsam ist. Also setze ich traceShow "doing" an die Spitze von floydWarshall und wiederhole, dass jeder res ! (start,end) wiederholt floydWarshall aufruft.

Warum wird das Array jedes Mal neu generiert?

Volle Quelle mit Abtastwerteingang: 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 

Probelauf:

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

Haben Sie irgendwo den vollen Code irgendwo online? Im Optimalfall sollten Sie sich die Kernausgabe ansehen, um zu sehen, was passiert, aber das ist nur möglich, wenn etwas kompiliert wird. Welche Flags verwenden Sie beim Kompilieren? – Alec

+1

Floyd-Warshall ist ein klassischer dynamischer Programmieralgorithmus. Sie können einen besseren Ansatz finden, um solche Probleme mit _Haskell_ in diesem Beitrag zu lösen: http://jelv.is/blog/Lazy-Dynamic-Programming/ – Shersh

+0

Ich habe den vollständigen Code hochgeladen und Beispieleingabe: https: //gist.github. com/cwyang/27ab81bee731e6d01bb3a7483fdb748e –

Antwort

4

Frustrierend, scheint dies "Costly let binding gets duplicated in IO action value" durch das GHC Problem verursacht werden.

Verwenden Sie forM_ statt replicateM_ oder BangPatterns verwenden, löst dieses Problem.

+1

@ Shershs Kommentar oben scheint ziemlich gut zu sein:" Sie können einen besseren Ansatz finden, um solche Probleme mit Haskell in diesem Post zu lösen: jelv.is/blog/Lazy-Dynamic-Programmierung "Generell sollten Sie so wenig wie möglich in der IO-Monade machen ... Sie müssen Ihr Denken ein wenig ändern, wenn Sie Haskell programmieren, aber mit der Zeit werden Sie es mehr und mehr genießen:) – mb21

+0

Oh, wow, das ist eine böse gotcha. Es ist einer, von dem ich zumindest nichts wusste, und ich benutze seit fast einem Jahrzehnt Haskell als meine Sprache für neue Projekte, einschließlich mehrjähriger Haskell Projekte professionell zu hacken. Vielleicht ist das ein kleiner Beweis dafür, dass dieses Problem nicht sehr oft beißt. –

+1

@ mb21 Während ich mit dem Geist Ihres Kommentars einverstanden bin, ist das OP nicht seine Arbeit innerhalb der IO-Monade, sondern innerhalb der ST-Monade (der Fehler ist eigentlich nicht einmal im Algorithmus, sondern im Code, der anruft es). Da dieser spezielle Algorithmus eine Menge Aktualisierungen erfordert, scheint 'ST' eine ziemlich gute Wahl zu sein. – Alec

Verwandte Themen