在haskell中重写euler-totient函数

时间:2018-03-31 19:34:43

标签: haskell

如果我想编写totient函数,一种方式(假设我们有primeFactors函数,返回包含多重性的列表。)如下:

totient:: Integer->Integer
totient m = product [(p - 1)  | p <- primeFactors m]

这样会好的,但还有另一种方法可以做到这一点,将产品超过(1-1 / p)并将产品乘以n。但是,这里存在“类型”问题,因为1-1 / p永远不是整数,所以类似

totientRevisited n =n* product  map (\x->1-1/x) (primeFactors n)

不会像写的那样在Haskell中运行。

我的问题是,是否有一种简单的方法可以在函数之间传递给新类型。我怀疑是某种方式,但我一直无法解决这个问题。

2 个答案:

答案 0 :(得分:2)

有两个原因:

totientRevisited n = n * product  map (\x->1-1/x) (primeFactors n)

不起作用。

首先,product需要一个参数((Num a, Foldable t) => t a),然后给它三个(map(\x->1-1/x)(primeFactors n))。这是$的典型用例。你也没有围绕产品的括号。让我们写下这个:

totientRevisited n = n * (product $ map (\x->1-1/x) (primeFactors n))

其次,您遇到数字类型问题。您将1除以整数。然而/需要两个分数:

Prelude> :t (/)
(/) :: Fractional a => a -> a -> a

您必须将x转换为Fractional。使用fromIntegral功能:

totientRevisited n = (fromIntegral n) * (product $ map (\x->1-1/(fromIntegral x)) (primeFactors n))

您必须使用fromIntegral n,因为(*)需要两个相同类型的Nums:

Prelude> :t (*)
(*) :: Num a => a -> a -> a

现在它运行,但它返回一个Fractional。

只需获取结果的round即可获得整数(再次注意$),您就完成了:

totientRevisited n = round $ (fromIntegral n) * (product $ map (\x->1-1/(fromIntegral x)) (primeFactors n))

编辑精确度:我写过&#34; product期望一个参数((Num a, Foldable t) => t a),你给它三个(map,{{ 1}}和(\x->1-1/x))&#34;。这在技术上并不正确:你不能给函数赋予三个参数,因为Haskell中的函数总是需要一个参数并且可能返回另一个将接受另一个参数的函数。那是什么&#34; curryfication&#34;手段。因此,您向(primeFactors n)提供了product参数,但它期望可折叠,因此错误。

答案 1 :(得分:0)

那么,你的totientRevisited n = n * (1 - 1/p_1) * (1 - 1/p2) * .. * (1 - 1/p_k)

不要先在括号中乘以表达式,为什么不这样做呢:

x_0 = n
x_1 = x_0 * (1 - 1/p_1) = x_0 - x_0/p_1 = x_0 - x_0 `div` p_1
x_2 = x_1 * (1 - 1/p_2) = x_1 - x_1/p_2 = x_1 - x_1 `div` p_2
x_3 = ...

等等。这是整数算术,可以很容易地写在Haskell上。