好的,回头看我的previous question,我还在努力学习haskell并解决当前从下一次迭代中找到最长链的问题:
chain n | n == 0 = error "What are you on about?"
| n == 1 = [1]
| rem n 2 == 0 = n : chain (n `div` 2)
| otherwise = n : chain (3 * n + 1)
我对此进行了排序,但我需要从1,000,000以下的起始编号中找到最长的链。那么如何使每个起始编号达到1,000,000,然后打印链长最长的编号。 我可以用一个例子来做:
Main> length (chain n)
我假设我需要将输出作为数组,然后使用maximum
函数找到最大链长的值,然后查看它在答案数组中的距离。
这是一个寻找解决方案的好方法,还是有更好的方法(也许效率更高)?
答案 0 :(得分:2)
你对maximum
部分是正确的。要获取列表(这就是Haskell的[]是什么,数组是不同的结构),您需要使用map
高阶函数,如下所示:
chainLength n = length (chain n)
lengths = map chainLength [1..1000000]
基本上,map
将函数和列表作为参数。它将函数应用于列表中的每个元素,并返回结果列表。
由于您需要其链具有该长度的数字,您可能需要更改chainLength
函数以返回该数字,如下所示:
chainLength n = (n, length (chain n))
这样你就会得到一对数组,每个数字及其链长。
现在你需要获得具有最大第二个组件的对。这就是maximumBy
函数的用武之地。它的工作方式与maximum
类似,但它将一个函数作为参数来选择如何比较这些值。在这种情况下,该对的第二个组成部分。此比较函数采用两个数字并返回类型Ordering
的值。此类型只有三个可能的值:LT
,EQ
,GT
,分别小于,等于和大于。
所以,我们需要一个给出两对的函数告诉我们第二个组件如何相互比较:
compareSnd (_, y1) (_, y2) = compare y1 y2
-- Or, if you import Data.Function, you can write it like this (thanks alexey_r):
compareSnd = compare `on` snd -- reads nicely
我使用了比较数字的默认compare
函数(好吧,not just numbers)。
现在我们只需要使用此功能获得最大值:
longestChain = maximumBy compareSnd lengths
它会为您提供一对具有最长链和相应长度的数字。您可以随意申请fst
和snd
。
请注意,使用zip
和合成可以更简洁,但由于您将问题标记为新手,我认为最好将其分解为这样。
答案 1 :(得分:1)
SPOILER(解决100以下正整数的问题):
module Test where
import Data.List -- this contains maximumBy
chain n
| n == 0 = error "What are you on about?"
| n == 1 = [1]
| rem n 2 == 0 = n : chain (n `div` 2)
| otherwise = n : chain (3 * n + 1)
chains = map (\x -> (x,chain x)) [1..100]
cmpSnd (a,b) (c,d)
| length b > length d = GT
| length b == length d = EQ
| otherwise = LT
solve = (fst . maximumBy cmpSnd) chains
chain函数使用map。它将函数应用于值列表的每个元素,所以
map succ [1,2]
与
相同[succ 1,succ 2]
cmpSnd函数是一个比较函数,可能存在于Hierarchical Libraries深处,但是我找不到它,所以我创建了它。 GT意味着“第一个值大于第二个值”,其余值很小。
Solve采用列表的最大值(通过利用我们之前定义的比较函数)。这将是一对整数和一个列表。它只返回整数(因为fst)。
评论:你的链函数不是尾递归的。这意味着大型链将不可避免地导致堆栈溢出。你应该添加一个显式的累加器变量并使其尾递归。
答案 2 :(得分:0)
像
这样的东西fst $ maximumBy (length . snd) $ zip [1..1000000] $ map chain [1..1000000]
(未测试的)
即。不知道沿着最长链在最长链的列表中走多远,而是用链子来携带种子值。
答案 3 :(得分:0)
几年前我研究过Haskell,所以我不记得那么好。另一方面,我已经测试了这段代码并且它有效。您将获得最大链和生成它的数字。但正如fiships之前所说的那样,它会因为大价值而溢出。
chain :: Int -> [Int]
chain n
| n == 0 = []
| n == 1 = [1]
| rem n 2 == 0 = n : chain (n `div` 2)
| otherwise = n : chain (3 * n + 1)
length_chain :: Int -> Int
length_chain n = length (chain n)
max_pos :: (Int,Int) -> Int -> [Int] -> (Int,Int)
max_pos (m,p) _ [] = (m,p)
max_pos (m,p) a (x:xs)
| x > m = max_pos (x,a) (a+1) xs
| otherwise = max_pos (m,p) (a+1) xs
指令将
Main> max_pos (0,0) 1 (map length_chain [1..10000])
(262,6171)