如何在合并的排序列表中打印给定数字列表的倍数?
即
take 10 (multiples [4,5])
给出
4,5,8,10,12,15,16,20,24,25
我已经让它适用于大小为2或1的列表,但我需要一个更通用的解决方案:)
答案 0 :(得分:8)
以下是两个有效的解决方案,可以生成无重复的排序无限列表,您可以take
来实现。假设您对multiples
的输入有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)时间。
更多(和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)
答案 1 :(得分:4)
参数中的每个数字都成为无数的倍数列表
multiLists :: [Integer] -> [[Integer]]
multiLists = map (\x -> iterate (+x) x)
然后您需要合并结果列表。由于每个列表都保证按升序排列,因此您可以使用类似this page末尾的合并函数。
最后,您可能希望消除重复项。使用排序列表执行此操作的方法是:
sortedNub :: [Integer] -> [Integer]
sortedNub = map head . group
答案 2 :(得分:2)
这是一个总是生成排序结果,删除重复项,生成无限列表(你可以take
)并且相对有效(应该是常量内存!)的版本:
multiples :: (Num a, Ord a) => [a] -> [a]
multiples = map (fst.head) . iterate step . prep
where prep = map (\i -> (i,i))
next (m,i) = (m+i,i)
step (p:ps) = uniq $ insert (next p) ps
insert q [] = [q]
insert q (p:ps) | q > p = p : insert q ps
insert q ps = q : ps
uniq p@((ma,_):(mb,_):_) | ma == mb = step p
uniq p = p
示例:
> take 20 $ multiples [4,9]
[4,8,9,12,16,18,20,24,27,28,32,36,40,44,45,48,52,54,56,60]
> take 20 $ multiples [4,8,10]
[4,8,10,12,16,20,24,28,30,32,36,40,44,48,50,52,56,60,64,68]
> take 20 $ multiples [4, 9, 20]
[4,8,9,12,16,18,20,24,27,28,32,36,40,44,45,48,52,54,56,60]
注意:假设输入列表已排序。在. sort
之后添加. prep
以删除此约束。
答案 3 :(得分:1)
multi xs = [x*y | y <- [1..], x <- xs ]
应该这样做。主要问题是控制你应该take
的数量有点难。
要避免结果中的多个相等数字,请在结果列表中应用Data.List.nub。这不是非常复杂,可以更快地完成,但可以完成工作。
答案 4 :(得分:1)
我将此视为整数列表中的过滤器。
您需要的只是一个谓词,用于确定整数是否是列表中项目的倍数。
然后用该谓词过滤[1 ..]。
multiples xs = filter (isDividedByAny xs) [1..]
where isDividedByAny xs int = any (divides int) xs
where divides int elem = int `mod` elem == 0
答案 5 :(得分:1)
我很惊讶地发现没有提到“汉明问题”:汉明问题是大卫特纳为他的FP提出的懒惰函数式编程的经典例子之一,这是第一个类似Haskell的语言Miranda。
汉明问题与相同,与multiples [2,3,5]
类似,而特纳的解决方案是(见下面的评论):
ham = 1 : foldr1 merge [mult 2 ham, mult 3 ham, mult 5 ham] where mult n x = [n*a|a<-x] merge (a:x) (b:y) = a : merge x y, if a=b = a : merge x (b:y), if a<b = b : merge (a:x) y, if a>b
(来自特纳的Example Miranda scripts)
这直接推广到(假设传递给倍数的所有元素都大于1,并且与问题相反,参数列表正在增加):
multiples ms = drop 1 mms where mms = 1: foldr1 merge (map (mult mms) ms)) mult x n = [n*a|a<-x] merge (a:x) (b:y) = a : merge x y, if a=b = a : merge x (b:y), if a<b = b : merge (a:x) y, if a>b
讨论了关于LtU的汉明问题的四种解决方案:expressivity of "idiomatic C++"。
答案 6 :(得分:1)
另一个答案?那么,解决这个问题的一种方法就是广义合并。我变得有点痴迷于找到一种相对干净和有效的多向合并方法。
此合并函数将任意有限数量的任意列表作为输入并生成它们的合并。唯一的前提条件是列表已排序。列表可以是空的或无限的:
merge :: (Ord a) => [[a]] -> [a]
merge rs =
case foldr minToFront [] rs of
[] -> []
([]:rs) -> merge rs
((a:as):rs) -> a : merge (as:rs)
where
minToFront a (b:rs) | a `after` b = b:a:rs
minToFront a qs = a:qs
[] `after` _ = False
_ `after` [] = True
(a:_) `after` (b:_) = a > b
这段代码只为生成的每个元素输入一个输入列表的头部。
一旦你有了这个,定义原始功能很容易:
multiples :: (Num a, Ord a) => [a] -> [a]
multiples = uniq . merge . map (\n -> iterate (+n) n)
你需要另一个很好的通用效用函数去除重复的答案。以unix实用程序命名,这里是:
uniq :: (Eq a) => [a] -> [a]
uniq :: (Eq a) => [a] -> [a]
uniq [] = []
uniq (a:bs@(b:_)) | a == b = uniq bs
uniq (a:bs) = a : uniq bs
你可以用这个简单的代码将那个小小的snippit变成一个完全可行的等效于uniq
命令行实用程序(好吧,忽略命令行选项):
main :: IO ()
main = interact (unlines . uniq . lines)
哈斯克尔让我微笑!
答案 7 :(得分:0)
也许:
let howmany = 10 in take howmany (nub (sort [ x * y | x <- [4, 5, 6, 7], y <- [1..howmany] ]))
给出:
[4,5,6,7,8,10,12,14,15,16]
Haskell不是我的强项,对于更大的列表来说效率相当低,但是它有用(我想!)。
您需要hugs / ghci中的列表模块:l List
。
答案 8 :(得分:0)
这确实有效,但不适用于无限列表:
sort [ x * y | x <- [4, 5], y <- [1..10] ]
因此,您必须在[1..10]部分中指定所需的倍数。 不好的是,它不会尊重[5,4],例如,只是把它分类到同一个东西。
好的,更好的一个:
multiples :: (Num a, Ord a) => [a] -> [a]
multiples nums = sort $ multiply 1
where multiply n = map (*n) nums ++ (multiply $ n + 1)
需要10 $倍数[4,5]
[4,5,8,10,12,15,16,20,20,25]
你可能想在那里添加“nub”,以删除双号
multiples :: (Num a, Ord a) => [a] -> [a]
multiples nums = sort . nub $ multiply 1
where multiply n = map (*n) nums ++ (multiply $ n + 1)