Haskell で Int が完全な正方形であるかどうかを判断する方法は何ですか?
質問
簡単な機能が欲しい
is_square :: Int -> Bool
これは、Int N が完全な正方形であるかどうか (x*x = N となるような整数 x が存在するかどうか) を決定します。
もちろん、次のようなことを書くこともできます
is_square n = sq * sq == n
where sq = floor $ sqrt $ (fromIntegral n::Double)
しかし、それはひどいようです!おそらく、そのような述語を実装するための一般的な簡単な方法はあるでしょうか?
解決 5
ああ、今日は数が完璧立方体であるかどうかを判断するために必要な、と同様のソリューションが非常に遅かった。
だから、私はかなり賢い選択肢
を思い付いcubes = map (\x -> x*x*x) [1..]
is_cube n = n == (head $ dropWhile (<n) cubes)
非常にシンプル。私は、私はより速く検索にツリーを使用する必要があると思うが、今私は多分それは私の仕事のために十分に速くなり、このソリューションを試してみましょう。そうでない場合、私は適切なデータ構造との答えを編集します。
他のヒント
は正のint n
を持っている場合、あなたは基本的に1から番号の範囲にバイナリ検索をやって、それをこのように考えて... n個の第1の数のn'
を検索する場所をn' * n' = n
ます。
私はHaskellのを知らないが、このF#は、変換するのは簡単である必要があります:
let is_perfect_square n =
let rec binary_search low high =
let mid = (high + low) / 2
let midSquare = mid * mid
if low > high then false
elif n = midSquare then true
else if n < midSquare then binary_search low (mid - 1)
else binary_search (mid + 1) high
binary_search 1 n
O(Nログ)であることが保証されています。簡単に完全な立方体と高出力を変更します。
があります 素晴らしい Haskell のほとんどの整数論関連の問題に対応するライブラリ。 arithmoi
パッケージ。
使用 Math.NumberTheory.Powers.Squares
図書館。
具体的には、 isSquare'
関数。
is_square :: Int -> Bool
is_square = isSquare' . fromIntegral
このライブラリは、あなたや私よりもはるかに効率化に熱心な人々によって最適化され、十分に精査されています。現在はありませんが、 この種の悪ふざけ 内部で行われているため、ライブラリが進化して最適化されるにつれて、将来的にはそうなる可能性があります。 ソースコードを表示する それがどのように機能するかを理解するために!
車輪の再発明ではなく、利用可能な場合は常にライブラリを使用してください。
私はあなたが提供したコードは、あなたが得ようとしていることを最速だと思います:
is_square n = sq * sq == n
where sq = floor $ sqrt $ (fromIntegral n::Double)
は、このコードの複雑さがある:1 SQRT、1回のダブル乗算、1人のキャスト(dbl-> int型)、および1つの比較。あなたはSQRTとちょうど整数演算とシフトとの乗算を置き換えるために、他の計算方法を使用することを試みることができるが、チャンスは1 SQRTと1回の乗算よりも高速であることを行っていないです。
あなたが実行されているCPUは、浮動小数点演算をサポートしていない場合は、それは別の方法を使用して価値があるかもしれない唯一の場所です。この場合、コンパイラは、おそらくソフトウェアでSQRTとダブル乗算を生成する必要があります、あなたはあなたの特定のアプリケーション用に最適化する利点を得ることができます。
としては、他の回答で指摘され、大きな整数の制限が依然として存在しているが、あなたはこれらの数字に実行しようとしている場合を除き、それはあなた自身のアルゴリズムを書くよりも、浮動小数点ハードウェアサポートを利用する方が良いと考えられます。
のアルゴリズムをすることができていの整数広場のルーツにウィキペディアのの記事あなたのニーズに合うようになっています。ニュートン法いいです、それは二次関数的に収束するので、つまり、あなたが二回多くの正しい桁数として各ステップを取得します。
私は入力がないすべての整数が正確にDouble
として表すことができた後、2^53
よりも大きくなる可能性がある場合Double
から離れて滞在することをアドバイスします。
時々、あなたは(チェックしis_square
など)が小さすぎる部分に問題を分けるべきではありません。
intersectSorted [] _ = []
intersectSorted _ [] = []
intersectSorted xs (y:ys) | head xs > y = intersectSorted xs ys
intersectSorted (x:xs) ys | head ys > x = intersectSorted xs ys
intersectSorted (x:xs) (y:ys) | x == y = x : intersectSorted xs ys
squares = [x*x | x <- [ 1..]]
weird = [2*x+1 | x <- [ 1..]]
perfectSquareWeird = intersectSorted squares weird
完璧な正方形のためのテストに非常に単純な方法があります - 数の平方根は、それの小数部分にゼロ以外のものを持っている場合は文字通り、あなたがチェックし
。
func IsSquare(N) sq = sqrt(N) return (sq modulus 1.0) equals 0.0
この質問に対する別の回答へのコメントで、次のように議論しました メモ化. 。このテクニックは、プローブ パターンが良好な密度を示す場合に役立つことに留意してください。この場合、同じ整数を何度もテストすることになります。コードが同じ作業を繰り返す可能性があり、その結果、回答をキャッシュすることで利益が得られる可能性はどのくらいですか?
入力の分布についてのアイデアが提供されていないため、優れた 基準 パッケージ:
module Main
where
import Criterion.Main
import Random
is_square n = sq * sq == n
where sq = floor $ sqrt $ (fromIntegral n::Double)
is_square_mem =
let check n = sq * sq == n
where sq = floor $ sqrt $ (fromIntegral n :: Double)
in (map check [0..] !!)
main = do
g <- newStdGen
let rs = take 10000 $ randomRs (0,1000::Int) g
direct = map is_square
memo = map is_square_mem
defaultMain [ bench "direct" $ whnf direct rs
, bench "memo" $ whnf memo rs
]
このワークロードは、実行していることを適切に表している場合とそうでない場合がありますが、記述されているように、キャッシュ ミス率が高すぎるようです。
これは特に、かなりまたは高速ではないのですが、ここではニュートン法その任意の大きな整数の作品(ゆっくり)に基づいて、キャストのない、FPA-無料版です。
import Control.Applicative ((<*>))
import Control.Monad (join)
import Data.Ratio ((%))
isSquare = (==) =<< (^2) . floor . (join g <*> join f) . (%1)
where
f n x = (x + n / x) / 2
g n x y | abs (x - y) > 1 = g n y $ f n y
| otherwise = y
これは、おそらくいくつかの追加の数論の策略でスピードアップすることができます。