我想迭代2(或3)个无限列表,找到满足条件的“最小”对,如下所示:
until pred [(a,b,c) | a<-as, b<-bs, c<-cs]
where pred (a,b,c) = a*a + b*b == c*c
as = [1..]
bs = [1..]
cs = [1..]
在整个程序运行期间,上面的内容不会很快,a == b == 1
。
是否有一个很好的方法来解决问题,例如构建无限序列[(1,1,1),(1,2,1),(2,1,1),(2,1,2),(2,2,1),(2,2,2),(2,2,3),(2,3,2),..]
?
奖励:是否有可能推广到n元组?
答案 0 :(得分:8)
有一个monad,Omega。
Prelude> let as = each [1..]
Prelude> let x = liftA3 (,,) as as as
Prelude> let x' = mfilter (\(a,b,c) -> a*a + b*b == c*c) x
Prelude> take 10 $ runOmega x'
[(3,4,5),(4,3,5),(6,8,10),(8,6,10),(5,12,13),(12,5,13),(9,12,15),(12,9,15),(8,15,17),(15,8,17)]
使用它的应用功能,您可以推广到任意元组:
quadrupels = (,,,) <$> as <*> as <*> as <*> as -- or call it liftA4
但:当然,仅此一项并不能消除重复。它只给你正确的对角化。也许您可以将monad comprehensions与Thomas的方法一起使用,或者只使用另一个mfilter
传递(在这种情况下限制为b /= c
)。
答案 1 :(得分:7)
列表理解是解决此类问题的好方法(也是简明扼要的方法)。首先,您知道您希望(a,b,c)
的所有组合可能满足a^2 + b^2 = c^2
- 有用的观察结果是(仅考虑正数),a <= c && b <= c
始终是这样的。
为了生成我们的候选人列表,我们可以说c
范围从1
到无穷大,a
和b
范围从1到c
。< / p>
[(a,b,c) | c <- [1..], a <- [1..c], b <- [1..c]]
要获得解决方案,我们只需要将您想要的等式添加为警卫:
[(a,b,c) | c <- [1..], a <- [1..c], b <- [1..c], a*a+b*b == c*c]
效率低,但输出正确:
[(3,4,5),(4,3,5),(6,8,10),(8,6,10),(5,12,13),(12,5,13),(9,12,15)...
有比盲测更多的原则方法可以解决这个问题。
答案 2 :(得分:3)
{ - 这取决于什么是“最小的”。但是,如果元组首先按其最大值进行比较,这里有一个“最小”概念的解决方案。数字,然后按他们的总和。 (当我在评论中写下文本时,您可以将我的整个答案复制并粘贴到文件中。)
我们稍后需要nub
。 - }
import Data.List (nub)
{ - 仅用于说明:2元组的简单案例。 - }
-- all the two-tuples where 'snd' is 'n'
tuples n = [(i, n) | i <- [1..n]]
-- all the two-tuples where 'snd' is in '1..n'
tuplesUpTo n = concat [tuples i | i <- [1..n]]
{ - 要获得所有结果,您需要将每个元组的翻转插入到流中。但是,让我们稍后再做,然后先概括一下。
构建任意长度的元组有点困难,因此我们将在列表上工作。如果它们的长度为'k',我称它们为'kList'。 - }
-- just copied from the tuples case, only we need a base case for k=1 and
-- we can combine all results utilizing the list monad.
kLists 1 n = [[n]]
kLists k n = do
rest <- kLists (k-1) n
add <- [1..head rest]
return (add:rest)
-- same as above. all the klists with length k and max number of n
kListsUpTo k n = concat [kLists k i | i <- [1..n]]
-- we can do that unbounded as well, creating an infinite list.
kListsInf k = concat [kLists k i | i <- [1..]]
{ -
下一步是旋转这些列表,因为到目前为止,最大的数字总是在最后一个位置。因此,我们只需查看所有旋转即可获得所有结果。在这里使用nub
无疑是尴尬的,你可以改进它。但如果没有它,所有元素相同的列表将重复k
次。
- }
rotate n l = let (init, end) = splitAt n l
in end ++ init
rotations k l = nub [rotate i l | i <- [0..k-1]]
rotatedKListsInf k = concatMap (rotations k) $ kListsInf k
{ - 剩下的就是将这些列表转换为元组。这有点尴尬,因为每个n元组都是一个单独的类型。但它当然是直截了当的。 - }
kListToTuple2 [x,y] = (x,y)
kListToTuple3 [x,y,z] = (x,y,z)
kListToTuple4 [x,y,z,t] = (x,y,z,t)
kListToTuple5 [x,y,z,t,u] = (x,y,z,t,u)
kListToTuple6 [x,y,z,t,u,v] = (x,y,z,t,u,v)
{ - 一些测试:
*Main> take 30 . map kListToTuple2 $ rotatedKListsInf 2
[(1,1),(1,2),(2,1),(2,2),(1,3),(3,1),(2,3),(3,2),(3,3),(1,4),(4,1),(2,4),(4,2),(3,4),
(4,3),(4,4),(1,5),(5,1),(2,5),(5,2),(3,5),(5,3),(4,5),(5,4),(5,5),(1,6),(6,1),
(2,6), (6,2), (3,6)]
*Main> take 30 . map kListToTuple3 $ rotatedKListsInf 3
[(1,1,1),(1,1,2),(1,2,1),(2,1,1),(1,2,2),(2,2,1),(2,1,2),(2,2,2),(1,1,3),(1,3,1),
(3,1,1),(1,2,3),(2,3,1),(3,1,2),(2,2,3),(2,3,2),(3,2,2),(1,3,3),(3,3,1),(3,1,3),
(2,3,3),(3,3,2),(3,2,3),(3,3,3),(1,1,4),(1,4,1),(4,1,1),(1,2,4),(2,4,1),(4,1,2)]
修改强> 的 我意识到有一个错误:当然,旋转有序列表是不够的。解决方案必须符合
的某些方面rest <- concat . map (rotations (k-1)) $ kLists (k-1) n
kLists
中的,但随后出现了重复输出的一些问题。我想你可以搞清楚。 ;-) - }
答案 3 :(得分:1)
这个答案是针对未知谓词的更普遍的问题。如果谓词是已知的,那么更有效的解决方案是可能的,就像其他人已经列出了基于知识的解决方案,您不需要针对给定的c迭代所有Int。
在处理无限列表时,您需要执行广度优先搜索解决方案。列表理解仅提供深度优先搜索,这就是您从未在原始代码中找到解决方案的原因。
counters 0 xs = [[]]
counters n xs = concat $ foldr f [] gens where
gens = [[x:t | t <- counters (n-1) xs] | x <- xs]
f ys n = cat ys ([]:n)
cat (y:ys) (x:xs) = (y:x): cat ys xs
cat [] xs = xs
cat xs [] = [xs]
main = print $ take 10 $ filter p $ counters 3 [1..] where
p [a,b,c] = a*a + b*b == c*c
counters
为指定数字范围内的值生成所有可能的计数器,包括无限范围。
首先,我们获得有效计数器组合的生成器列表 - 对于每个允许的数字,将其与较小尺寸的计数器的所有允许组合相结合。这可能导致生成器产生无限数量的组合。因此,我们需要均匀地从每台发电机借用。
所以gens
是一个生成器列表。可以将此视为以一位数开头的所有计数器的列表:gens !! 0
是以1
开头的所有计数器的列表,gens !! 1
是以{{1}开头的所有计数器的列表等等。
为了均匀地从每个发生器借用,我们可以转置生成器列表 - 这样我们就可以获得生成器的第一个元素列表,然后是生成器的第二个元素列表等。
由于生成器列表可能是无限的,我们不能转置生成器列表,因为我们可能永远不会看到任何生成器的第二个元素(对于无限数量的数字,我们有无穷大的数字)发电机组)。因此,我们从发生器“对角地”枚举元素 - 从第一个生成器获取第一个元素;然后从第一个发生器获取第二个元素,从第二个发生器获取第一个元素;然后从第一个生成器获取第三个元素,从第二个生成器获取第二个元素,从第三个生成器获取第一个元素,等等。这可以通过使用函数2
折叠生成器列表来完成,它将两个拉链在一起列表 - 一个列表是生成器,另一个列表是已经压缩的生成器 - 其中一个列表的开头通过向头部添加f
而向前偏移一步。这差不多是[]:
- 区别在于如果n或ys比另一个短,我们不会删除其他列表的其余部分。请注意,使用zipWith (:) ys ([]:n)
进行折叠将是转置。
答案 4 :(得分:1)
这实际上取决于你所说的“最小”的含义,但我假设你想要找到一个关于其最大元素的数字元组 - 所以(2,2)
小于(1,3)
(而标准Haskell排序是词典的。)
有一个包data-ordlist,其目的正是为了处理有序列表。它的功能mergeAll
(和mergeAllBy
)允许您将每个方向排序的二维矩阵组合成一个有序列表。
首先让我们在元组上创建一个所需的比较函数:
import Data.List (find)
import Data.List.Ordered
compare2 :: (Ord a) => (a, a) -> (a, a) -> Ordering
compare2 x y = compare (max2 x, x) (max2 y, y)
where
max2 :: Ord a => (a, a) -> a
max2 (x, y) = max x y
然后使用mergeAll
创建一个函数,该函数采用比较器,组合函数(两个参数中必须为monotonic)和两个排序列表。它使用函数组合来自两个列表的所有可能元素,并生成结果排序列表:
mergeWith :: (b -> b -> Ordering) -> (a -> a -> b) -> [a] -> [a] -> [b]
mergeWith cmp f xs ys = mergeAllBy cmp $ map (\x -> map (f x) xs) ys
使用此功能,根据最大值生成元组非常简单:
incPairs :: [(Int,Int)]
incPairs = mergeWith compare2 (,) [1..] [1..]
它的前10个元素是:
> take 10 incPairs
[(1,1),(1,2),(2,1),(2,2),(1,3),(2,3),(3,1),(3,2),(3,3),(1,4)]
当我们(例如)寻找其平方和等于65的第一对时:
find (\(x,y) -> x^2+y^2 == 65) incPairs
我们得到正确的结果(4,7)
(与(1,8)
相反,如果使用了词典排序)。
答案 5 :(得分:0)
对于这个答案,我会用“最小”来表示元组中数字的总和。
要按顺序列出所有可能的对,您可以先列出总和为2的所有对,然后列出总和为3的所有对,依此类推。在代码中
pairsWithSum n = [(i, n-i) | i <- [1..n-1]]
xs = concatMap pairsWithSum [2..]
Haskell没有使用模板Haskell处理n元组的工具,所以为了概括这一点,你必须切换到列表。
ntuplesWithSum 1 s = [[s]]
ntuplesWithSum n s = concatMap (\i -> map (i:) (ntuplesWithSum (n-1) (s-i))) [1..s-n+1]
nums n = concatMap (ntuplesWithSum n) [n..]
答案 6 :(得分:0)
这是另一种解决方案,可能是另一种略微不同的“最小”概念。我的命令只是“所有具有最大元素N的元组在所有具有最大元素N + 1的元组之前出现”。我写了对和三元组的版本:
gen2_step :: Int -> [(Int, Int)]
gen2_step s = [(x, y) | x <- [1..s], y <- [1..s], (x == s || y == s)]
gen2 :: Int -> [(Int, Int)]
gen2 n = concatMap gen2_step [1..n]
gen2inf :: [(Int, Int)]
gen2inf = concatMap gen2_step [1..]
gen3_step :: Int -> [(Int, Int, Int)]
gen3_step s = [(x, y, z) | x <- [1..s], y <- [1..s], z <- [1..s], (x == s || y == s || z == s)]
gen3 :: Int -> [(Int, Int, Int)]
gen3 n = concatMap gen3_step [1..n]
gen3inf :: [(Int, Int, Int)]
gen3inf = concatMap gen3_step [1..]
你不能把它归结为N元组,但只要你保持同质,你可以在使用数组时将其概括为一般。但我不想把我的大脑绑在那个结上。
答案 7 :(得分:0)
我认为这是最简单的解决方案,如果&#34;最小的&#34;定义为x + y + z,因为在积分值为毕达哥拉斯三角形的空间中找到第一个解后,无限列表中的下一个解更大。
take 1 [(x,y,z) | y <- [1..], x <- [1..y], z <- [1..x], z*z + x*x == y*y]
- &GT; [(4,5,3)]
它有一个很好的属性,它只返回一次对称唯一解决方案一次。 x和z也是无限的,因为y是无穷大的。
这不起作用,因为x的序列永远不会结束,因此你永远不会得到y的值,更不用说z了。最右边的生成器是最里面的循环。
take 1 [(z,y,x)|z <- [1..],y <- [1..],x <- [1..],x*x + y*y == z*z]
答案 8 :(得分:-1)
Sry,自从我做了haskell以来已经有一段时间了,所以我将用文字来形容它。
正如我在评论中指出的那样。无法在无限列表中找到最小的任何东西,因为总会有一个较小的东西。
你可以做的是,有一个基于流的方法,它采用列表并返回一个只有'有效'元素的列表,i。即满足条件的地方。让我们调用这个函数triangle
然后,您可以使用take n (triangle ...)
在某种程度上计算三角形列表,并从此n
元素中找到minium。