Reputation: 170
sorry for the title, I could not think of a better one. I wanted to show the fact that in a group of 23 people it's more likely that there are two with the same birthday than not when I wrote this function:
birth :: Int -> Double
birth n = (fromIntegral . product $ [365,364..366-n]) / (fromIntegral . product . replicate n $ 365)
which does not work properly while these three do
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)
The results:
birth 23 == -1.7444829681771515e-41
birth1 23 == 0.4927027656760146
birth2 23 == 0.4927027656760146
birth3 23 == 0.4927027656760146
The most surprising is that the first one is negative. Why is this behaviour? I renamed everything and tried again before posting this, nothing is imported except for Prelude
.
Cheers
Upvotes: 1
Views: 105
Reputation: 54058
The problem is that you're using Int
instead of Integer
. If you change the definition of birth
to be:
-- 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)
You'll get the right answer.
You unfortunately can't use an Integer
for the first argument of replicate
, so you have to convert it to an Int
. You could alternatively use birth :: Int -> Double
and product [365,364..366 - fromIntegral n] // ...
, which would probably be safer.
As for your other definitions, birth1
could easily have the type Integer -> Double
, as could birth2
because the n
is only used in the numerator, while in birth3
you get away with Int -> Double
because n
is only used as the argument to replicate
. In fact, if you type these definitions directly into GHCi, you see that the types of these functions are
birth :: Fractional a => Int -> b
birth1 :: (Fractional a, Integral b) => b -> a
birth2 :: (Fractional a, Integral b) => b -> a
birth3 :: Fractional a => Int -> a
So it's only birth
and birth3
that restrict you to using Int
, but because of the way you defined birth3
, you still get the right answer.
Another way to do this with only Int
is to perform the divisions as you go, then do the product. This keeps you from having to convert very large numbers, but it might be slower depending on n
. You could do this as
birth :: Int -> Double
birth n = product $ take n $ zipWith (//) [365,364..] $ repeat 365
Which is arguably easier to read as well (I like it better, but that's just an opinion).
Upvotes: 4
Reputation: 12060
This is caused by an overflow....
Consider just part of the numerator alone
> product [366-(22::Int)..365]
4190530200557060096
> product [366-(23::Int)..365]
-1494178958273413120
Once you get past a certain point, the value goes negative.... This is because int only goes to 2^64 (one bit is used for the sign, so going to 2^63 will cause things to go negative).
> 2^63-1::Int
9223372036854775807
> 2^63::Int
-9223372036854775808
The Int
type is actually cyclical, tied to the size of values on your system. Once you cross this magical value, the values cycle back through large negative numbers....
Upvotes: 4