我正在项目euler做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
也欢迎任何优化; - )
答案 0 :(得分:8)
尽量避免使用浮点数,尤其是遇到涉及整数值的问题时。浮点数在舍入时存在问题,并且某些值(如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
祝你好运!
答案 1 :(得分:3)
对Haskell不太了解,但我会将立方根,圆形到nearerst整数,取出立方体,并与原始值进行比较。
答案 2 :(得分: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
答案 3 :(得分:0)
perms
的类型为[Integer]
。 isCube
的类型为(RealFrac a, Floating a) => a -> Bool
(您可以在GHCI中查看)。 RealFrac
约束来自round x
,Floating
约束来自x**(1/3)
。由于Integer
既不是RealFrac
也不是Floating
,isCube
不能用作Integer -> Bool
。所以filter isCube (perms n)
没有意义。
因此,您需要修复isCube
才能在Integer
s:
isCube x = isInt $ (fromInteger x)**(1/3)
事实上,isCube (384^3)
甚至编译的原因是它“真的”意味着isCube ((fromInteger 384)^(fromInteger 3))
。
当然,由于浮点错误,这仍然会很糟糕。基本上,像在isInt
中一样检查浮动数字是否相等,几乎总是一个坏主意。请参阅其他答案,了解如何进行更好的测试。