对不起标题,我想不出更好的一个。 我想表明这样一个事实:在一个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
之外没有导入任何内容。
干杯
答案 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 -> Double
和product [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
所以只有birth
和birth3
限制您使用Int
,但由于您定义birth3
的方式,您仍然可以得到正确答案。
仅使用Int
执行此操作的另一种方法是随时执行分区,然后执行产品。这使您不必转换非常大的数字,但可能会慢n
。你可以这样做
birth :: Int -> Double
birth n = product $ take n $ zipWith (//) [365,364..] $ repeat 365
这也可以说更容易阅读(我更喜欢它,但这只是一个意见)。