2016-11-01 5 views
6

최단 경로 알고리즘을 코딩하려고하면 이상한 점이 있습니다. floydWarshall 함수가 배열 형식의 인접 행렬을 생성 한 후 main 함수는 배열을 여러 번 쿼리하려고 시도합니다 (replicateM_ 루프).왜 함수의 결과가 재사용되지 않습니까?

내가 발견 한 것은 코드가 너무 느리다는 것입니다. 그래서 floydWarshall의 상단에 traceShow "doing"을 넣고 다시 실행하여 각각 res ! (start,end)floydWarshall을 반복적으로 호출하는 것을 찾습니다.

매번 왜 배열이 다시 생성됩니까? 샘플 입력이

전체 소스 : 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 

샘플 실행 :

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

혹시 온라인 어딘가에 전체 코드가 있습니까? 최적으로, 코어 출력을보고 무슨 일이 일어나고 있는지보고 싶지만 컴파일하는 경우에만 가능합니다. 또한 컴파일 할 때 사용하는 플래그는 무엇입니까? – Alec

+1

Floyd-Warshall은 고전적인 동적 프로그래밍 알고리즘입니다. 이 게시물에서 _Haskell_을 사용하여 이러한 문제를 해결하는 더 나은 방법을 찾을 수 있습니다. http://jelv.is/blog/Lazy-Dynamic-Programming/ – Shersh

+0

전체 코드 및 샘플 입력 : https : //gist.github를 업로드했습니다. com/cwyang/27ab81bee731e6d01bb3a7483fdb748e –

답변

4

실망스럽게도, 이것은 GHC 문제 "Costly let binding gets duplicated in IO action value"에 의한 것으로 보인다.

replicateM_ 대신 forM_을 사용하거나 BangPatterns을 사용하면이 문제가 해결됩니다.

+1

위의 @ Shersh의 의견은 꽤 좋아 보입니다. "이 게시물에서 하스켈을 사용하여 이러한 문제를 해결하는 더 좋은 방법을 찾을 수 있습니다 : jelv.is/blog/Lazy-Dynamic-Programming "일반적으로 IO Monad에서 가능한 한 적게해야합니다 ... Haskell을 프로그래밍 할 때 생각을 조금 바꿔야하지만 시간이 지나면 점점 더 좋아하게 될 것입니다.) – mb21

+0

아, 와우, 그것은 불쾌한 잡았다. 최소한 내가 알지 못했던 것 중 하나이며, 하스켈을 전문적으로 해킹하는 몇 년을 포함하여 거의 10 년 동안 새로운 프로젝트를위한 언어로 하스켈을 사용 해왔다. 아마도 그것은이 문제가 매우 자주 물지 않는다는 작은 증거입니다. –

+1

@ mb21 여러분의 의견에 동의하지만, OP는'IO' 모나드 내부에서 작업하지 않고'ST' 모나드 내부에서 작동합니다. (버그는 실제로 알고리즘에 있지만 코드를 호출하는 코드에는 없습니다. 그것). 또한,이 특별한 알고리즘은 많은 업데이트가 필요하기 때문에'ST'는 꽤 좋은 선택 인 것 같습니다. – Alec

관련 문제