当前位置:  开发笔记 > 人工智能 > 正文

列表中的数字乘数

如何解决《列表中的数字乘数》经验,为你挑选了1个好方法。

如何在合并的排序列表中打印给定数字列表的倍数?

take 10 (multiples [4,5])

4,5,8,10,12,15,16,20,24,25

我已经让它适用于大小为2或1的列表,但我需要一个更通用的解决方案:)



1> ShreevatsaR..:

这里有两个有效的解决方案,可以生成排序的无限列表,无需重复,您可以take从中获取.假设您的输入multiples有n个元素.

每个元素O(n)

首先,对于输入中的每个数字,创建其倍数的无限列表.然后,仔细合并这些列表,保持它们的排序并避免重复.(这是问题的难点.)

multiples xs = merge [map (*x) [1..] | x<-xs]

merge xss
    | all null xss = []
    | otherwise    = m : merge (map (avoid m) xss)
    where
      m = minimum [head xs | xs<-xss, xs/=[]]
      avoid m (x:xs) | m==x  = xs
      avoid m   xs           = xs

(感谢MtnViewMark的评论,代码从原始版本中清理完毕.)这有效:

*Main> take 20 $ multiples [4,6,9]
[4,6,8,9,12,16,18,20,24,27,28,30,32,36,40,42,44,45,48,52]

这种实现merge比一次合并两个列表更有效,并且生成每个输出元素只需要O(n)时间.

每个元素O(log n)

更多(和AFAICT,最有效)算法是通过将候选者保持在堆中来生成所需的倍数.每个输出元素只需要O(log n)时间.

import Data.Heap as Heap (MinHeap, insert, empty, view)

multiplesH xs = uniq $ tail $ map fst $ iterate (next . snd) (0, prep xs)
    where
      prep :: Ord a => [a] -> MinHeap (a,a)
      prep = foldr (\x -> insert (x,x)) empty
      next h = case view h of Just ((x,n),hh) -> (x, insert (x+n,n) hh)
      uniq (x:y:ys) | x==y  = uniq (y:ys)
      uniq (x:xs)           = x: (uniq xs)
      uniq []               = []

当你只有几个数字时,它们没有太大的不同,但是对于大数n,堆版本快得多:

*Main> :set +s
*Main> multiples [1000..2000] !! 10000
20088
(21.70 secs, 2108213464 bytes)
*Main> multiplesH [1000..2000] !! 10000
20088
(0.08 secs, 15348784 bytes)


根据您的要求:学习使用"地图"而不是列表推导.而不是"[notm m xs | xs <-xss]"你应该写"map(notm m)xss","allEmpty xss = and [xs == [] | xs <-xss]"变成"allEmpty xss = and $ map(== [])xss".然后学习'all'和'null',这就变成了'allEmpty = all null'!使用'过滤器',现在你可以写"m =最小$ map head $ filter(not.null)xss".最后,你可以通过写'multiples xs = merge $ map(\n - > map(*n)[1 ..])xs"来删除倍数的第一个子句.
推荐阅读
周扒pi
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有