2

STモナドを使って学びたい。したがって、すべての整数について計算するコードを少し書き直して、限界まで、すべての適切な約数のリストを作成したいと思います。結果は配列である必要があり、インデックス 'n' のエントリは適切な除数のリストである必要があります。

これは、整数 'n' ごとにその倍数のリスト 'l' を計算し、インデックス 'm' で 'l' から倍数 'm' ごとにリストに除数 'n' を追加することによって行われます。

変更したいコードは次のとおりです。

properDivisorsOf' :: forall a. (Integral a, Ix a) => a -> Array a [a]
properDivisorsOf' limit =
  let generate :: (Integral a, Ix a) => a -> Array a [a] -> Array a [a]
      generate n acc
        | n > (limit `div` 2) = acc
        | otherwise           =
              let acc' = acc // [(i, n : (acc ! i)) | i <- [2*n, 3*n .. limit]]
              in  generate (n + 1) acc'

  in generate 1 (array (1, limit) [(i, [])| i <- [1..limit]])

そして、それが私がそれを試す方法です:

properDivisorsOf :: forall a. (Integral a, Ix a) => a -> Array a [a]
properDivisorsOf limit =
  let result :: ST s (STArray s a [a])
      result = newArray (1, limit) [] -- In the beginning for every number: no divisors known (empty list) 

      update (index, divisor) = do
        l <- readArray result index -- extracting list of divisors of number 'index'
        let u = divisor : l
        writeArray result index u   -- and adding 'divisor' to the list

      content :: [(a, a)]
      content = do
        n <- [1 .. (limit `div` 2)]
        multiple <- [2*n, 3*n .. limit]
        return (multiple, n)

      doUpdate = map update content -- update result for all multiples (in content)

runSTArray の結果

残念ながら、それはコンパイルされておらず、エラーメッセージは私に何も言いません。2 つの質問があります。

  1. なぜコンパイルされないのですか?エントリを正しく抽出するにはどうすればよいですか?
  2. 経験豊富な Haskell-Programm は、ST-Monad を使用しなければならないという制限の下で、この問題をどのように解決するでしょうか (効率化のため)。

