迭代函数并分析haskell中的结果

时间:2009-08-06 10:17:22

标签: haskell

好的,回头看我的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函数找到最大链长的值,然后查看它在答案数组中的距离。

这是一个寻找解决方案的好方法,还是有更好的方法(也许效率更高)?

4 个答案:

答案 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的值。此类型只有三个可能的值:LTEQGT,分别小于,等于和大于。

所以,我们需要一个给出两对的函数告诉我们第二个组件如何相互比较:

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

它会为您提供一对具有最长链和相应长度的数字。您可以随意申请fstsnd

请注意,使用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)