Ho giocato con la programmazione dinamica in Haskell. Praticamente ogni tutorial che ho visto sull'argomento fornisce lo stesso algoritmo molto elegante basato sulla memoizzazione e la pigrizia del tipo di matrice. Ispirato da questi esempi, ho scritto il seguente algoritmo come un test:Come si scrivono algoritmi di programmazione dinamica efficienti in Haskell?
-- pascal n returns the nth entry on the main diagonal of pascal's triangle
-- (mod a million for efficiency)
pascal :: Int -> Int
pascal n = p ! (n,n) where
p = listArray ((0,0),(n,n)) [f (i,j) | i <- [0 .. n], j <- [0 .. n]]
f :: (Int,Int) -> Int
f (_,0) = 1
f (0,_) = 1
f (i,j) = (p ! (i, j-1) + p ! (i-1, j)) `mod` 1000000
Il mio unico problema è l'efficienza. Anche usando GHC's -O2, questo programma impiega 1,6 secondi per calcolare pascal 1000
, che è circa 160 volte più lento di un equivalente programma C++ non ottimizzato. E il divario si allarga solo con input più grandi.
Sembra che abbia provato ogni possibile permutazione del codice precedente, insieme a alternative suggerite come la libreria di dati-memocombinatori, e tutte hanno avuto le stesse o peggiori prestazioni. L'unica cosa che non ho provato è la ST Monad, che sono sicuro che potrebbe essere fatta per eseguire il programma solo più lentamente più lento della versione C. Ma mi piacerebbe davvero scriverlo nell'idiomatico Haskell, e non capisco perché la versione idiomatica sia così inefficiente. Ho due domande:
Perché il codice di cui sopra è così inefficiente? Sembra una semplice iterazione attraverso una matrice, con un'operazione aritmetica ad ogni voce. Chiaramente Haskell sta facendo qualcosa dietro le quinte che non capisco.
C'è un modo per renderlo molto più efficiente (al massimo 10-15 volte il tempo di esecuzione di un programma C) senza sacrificare la sua formulazione ricorsiva senza stato (di fronte a un'implementazione che utilizza matrici mutevoli nella ST Monade)?
Grazie mille.
Edit: Il modulo matrice utilizzata è lo standard Data.Array
uso 'rem' invece di' mod' – is7s
Quale modulo serie stai usando? – is7s
Come si confronta il rendimento se si usa semplicemente "f (i, j) = (f (i, j-1) + f (i-1, j))" e si interrompe completamente? Non capisco come sia utile passare attraverso p, anche se ammetto di non avere molta esperienza con Haskell. – DGH