編集: コンパイラ メッセージ

    Couldn't match expected type `[t0]'
            with actual type `STArray i0 a [a]'
    In the second argument of `(:)', namely `l'
    In the expression: divisor : l
    In an equation for `u': u = divisor : l

失敗しました。モジュールがロードされました: なし。

4

1 に答える 1

5

解決

2. 経験豊富な Haskell-Programm は、ST-Monad を (効率化のために) 使用しなければならないという制限の下で、この問題をどのように解決しますか?

私は決して経験豊富な Haskell プログラマーではありませんが、の命令型コードを使用しますが、コードからの直接の移行は次のとおりです。

properDivisorsOf :: forall a. (Integral a, Ix a) => a -> Array a [a]
properDivisorsOf limit =
  runSTArray $ do
    result <- newArray (1, limit) []
    mapM_ (update result) content
    return result
  where
      content :: [(a, a)]
      content = do
        n <- [1 .. (limit `div` 2)]
        multiple <- [2*n, 3*n .. limit]
        return (multiple, n)

      update arr (index, divisor) = do
        l <- readArray arr index -- extracting list of divisors of number 'index'
        let u = divisor : l
        writeArray arr index u   -- and adding 'divisor' to the list

非 ST と ST の比較:

非 ST バリアント

あなたの元の機能:

main = print $ properDivisorsOf' 100000
$ .\Original.exe +RTS -s
Original.exe: メモリ不足

交換100000方法10000

     ヒープに割り当てられた 221,476,488 バイト
      GC 中にコピーされた 21,566,328 バイト
     172,813,068 バイトの最大常駐 (9 サンプル)
       4,434,480 バイトの最大スロップ
             合計 210 MB の使用メモリ (断片化により 5 MB が失われました)

                                    合計時間 (経過) 平均一時停止 最大一時停止
  Gen 0 378 列、0 パー 0.41 秒 0.43 秒 0.0011 秒 0.0024 秒
  Gen 1 9 列、0 パー 0.36 秒 0.37 秒 0.0409 秒 0.1723 秒

  INIT 時間 0.00s (0.00s 経過)
  MUT 時間 0.28 秒 (0.60 秒経過)
  GC 時間 0.77 秒 (0.80 秒経過)
  EXIT時間 0.00s (0.02s経過)
  合計時間 1.05 秒 (1.42 秒経過)

  %GC 時間 73.1% (56.4% 経過)

  割り当てレート 787,471,957 バイト/MUT 秒

  生産性 総ユーザーの 26.9%、総経過時間の 19.8%

プログラムは非常に高速ですが (結局のところ 1 秒)、210MB のメモリ使用量は非常に顕著です。また、必要なメモリは 20000 の制限では約 800 MB、20000 では約 1.9GB の 2 乗になります。

STバリアント

$ .\ST.exe +RTS -s > $null
     ヒープに割り当てられた 300,728,400 バイト
      GC 中に 89,696,288 バイトがコピーされました
      13,628,272 バイトの最大常駐 (10 サンプル)
         292,972 バイトの最大スロップ
              合計 38 MB の使用メモリ (断片化により 0 MB が失われる)

                                    合計時間 (経過) 平均一時停止 最大一時停止
  Gen 0 565 colls、0 par 0.14s 0.14s 0.0002s 0.0008s
  Gen 1 10 列、0 パー 0.09 秒 0.08 秒 0.0076 秒 0.0223 秒

  INIT 時間 0.00s (0.00s 経過)
  MUT 時間 0.11 秒 (0.16 秒経過)
  GC 時間 0.23 秒 (0.21 秒経過)
  EXIT時間 0.00s ( 0.00s経過)
  合計時間 0.34 秒 ( 0.38 秒経過)

  %GC 時間 68.2% (56.6% 経過)

  割り当て率 2,749,516,800 バイト/MUT 秒

  生産性 総ユーザーの 31.8%、総経過時間の 28.9%

元の実装の約 17% である 38 MB のみですが、10 倍の値 (10000 対 100000) を計算します。

命令型バリアント

捨てるcontentと、次のプログラムになります。

import Data.Array (Array(..), Ix)
import Data.Array.ST (newArray, readArray, writeArray, runSTArray)
import Control.Monad (forM_)

properDivisorsOf :: (Integral a, Ix a) => a -> Array a [a]
properDivisorsOf limit =
  runSTArray $ do
    result <- newArray (1, limit) []
    forM_ [1.. (limit `div`2)] $ \n -> do
      forM_ [2*n, 3*n .. limit] $ \index -> do
        l <- readArray result index
        writeArray result index (n:l)
    return result

main = print $ properDivisorsOf 100000
$ .\Imperative.exe +RTS -s > $null
     ヒープに割り当てられた 237,323,392 バイト
      GC 中にコピーされた 63,304,856 バイト
      13,628,276 バイトの最大常駐 (7 サンプル)
         138,636 バイトの最大スロップ
              合計 34 MB の使用メモリ (断片化により 0 MB が失われる)

                                    合計時間 (経過) 平均一時停止 最大一時停止
  Gen 0 447 colls、0 par 0.12s 0.09s 0.0002s 0.0008s
  Gen 1 7 列、0 パー 0.05 秒 0.06 秒 0.0087 秒 0.0224 秒

  INIT 時間 0.00s (0.00s 経過)
  MUT 時間 0.11 秒 (0.18 秒経過)
  GC 時間 0.17 秒 (0.16 秒経過)
  EXIT時間 0.00s ( 0.00s経過)
  合計時間 0.30 秒 (経過時間 0.34 秒)

  %GC 時間 57.9% (45.9% 経過)

  割り当てレート 2,169,813,869 バイト/MUT 秒

  生産性 総ユーザーの 42.1%、総経過時間の 36.9%

なぜコンパイルされないのですか?

  1. なぜコンパイルされないのですか?エントリを正しく抽出するにはどうすればよいですか?

正しく抽出されていると思いますが (上記を参照してください。これは、使用したコードとほぼ同じです)、update使用法が正しくないため、推論された型が間違っています。たとえばupdateSTモナドで動作するはずなので、mapMではなくで使用しますmap。また、他にもいくつかのオフがあります。たとえば、定義doUpdateしても使用しないなどです。

于 2014-02-14T14:34:02.473 に答える