2010-05-11 104 views
13

我需要一個簡單的函數如何確定Int是否是Haskell中的完美正方形?

is_square :: Int -> Bool 

,其確定一個Int N A完美的正方形(是否有一個整數x,使得x * X = N)。

當然我可以只寫類似

is_square n = sq * sq == n 
    where sq = floor $ sqrt $ (fromIntegral n::Double) 

,但它看起來很可怕!也許有一個簡單的方法來實現這樣的謂詞?

+2

[確定整數的平方根是否爲整數的最快方法](http:// stackoverflow。com/questions/295579 /最快的方式來確定如果一個整數平方根是一個整數) – finnw 2010-07-23 14:02:27

回答

1

哦,今天我需要確定一個數字是否是完美的立方體,而類似的解決方案非常慢。

所以,我想出了一個非常聰明的替代

cubes = map (\x -> x*x*x) [1..] 
is_cube n = n == (head $ dropWhile (<n) cubes) 

很簡單。我認爲,我需要使用樹來進行更快速的查找,但現在我會嘗試這種解決方案,也許它對我的任務來說足夠快。如果沒有,我會適當的數據結構

+0

我不知道這是否會比原來的更快。它需要更多的指令來執行。這可能會更快,具體取決於Haskell如何做'head'/'dropWhile'以及您的數字有多大。 – earlNameless 2010-05-13 17:35:07

+0

它在實踐中速度更快。 – 2010-05-13 17:42:12

+0

使用不同的數據結構('Seq','Vector')和一個二進制搜索將可能是更快的秩序 – 2015-08-01 14:40:51

1

維基百科的article on Integer Square Roots算法可以適應您的需要。牛頓的方法很好,因爲它以二次方式收斂,即每一步你得到兩倍的正確數字。

如果輸入可能大於2^53,那麼我建議您遠離Double,在這之後不是所有整數都可以精確地表示爲Double

+1

我不需要非常複雜的algorythm,我只是認爲有一個簡單而美麗的解決方案沒有兩種類型轉換:) – 2010-05-11 03:32:37

+0

@valya:編寫'isqrt'函數可以消除類型轉換。 – 2010-05-11 20:55:45

5

我想您所提供的代碼是最快的,你會得到:

is_square n = sq * sq == n 
    where sq = floor $ sqrt $ (fromIntegral n::Double) 

的這段代碼的複雜性:一個平方根,一個雙乘法,一個投(dbl-> INT)和一個比較。您可以嘗試使用其他計算方法來替換sqrt和乘法,只用整數算術和移位,但有可能它不會比一個sqrt和一個乘法更快。

可能值得使用另一種方法的唯一地方是,如果您運行的CPU不支持浮點運算。在這種情況下,編譯器可能必須在軟件中生成sqrt和double乘法,並且可以在針對特定應用程序進行優化時獲得優勢。

正如其他答案所指出的那樣,大整數仍然存在限制,但除非您打算遇到這些數字,否則利用浮點硬件支持可能比編寫自己的算法更好。

+1

我只是認爲有一個簡單而美觀的解決方案,沒有兩種類型的轉換:)好的,謝謝! – 2010-05-11 03:33:09

+0

分析我的應用程序顯示在is_square函數中花費了57%的時間:( – 2010-05-11 03:44:58

+0

您可能需要做一些緩存(不要計算相同的整數兩次),或者最初預先計算所有整數。 bool [] isSquare = new bool [100000]; for(int i = 1; i earlNameless 2010-05-11 03:56:29

0

這不是特別漂亮或快,但這裏是基於牛頓法的作品(慢)的自由鑄造,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 

它也許可以用加速一些額外的數字理論欺騙。

+0

謝謝!但它與我的任務相反 – 2010-05-11 17:17:59

1

有時編輯答案你不應該劃分問題轉化成過小零件(如檢查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 
+0

哦,非常有趣!我會考慮如何使它更適合我 – 2010-05-11 18:57:03

1

有一個很簡單的方法來測試完美正方形 - 從字面上看,你檢查數字的平方根是否在其小數部分不是零。
我假設平方根函數返回一個浮點,在這種情況下,你可以做的(僞代碼):

func IsSquare(N) 
    sq = sqrt(N) 
    return (sq modulus 1.0) equals 0.0 
+1

isSquare b n =(mod'(logBase b n)1.0)== 0.0 - Mod'from Data.Fixed – JayJay 2014-12-21 03:40:07

1

在另一個回答這個問題評論,您討論memoization 。請記住,這種技術有助於探針圖案表現出良好的密度。在這種情況下,這意味着一遍又一遍地測試相同的整數。你的代碼有多大可能重複相同的工作,從而從緩存的答案中受益?

你沒有給我們您輸入的分佈的概念,所以考慮採用了優秀的criterion包快速基準:

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 
       ] 

這個工作量可能會或可能不會是一個公平代表什麼你正在做,但寫的,高速緩存未命中率顯得過高:

timing probability-density

8

認爲它這樣,如果你有一個積極INT n,那麼你基本上幹什麼g在1 .. n的數字範圍上進行二進制搜索,找到第一個數字n',其中n' * n' = n

我不知道哈斯克爾,但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(log n)的。易於修改完美的立方體和更高的權力。

+3

我非常喜歡這個解決方案。我一直很驚訝,二進制搜索對於不同的事情有多麼有用。但是,O(log n)是誤導性的。你將執行O(log n)迭代,但是在每次迭代中你都有一個隱藏的mid * mid。平方數大約爲O(mlogm)。 m在sqrt(n)上關閉,所以假設m = sqrt(n)。對於m = sqrt(n),這最終的效率實際上是O(log n)* O(m log m)。仍然,二分搜索+1:P – Rubys 2010-05-11 20:06:18

+1

問題指定Haskell中的「Int」是固定精度(通常爲32位),所以我更喜歡在問題中使用的浮點方法。如果'N'是一個'Integer'(任意精度),我會使用你的方法。 – finnw 2010-07-23 14:07:06

7

有一個精彩庫包含在arithmoi包中包含的Haskell中的大多數數論相關問題。使用Math.NumberTheory.Powers.Squares庫。

具體爲isSquare'函數。

is_square :: Int -> Bool 
is_square = isSquare' . fromIntegral 

圖書館作爲圖書館的演進優化,深受人們更專注於效率的審覈,那麼你或I.雖然目前沒有this kind of shenanigans引擎蓋下的機制,它可以在未來,獲得更多優化。 View the source code瞭解它是如何工作的!

不要重新發明輪子,在可用時總是使用圖書館。

+0

我正在寫有趣的我自己的數字理論庫。我的觀點是瞭解我在其中的功能是如何工作的。該庫函數的最壞情況是: – Sean 2015-02-27 14:37:57

相關問題