Haskell 中可靠的立方根
我正在 euler 项目中做问题 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
也欢迎任何优化;-)
I am doing question 62 at project euler and came up with the following to test whether a number is cubic:
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)
:
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 ;-)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
尽量避免使用浮点数,尤其是当您遇到涉及整数值的问题时。浮点数存在舍入问题,并且某些值(如 1/3)无法精确表示。因此,您得到神秘的答案也就不足为奇了。
首先,为了修复类型错误,您必须重新定义
isCube
。如果您检查它的类型签名,它看起来像这样:请注意,它需要 Floating 类的内容作为其第一个参数。您的问题是您想在整数值上使用此函数,而整数不是 Floating 的实例。您可以像这样重新定义
isCube
来进行函数类型检查。但是,这不会使您的程序正确。
让你的程序更加正确的一种方法是按照 Henrik 的建议去做。它看起来像这样:
祝你好运!
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. 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
. If you check it's type signature it looks like this:Note that it expects something that is of class
Floating
as its first argument. Your problem is that you want to use this function on integer values and integers are not an instance ofFloating
. You can redefineisCube
like this to make the function type check.However, that will not make your program correct.
One way to make your program more correct is to do what Henrik suggested. It would look like this:
Good luck!
对 Haskell 不太了解,但我会取立方根,四舍五入到最接近的整数,取立方,然后与原始值进行比较。
Don't know much about Haskell, but I would take the cube root, round to the nearerst integer, take the cube, and compare to the original value.
对于对 Integer 值有用的另一种方法,请查看 arithmoi 包。
例子:
For another approach useful for
Integer
values have a look at theintegerCubeRoot
function in the arithmoi package.Example:
perms
的类型为[Integer]
。isCube
的类型为(RealFrac a, Floating a) =>一个-> Bool
(您可以在 GHCI 中查看)。RealFrac
约束来自round x
,Floating
约束来自x**(1/3)
。由于Integer
既不是RealFrac
也不是Floating
,isCube
不能用作整数 ->布尔。所以
filter isCube (perms n)
没有意义。因此,您需要修复
isCube
才能在Integer
上正常工作:事实上,
isCube (384^3)
甚至可以编译的原因是它“真的”意味着isCube ((fromInteger 384)^(fromInteger 3))
。当然,由于浮点错误,这仍然会工作得很糟糕。基本上,像在 isInt 中所做的那样检查浮点数是否相等几乎总是一个坏主意。请参阅其他答案以了解如何进行更好的测试的说明。
perms
has the type[Integer]
.isCube
has the type(RealFrac a, Floating a) => a -> Bool
(as you can check in GHCI). TheRealFrac
constraint comes fromround x
, theFloating
constraint comes fromx**(1/3)
. SinceInteger
is neitherRealFrac
norFloating
,isCube
can't be used asInteger -> Bool
. Sofilter isCube (perms n)
doesn't make sense.So you need to fix
isCube
to work properly onInteger
s:In fact, the reason
isCube (384^3)
even compiles is that it "really" meansisCube ((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. See other answers for explanation how to make a better test.