我的目标是编写一个函数,该函数计算某个特定数字“ n”以下的最大Collatz数字。 (对于熟悉的人来说,这是欧拉计画的问题。)
一些上下文:给定整数的Collatz数等于该整数的Collatz序列的长度。整数的Collatz序列计算如下:序列中的第一个数字(“ n0”)是该整数本身;如果n0是偶数,则序列中的下一个数字(“ n1”)等于n / 2;如果n0是奇数,则n1等于3 * n0 +1。我们继续递归扩展序列,直到到达1,此时序列结束。例如,5的collatz序列为:{5,16,8,8,4,2,1}(因为16 = 3 * 5 + 1,8 = 16/2,4 = 8/2,...)
我正在尝试编写一个函数(“ maxCollatzUnder”),该函数在传递整数“ m”时会返回具有最长Collatz序列(即最大Collatz编号)的整数(小于或等于m)。例如,maxCollatz 20(即(包含)20以下的整数具有最长的拼贴序列?)应返回19(数字19的Collatz序列长度为21:[19,58,29,88,44,22, 11,34,17,52,26,13,40,20,10,5,16,8,4,2,1])。
在下面的代码中,“ collatz”和“ collatzHelper”函数可以正确编译并运行。我在使用“ maxCollatzUnder”函数时遇到麻烦。此函数旨在(I)为范围从1到m的每个整数x创建一个2元组(x,y)的列表(其中m是函数参数),其中y代表整数x的Collatz数,然后( II)浏览列表中最高的Collatz数(即y),并返回其关联的整数(即x)
maxCollatzUnder n = foldl(\acc (i,j) -> if j > acc then i else acc) 0
(zip [1..n] ( map collatzLength [1..n]))
where collatzLength n = length . collatz $ n
collatz n = map truncate $ collatzHelper n
collatzHelper 0 = [0]
collatzHelper 1 = [1]
collatzHelper n
| (truncate n) `mod` 2 == 0 = [n] ++ collatzHelper (n/2)
| otherwise = [n] ++ collatzHelper (3*n+1)
我(尝试)编译时收到以下错误。
*Main> :l PE14Collatz.hs
[1 of 1] Compiling Main ( PE14Collatz.hs, interpreted )
PE14Collatz.hs:7:89:
No instance for (RealFrac Int)
arising from a use of `collatzLength'
In the first argument of `map', namely `collatzLength'
In the second argument of `zip', namely
`(map collatzLength [1 .. n])'
In the third argument of `foldl', namely
`(zip [1 .. n] (map collatzLength [1 .. n]))'
Failed, modules loaded: none.
令人好奇的是,如果将“ maxCollatzUnder”更改为以下代码(请参见下文),则代码可以正确编译并运行。唯一的变化是,在以下版本中,fold函数返回的是“ j”(即最大的Collatz数),而不是“ i”(即产生最大的Collatz数的整数)。
maxCollatzUnder n = foldl(\acc (i,j) -> if j > acc then j else acc) 0
(zip [1..n] ( map collatzLength [1..n]))
where collatzLength n = length . collatz $ n
欢迎提出关于更有效/更优雅的方法的建议,尽管我仍然有兴趣了解此错误的原因。
最佳答案
由于使用了truncate
(RealFrac
的方法)和/
(Fractional
的方法,RealFrac
的超类),Haskell为您的后两个函数推断出以下两个类型签名:
collatz :: (RealFrac a, Integral b) => a -> [b]
collatzHelper :: RealFrac a => a -> [a]
然后,Haskell尝试推断
maxCollatzUnder
的类型,其思考过程如下:“在
collatzLength n = length . collatz $ n
中,我们将n
传递给collatz
,因此collatzLength
的参数必须是RealFrac
。”“因此,在
map collatzLength [1..n]
中,[1..n]
必须是RealFrac
值的列表。”“因此,
n
中的map collatzLength [1..n]
必须是RealFrac
类型。”“因此,
n
中的zip [1..n]
(与n
相同)必须是RealFrac
类型,因此[1..n]
是RealFrac
的列表。”“因此,
i
中的(\acc (i,j) -> if j > acc then i else acc)
必须是RealFrac
。”“由于上述lambda可以返回
i
或acc
,因此它们必须是相同的类型。”“因为将
j
与acc
进行比较,所以j
必须与acc
属于同一类型-因此与i
和RealFrac
属于同一类型。”“但是等等-
j
是collatzLength
的返回值,它是对length
的调用的返回值,因此它必须是Int
,但是Int
不在!”“错误!错误!”
我现在必须走(编译器阴谋不喜欢我泄露他们的秘密),但最短的解决方法是不使用
RealFrac
和truncate
,而仅使用/
进行(泛洪)整数除法。