Haskell mod 故障?

问题描述

这是我的 Haskell 函数,用于从列表中删除 2::Int5::Int

remPrimesFactors25 :: [Int] -> [Int]
remPrimesFactors25 [] = []
remPrimesFactors25 (x:xs)
  | x == 2                  = remPrimesFactors25 xs
  | x == 5                  = remPrimesFactors25 xs
  | otherwise           = x : remPrimesFactors25 xs
λ>  remPrimesFactors25 [2,5,23]
[23]
λ>  remPrimesFactors25 [2,23] == [23]
True
λ>  product (remPrimesFactors25 [2,23])
23
λ>  product [23]
23
λ>  product (remPrimesFactors25 [2,23]) == product [23]
True

这是我的问题。为什么会发生这种情况?

λ>  mod (10^22) (product (remPrimesFactors25 [2,23]) )
15
λ>  mod (10^22) (product [23])
1

解决方法

remPrimesFactors 总是返回 Int 值的列表,而不是 Integer 值。由于 mod 要求两个参数具有相同的类型,因此 10^22 被视为 Int,它没有处理大数的精度准确。

Prelude> 10^22 :: Integer
10000000000000000000000
Prelude> 10^22 :: Int
1864712049423024128
,

您正在使用 IntInt 使用固定数量的位,并且至少可以表示 -229 到 229-1 范围内的所有值。通常在 32 位机器上,它的范围是 -231 到 231-1,在 64 位机器上 -263 到 263-1。 10^22 然而大于这些范围。这意味着 10^22Int 将在 64 位机器上表示为例如 1'864'712'049'423'024'128​​。

如果你使用一个Integer,它可以代表任意大小的值,那么就不存在这个问题了。如果您因此将函数重写为:

remPrimesFactors25 :: [Integer] -> [Integer]
remPrimesFactors25 = filter (\x -> x != 2 && x != 5)

然后 10^22 将被解释为 Integer,因此 10^22 将取值为 10'000'000'000'000'000'000'000。

然而,您不需要计算 10^22。您可以使用 powerMod :: (Integral a,Integral b) => a -> b -> a -> a 中的 arithmoi package 之类的算法。