劣線形時間で素数を合計するこのHaskellコードを最適化する方法は?

Adam Stelmaszczyk

プロジェクトオイラーの問題10は、与えられたnの下にあるすべての素数の合計を見つけることです。

エラトステネスのふるいによって生成された素数を合計するだけで解決しました。次に、Lucy_Hedgehog(劣線形!)によるはるかに効率的なソリューションに出会いました

以下のためのn = ^ 92⋅10

  • Pythonコード(上記の引用から)は、Python2.7.3では1.2秒で実行されます。

  • C ++コード(私のもの)は約0.3秒で実行されます(g ++ 4.8.4でコンパイル)。

私はそれを学んでいるので、Haskellで同じアルゴリズムを再実装しました:

import Data.List

import Data.Map (Map, (!))
import qualified Data.Map as Map

problem10 :: Integer -> Integer
problem10 n = (sieve (Map.fromList [(i, i * (i + 1) `div` 2 - 1) | i <- vs]) 2 r vs) ! n
              where vs = [n `div` i | i <- [1..r]] ++ reverse [1..n `div` r - 1]
                    r  = floor (sqrt (fromIntegral n))

sieve :: Map Integer Integer -> Integer -> Integer -> [Integer] -> Map Integer Integer
sieve m p r vs | p > r     = m
               | otherwise = sieve (if m ! p > m ! (p - 1) then update m vs p else m) (p + 1) r vs

update :: Map Integer Integer -> [Integer] -> Integer -> Map Integer Integer
update m vs p = foldl' decrease m (map (\v -> (v, sumOfSieved m v p)) (takeWhile (>= p*p) vs))

decrease :: Map Integer Integer -> (Integer, Integer) -> Map Integer Integer
decrease m (k, v) = Map.insertWith (flip (-)) k v m

sumOfSieved :: Map Integer Integer -> Integer -> Integer -> Integer
sumOfSieved m v p = p * (m ! (v `div` p) - m ! (p - 1))

main = print $ problem10 $ 2*10^9

でコンパイルしてghc -O2 10.hs実行しましたtime ./10

正解ですが、約7秒かかります。

でコンパイルしてghc -prof -fprof-auto -rtsopts 10実行しました./10 +RTS -p -h

10.profは、decrease52.2%の時間と67.5%の割り当てが必要であること示しています。

実行した後、hp2ps 10.hp私はそのようなヒーププロファイルを取得しました:

hp

ここでもdecrease、ヒープの大部分を占めるように見えます。GHCバージョン7.6.3。

このHaskellコードの実行時間をどのように最適化しますか?


アップデート13.06.17:

私が試した不変の交換Data.Map可変とData.HashTable.IO.BasicHashTableからhashtables小さなためにあるため、パッケージが、私はおそらく何かの悪いやってるのn = 30、それはすでに10秒程度、時間がかかりすぎます。どうしましたか?

アップデート18.06.17:

HashTableのパフォーマンスの問題に興味があるのは良い読み物です。ミュータブルを使用してSherhのコード取得しましData.HashTable.ST.LinearData.Judy、代わりに立ち寄りました1.1秒で実行されますが、それでも比較的低速です。

シャーシュ

私はいくつかの小さな改善を行ったので3.4-3.5、私のマシンでは数秒で実行されます。使用IntMap.Strictすることは大いに役立ちました。それ以外は、ghc念のために手動でいくつかの最適化を実行しました。そして、HaskellコードをリンクからのPythonコードにもっと近づけます。次のステップとして、いくつかの可変を使用してみることができますHashMapしかし、私にはわかりません...IntMap不変のコンテナであるため、いくつかの可変コンテナよりもはるかに高速になることはできません。私はまだそれの効率に驚いていますが。これをもっと早く実装できるといいのですが。

コードは次のとおりです。

