简体   繁体   English

Haskell中可靠的立方根

[英]Reliable cube root in Haskell

I am doing question 62 at project euler and came up with the following to test whether a number is cubic: 我正在项目euler做问题62 ,并提出以下测试数字是否为立方数:

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

But due to floating point error, it returns incorrect results: 但由于浮点错误,它返回不正确的结果:

*Main> isCube (384^3)
False

Is there a way to implement a more reliable cube test? 有没有办法实现更可靠的立方体测试?

On a side-note, here is the rest of my solution, which doesn't work because of a type interface error on filter (isCube) (perms n) : 从侧面说明,这是我的解决方案的其余部分,由于filter (isCube) (perms n)上的类型接口错误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]

What do I need to do to fix the error? 我需要做些什么来修复错误?

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

Any optimisations are also welcome ;-) 任何优化也是受欢迎的;-)

Try to avoid using floating point numbers as much as possible, especially when you have a problem which concerns integer values. 尽量避免使用浮点数,尤其是遇到涉及整数值的问题时。 Floating point numbers have problems with rounding and that certain values (like 1/3) cannot be represented exactly. 浮点数在舍入时存在问题,并且某些值(如1/3)无法准确表示。 So it's no surprise that you get mysterious answers. 所以你得到神秘的答案并不奇怪。

First of all, in order to fix your type error you have to redefine isCube . 首先,为了修复类型错误,您必须重新定义isCube If you check it's type signature it looks like this: 如果你检查它的类型签名,它看起来像这样:

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

Note that it expects something that is of class Floating as its first argument. 请注意,它期望一些类为Floating东西作为其第一个参数。 Your problem is that you want to use this function on integer values and integers are not an instance of Floating . 您的问题是您希望对整数值使用此函数,而整数不是Floating的实例。 You can redefine isCube like this to make the function type check. 您可以像这样重新定义isCube以进行功能类型检查。

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

However, that will not make your program correct. 但是,这不会使您的程序正确。

One way to make your program more correct is to do what Henrik suggested. 让程序更正确的一种方法是做Henrik所建议的。 It would look like this: 它看起来像这样:

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

Good luck! 祝好运!

不太了解Haskell,但我会将立方根,圆形到nearerst整数,取立方体,并与原始值进行比较。

For another approach useful for Integer values have a look at the integerCubeRoot function in the arithmoi package . 对于对Integer值有用的另一种方法,请查看arithmoi包中的integerCubeRoot函数。

Example: 例:

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

perms has the type [Integer] . perms的类型为[Integer] isCube has the type (RealFrac a, Floating a) => a -> Bool (as you can check in GHCI). isCube具有类型(RealFrac a, Floating a) => a -> Bool (您可以在GHCI中查看)。 The RealFrac constraint comes from round x , the Floating constraint comes from x**(1/3) . RealFrac约束来自round xFloating约束来自x**(1/3) Since Integer is neither RealFrac nor Floating , isCube can't be used as Integer -> Bool . 由于Integer既不是RealFrac也不是FloatingisCube 不能用作Integer -> Bool So filter isCube (perms n) doesn't make sense. 因此filter isCube (perms n)没有意义。

So you need to fix isCube to work properly on Integer s: 所以你需要修复isCube才能在Integer正常工作:

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

In fact, the reason isCube (384^3) even compiles is that it "really" means isCube ((fromInteger 384)^(fromInteger 3)) . 实际上,原因是isCube (384^3)甚至编译就是它“真的”意味着isCube ((fromInteger 384)^(fromInteger 3))

Of course, this will still work badly due to floating point errors. 当然,由于浮点错误,这仍然会很糟糕。 Basically, checking floating numbers for equality, as you do in isInt , is almost always a bad idea. 基本上,像在isInt一样检查浮动数字是否相等,几乎总是一个坏主意。 See other answers for explanation how to make a better test. 请参阅其他答案,了解如何进行更好的测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM