2016-10-13 2 views
0

ハスケルを使った関数型プログラミングが初めてで、繰り返しとリストの理解の間にパフォーマンスに違いがあるのだろうかと思っていました。現在、私はに収まる2の最も高いパワーを得るためにlast (takeWhile (< n) (iterate (2*) 1))を使用しています。私はこれを可能な限り最適化したいと思います。より良い方法がありますか? lastがなければ、nより低い2の累乗のリストを返します。 lastでは、最大値を返します。イテレータのリスト理解のパフォーマンス

例:nに117を入力すると、出力は64になり、リストは[1, 2, 4, 8, 16, 32, 64]になります。

+4

注意してください:リストの理解は魔法ではありません。 '[f x | x < - xs、px] 'は' map'、 'concatMap'、' filter'のように理解されます(この場合は基本的に 'map f(filter p xs)'です)。さらに、 '[a .. b]'は 'enumFromTo a b '(' enumFromTo'は 'Enum'型クラスの一部です)と言ってもいいです。 –

+0

@Rhymoidありがとう、それは本当にいい簡潔な説明です。 –

+1

@Rhymoid実際には、リスト内包は、最適化が有効になっているときに 'foldr'と' build'フォームに変換され、そうでなければあらかじめ最適化された再帰フォームに変換されます。 'map'、' concatMap'、 'filter'は生成されません。 – dfeuer

答えて

1

最適化すれば、間違った方向に進むでしょう。一般に、アルゴリズムを最適化する方が常に良い方法です。 (警告、未テスト)

このための基本的なアルゴリズムは、2 ^(床(LOG2(N)))

理由ではありません。

myFunc :: Int -> [Int] 
myFunc n = map (2^) list 
    where list = [1..x] 
      x = floor $ logBase 2 (fromIntegral n) 

またはあなただけの2の最高出力をしたい場合数

myFunc2 :: Int -> Int 
myFunc2 n = 2^(floor $ logBase 2 (fromIntegral n)) 
+0

素早くお返事ありがとうございます!あなたのコードをテストしたのか、頭の上から入力したのかわからないけど、コンパイルできないので、それが何であるかを完全には分かっていません。 –

+0

それは恐れていない。 fromIntegral関数はIntをNum型に変換します。 logBase 2.0は数値のlog2を見つけ、toIntegerはそれをintにキャストします。これはlog2の累乗で2を切り捨てて結果を返します。すべての機能はタイプについてHoogleでチェックできます。 – OllieB

+0

現時点ではコンパイルできません。その可能性は些細なことでしょう。 – OllieB

0

くらい単純な答えは、直接再帰を使用することです。 Prelude.untilイディオムは、あなたの最終結果に一定の空間末尾再帰の反復を生み出す

foo 1 = 1 
foo x = 2 * foo (div x 2) 
+0

多分簡単かもしれませんが、大きなxでは明らかに速くはありません。 – OllieB

+0

これは基本的に 'iterate'と' map'に基づくアプローチと同じアルゴリズムです。私は同時に、2のすべての小さな力の明示的なリストを作成していません。 1000桁の数字の場合、ほぼ即座に戻ります。 (与えられた、ハードコードされた1000桁の数字でテストしたので、 'ghc -O2'は答えを得るためにループを完全に展開したように見えます...) – chepner

+0

合意。私が与える例では、myFunc2は線形時間ではなく定数でこれを行います(logbase 2が一定時間であると仮定します) – OllieB

0
largestPowerOfTwoIn n = until ((>n).(*2)) (*2) 1