import Data.List (foldl')
import Data.IntMap.Strict (IntMap, (!))
import qualified Data.IntMap.Strict as IntMap

p :: Int -> Int
p n = (sieve (IntMap.fromList [(i, i * (i + 1) `div` 2 - 1) | i <- vs]) 2 r vs) ! n
               where vs = [n `div` i | i <- [1..r]] ++ [n', n' - 1 .. 1]
                     r  = floor (sqrt (fromIntegral n) :: Double)
                     n' = n `div` r - 1

sieve :: IntMap Int -> Int -> Int -> [Int] -> IntMap Int
sieve m' p' r vs = go m' p'
  where
    go m p | p > r               = m
           | m ! p > m ! (p - 1) = go (update m vs p) (p + 1)
           | otherwise           = go m (p + 1)

update :: IntMap Int -> [Int] -> Int -> IntMap Int
update s vs p = foldl' decrease s (takeWhile (>= p2) vs)
  where
    sp = s ! (p - 1)
    p2 = p * p
    sumOfSieved v = p * (s ! (v `div` p) - sp)
    decrease m  v = IntMap.adjust (subtract $ sumOfSieved v) v m

main :: IO ()
main = print $ p $ 2*10^(9 :: Int) 

更新:

ミュータブルhashtables使用~5.5secて、この実装でHaskellのパフォーマンスを実現することができました

また、いくつかの場所でリストの代わりにボックス化されていないベクトルを使用しました。Linearハッシュが最速のようです。これはもっと速くできると思います。パッケージsse42オプションがあることに気づきましhasthables正しく設定できたかどうかはわかりませんが、それがなくてもそれほど高速に実行されます。

更新2(19.06.2017)

3xジュディハッシュマップを完全に削除することで、@ Kromからの(私のコードと彼のマップを使用した)より高速で最良のソリューションを実現することができました代わりに、単純な配列のみが使用されます。あなたがのためのキーことに気づく場合は、同じ考えを思い付くことができSハッシュマップからいずれかの配列されている1n'n div iのためにiから1rしたがって、このようなHashMapを、検索キーに応じて配列内でルックアップを行う2つの配列として表すことができます。

私のコード+ Judy HashMap

$ time ./judy
95673602693282040

real    0m0.590s
user    0m0.588s
sys     0m0.000s

私のコード+私のまばらなマップ

$ time ./sparse
95673602693282040

real    0m0.203s
user    0m0.196s
sys     0m0.004s

これは、IOUArrayすでに生成されたベクトルとVectorライブラリの代わりに使用され、readArrayに置き換えられた場合、さらに高速に実行できますunsafeReadしかし、これを可能な限り最適化することに本当に興味がないのであれば、これを行うべきではないと思います。

このソリューションとの比較は不正行為であり、公平ではありません。PythonとC ++で実装された同じアイデアがさらに高速になることを期待しています。しかし、クローズドハッシュマップを使用した@Kromソリューションは、標準のデータ構造ではなくカスタムデータ構造を使用しているため、すでに不正行為を行っています。少なくとも、Haskellの標準的で最も人気のあるハッシュマップはそれほど高速ではないことがわかりますこのような問題には、より優れたアルゴリズムとより優れたアドホックデータ構造を使用する方がよい場合があります。

結果のコードは次のとおりです。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

Railsでこのコードを最適化する方法は?

分類Dev

このコードをjavascriptで最適化する方法は?

分類Dev

このasp.netコードを将来数年間最適化する方法は?

分類Dev

このPHPコードを最適化する方法は?

分類Dev

この冗長なコードを最適化する方法は?

分類Dev

Pythonでこのコードを最適化する方法

分類Dev

Pythonで非線形方程式を最適化する方法は?

分類Dev

Haskellで線形時間BFSを実装することは可能ですか?

分類Dev

このコードの時間計算量を計算する方法は?

分類Dev

この2行のbashコードを最適化する方法

分類Dev

この非線形モデルの直線を作成するscipyでカーブフィットを最適化するのはなぜですか?

分類Dev

スパースデータのパーティション(クラスター)を線形時間で、そして(うまくいけば)劣線形空間で検出する方法は?

分類Dev

この文字列置換コードを最適化する方法

分類Dev

このQtコードを最適化する方法(QByteArray変換)?

分類Dev

Clangがこのコードを最適化するのはなぜですか?

分類Dev

このコードのNumpy最適化を実行する方法は?

分類Dev

線形時間でint配列をソートする方法は?

分類Dev

このコードの時間計算量を分析する方法は?

分類Dev

Javaでこの方法を最適化するには?私は制限時間が超過取得しています

分類Dev

線形最適化でバイナリ制約を処理する方法は?

分類Dev

時間計算量の観点からcppで行列乗算を最適化する方法は?

分類Dev

次のforループコードを最適化する方法は?

分類Dev

PythonでMAPEコードを最適化する方法は?

分類Dev

Kotlinでコードを最適化する方法は?

分類Dev

PHPでこのmysqlコードを最適化する方法はありますか?

分類Dev

次のPerlコードを最適化する方法は?

分類Dev

コードの最適化:scipy最小化を使用してコードを最適化する方法は?

分類Dev

このforループの計算速度を最適化する方法は?

分類Dev

条件の組み合わせを調べるコードを単純化/最適化するための最良の方法は何ですか?

Related 関連記事

  1. 1

    Railsでこのコードを最適化する方法は?

  2. 2

    このコードをjavascriptで最適化する方法は?

  3. 3

    このasp.netコードを将来数年間最適化する方法は?

  4. 4

    このPHPコードを最適化する方法は?

  5. 5

    この冗長なコードを最適化する方法は?

  6. 6

    Pythonでこのコードを最適化する方法

  7. 7

    Pythonで非線形方程式を最適化する方法は?

  8. 8

    Haskellで線形時間BFSを実装することは可能ですか?

  9. 9

    このコードの時間計算量を計算する方法は?

  10. 10

    この2行のbashコードを最適化する方法

  11. 11

    この非線形モデルの直線を作成するscipyでカーブフィットを最適化するのはなぜですか?

  12. 12

    スパースデータのパーティション(クラスター)を線形時間で、そして(うまくいけば)劣線形空間で検出する方法は?

  13. 13

    この文字列置換コードを最適化する方法

  14. 14

    このQtコードを最適化する方法(QByteArray変換)?

  15. 15

    Clangがこのコードを最適化するのはなぜですか?

  16. 16

    このコードのNumpy最適化を実行する方法は?

  17. 17

    線形時間でint配列をソートする方法は?

  18. 18

    このコードの時間計算量を分析する方法は?

  19. 19

    Javaでこの方法を最適化するには?私は制限時間が超過取得しています

  20. 20

    線形最適化でバイナリ制約を処理する方法は?

  21. 21

    時間計算量の観点からcppで行列乗算を最適化する方法は?

  22. 22

    次のforループコードを最適化する方法は?

  23. 23

    PythonでMAPEコードを最適化する方法は?

  24. 24

    Kotlinでコードを最適化する方法は?

  25. 25

    PHPでこのmysqlコードを最適化する方法はありますか?

  26. 26

    次のPerlコードを最適化する方法は?

  27. 27

    コードの最適化:scipy最小化を使用してコードを最適化する方法は?

  28. 28

    このforループの計算速度を最適化する方法は?

  29. 29

    条件の組み合わせを調べるコードを単純化/最適化するための最良の方法は何ですか?

ホットタグ

アーカイブ