2009-12-02 77 views
5

我在項目歐拉做question 62並想出了下面來測試一個數是否爲立方:可靠的立方根

isInt x = x == fromInteger (round x) 
isCube x= isInt $ x**(1/3) 

但由於浮點錯誤,它返回不正確的結果:

*Main> isCube (384^3) 
False 

有沒有一種方法來實現更可靠的立方體測試?

在一個側面說明,這裏是我的解決方案的休息,這並不因爲filter (isCube) (perms n)一種類型的接口錯誤的工作:

cubes = [n^3|n<-[1..]] 
perms n = map read $ permutations $ show n :: [Integer] 
answer = head [n|n<-cubes,(length $ filter (isCube) (perms n)) == 5] 

什麼我需要做什麼來修復錯誤?

No instances for (Floating Integer, RealFrac Integer) 
    arising from a use of `isCube' at prob62.hs:10:44-49 

任何的優化,也歡迎;-)

回答

7

儘量避免使用浮點數,尤其是當您遇到涉及整數值的問題時。浮點數有四捨五入的問題,某些值(如1/3)無法精確表示。所以你得到神祕的答案並不奇怪。

首先,爲了解決您的類型錯誤,您必須重新定義isCube。如果你檢查它的類型簽名看起來是這樣的:

isCube :: (RealFrac a, Floating a) => a -> Bool 

注意,它期望的東西,是Floating類作爲第一個參數。你的問題是你想要在整數值上使用這個函數,整數不是Floating的實例。你可以像這樣重新定義isCube來進行函數類型檢查。

isCube x = isInt $ (fromIntegral x) ** (1/3) 

但是,這不會讓你的程序正確。

使您的程序更加正確的一種方法是完成Henrik建議的操作。它看起來像這樣:

isCube x = (round (fromIntegral x ** (1/3)))^3 == x 

祝你好運!

+0

感謝您的幫助 –

3

不知道很多關於Haskell的,但我會採取立方根,繞到nearerst整數,取立方體,並比較原值。

0

perms的類型爲[Integer]isCube的類型爲(RealFrac a, Floating a) => a -> Bool(因爲您可以在GHCI中進行檢查)。 RealFrac約束來自round xFloating約束來自x**(1/3)。由於Integer既不是RealFrac也不是Floating,isCube不能可用作Integer -> Bool。所以filter isCube (perms n)沒有意義。

所以你需要修復isCubeInteger s請正確工作:

isCube x = isInt $ (fromInteger x)**(1/3) 

事實上,isCube (384^3)甚至編譯原因是它「真」是指isCube ((fromInteger 384)^(fromInteger 3))

當然,由於浮點錯誤,這仍然會工作得很糟糕。基本上,像在isInt中那樣檢查浮點數是否相等,幾乎總是一個壞主意。請參閱其他答案以解釋如何進行更好的測試。

1

有關Integer有用的另一種方法,請參閱arithmoi package中的integerCubeRoot函數。

例子:

ghci> import Math.NumberTheory.Powers.Cube 
ghci> let x = 12345^3333 
ghci> length $ show x 
13637 
ghci> isCube x 
True 
ghci> isCube (x+1) 
False 
ghci> length $ show $ integerCubeRoot x 
4546