定义为函数和计算为变量时的不同行为

时间:2013-12-25 14:31:15

标签: haskell

对不起标题,我想不出更好的一个。 我想表明这样一个事实:在一个23人的小组中,当我写这个函数的时候,生日相同的两个人更有可能是:

birth :: Int -> Double
birth n = (fromIntegral . product $ [365,364..366-n]) / (fromIntegral . product . replicate n $ 365)

这三个不能正常工作

birth1 n = (fromIntegral . product $ [365,364..366-n])  / (fromIntegral $ 365^n)
birth2 n = (fromIntegral . product $ [365,364..366-n])  / (fromIntegral . product . replicate 23 $ 365)
birth3 n = (fromIntegral . product $ [365,364..366-23]) / (fromIntegral . product . replicate n $ 365)

结果:

birth  23 == -1.7444829681771515e-41
birth1 23 == 0.4927027656760146
birth2 23 == 0.4927027656760146
birth3 23 == 0.4927027656760146

最令人惊讶的是,第一个是否定的。为什么会这样?我重命名了所有内容并在发布之前再次尝试,除了Prelude之外没有导入任何内容。

干杯

2 个答案:

答案 0 :(得分:4)

这是由溢出引起的......

仅考虑分子的一部分

> product [366-(22::Int)..365]
4190530200557060096

> product [366-(23::Int)..365]
-1494178958273413120

一旦超过某一点,该值就会变为负值....这是因为int只会变为2 ^ 64(一位用于符号,所以转到2 ^ 63会导致事情变为负数)。

> 2^63-1::Int
9223372036854775807

> 2^63::Int
-9223372036854775808

Int类型实际上是周期性的,与系统上的值大小相关联。一旦你越过这个神奇的价值,这些价值就会循环回到大的负数....

答案 1 :(得分:4)

问题是您使用的是Int而不是Integer。如果您将birth的定义更改为:

-- Helper function
(//) :: (Integral a, Fractional b) => a -> a -> b
x // y = fromIntegral x / fromIntegral y

birth :: Integer -> Double
birth n = product [365,364..366-n] // (product . replicate (fromIntegral n) $ 365)

你会得到正确的答案。

很遗憾,您无法使用Integer作为replicate的第一个参数,因此您必须将其转换为Int。您也可以使用birth :: Int -> Doubleproduct [365,364..366 - fromIntegral n] // ...,这可能更安全。


至于您的其他定义,birth1可以很容易地使用Integer -> Double类型,因为birth2只能在分子中使用,而n只能在birth3中使用1}}你逃脱Int -> Double,因为n仅用作replicate的参数。实际上,如果您直接在GHCi中键入这些定义,您会看到这些函数的类型是

birth :: Fractional a => Int -> b
birth1 :: (Fractional a, Integral b) => b -> a
birth2 :: (Fractional a, Integral b) => b -> a
birth3 :: Fractional a => Int -> a

所以只有birthbirth3限制您使用Int,但由于您定义birth3的方式,您仍然可以得到正确答案。


仅使用Int执行此操作的另一种方法是随时执行分区,然后执行产品。这使您不必转换非常大的数字,但可能会慢n。你可以这样做

birth :: Int -> Double
birth n = product $ take n $ zipWith (//) [365,364..] $ repeat 365

这也可以说更容易阅读(我更喜欢它,但这只是一个意见)。

相关问题