无限列表的 Haskell 笛卡尔积

Posted

技术标签:

【中文标题】无限列表的 Haskell 笛卡尔积【英文标题】:Haskell cartesian product of infinite lists 【发布时间】:2011-10-31 18:16:12 【问题描述】:

我想从一个基对生成一个向量空间,它看起来像:

genFromPair (e1, e2) = [x*e1 + y*e2 | x <- [0..], y <- [0..]]

但是,当我检查输出时,似乎我得到了 [0, e2, 2*e2,...](即 x 永远不会超过 0)。当我考虑如何编写代码来执行此列表理解时,哪种是有意义的。

我编写了一些代码来从原点扩展“shell”(首先是范数为 0 的整数,然后是范数 1,然后是范数 2...),但这有点烦人,并且是 Z^2 特有的 - 我'd 必须为 Z^3 或 Z[i] 等重写它。有没有更清洁的方法?

【问题讨论】:

这就像证明有理数是可数的。而不是去 (1,1), (1,2), (1,3), ..., (2,1), ...,它永远不会让你进入第二行,而是按固定的总和递增:( 1,1); (1,2), (2,1); (1,3), (2,2), (3,1); ... @Kerrek SB:谢谢,这就是我试图通过说我采取“扩展外壳”来表明的意思。我只是想知道是否有一种haskelly方式。 我不知道 Haskell,但在 C 中它会是一个双循环:for (sum = 1; ; ++sum) for (i = 1; i &lt; sum; ++i) print (i, sum - i); @Kerrek:在 Haskell 中:[(x, s-x) | s &lt;- [0..], x &lt;- [0..s]] @hammar:谢谢 - 令人惊讶的自我描述 :-) 【参考方案1】:

data-ordlist 包有一些函数对于处理有序的无限光照非常有用。其中之一是mergeAllBy,它使用一些比较函数组合了一个无限列表。

然后的想法是构建一个无限的列表列表,这样y 在每个列表中都是固定的,而x 会增长。只要我们能保证每个列表都是排序的,并且列表的头部是排序的,按照我们的排序,我们就得到一个合并的排序列表。

这是一个简单的例子:

import Data.List.Ordered
import Data.Ord

genFromPair (e1, e2) = mergeAllBy (comparing norm) [[x.*e1 + y.*e2 | x <- [0..]] | y <- [0..]]

-- The rest just defines a simple vector type so we have something to play with
data Vec a = Vec a a
    deriving (Eq, Show)

instance Num a => Num (Vec a) where
    (Vec x1 y1) + (Vec x2 y2) = Vec (x1+x2) (y1+y2)
    -- ...

s .* (Vec x y) = Vec (s*x) (s*y)     
norm (Vec x y) = sqrt (x^2 + y^2)

在 GHCi 中尝试这个我们得到了预期的结果:

*Main> take 5 $ genFromPair (Vec 0 1, Vec 1 0)
[Vec 0.0 0.0,Vec 0.0 1.0,Vec 1.0 0.0,Vec 1.0 1.0,Vec 0.0 2.0]

【讨论】:

曼哈顿标准不是更有效率吗? ;) @Rotsor:可能 :) 但这只是一个玩具示例,您可以使用任何您想要的顺序,只要列表和列表的头部遵循该顺序。 有了延续 monad 和 monad 推导式,这几乎可以像原始代码一样编写:gist.github.com/1162126【参考方案2】:

您可以将您的空间视为一棵树。在树的根部选择第一个元素,在其子节点中选择第二个元素..

这是使用 ListTree 包定义的树:

import Control.Monad.ListT
import Data.List.Class
import Data.List.Tree
import Prelude hiding (scanl)

infiniteTree :: ListT [] Integer
infiniteTree = repeatM [0..]

spacesTree :: ListT [] [Integer]
spacesTree = scanl (\xs x -> xs ++ [x]) [] infiniteTree

twoDimSpaceTree = genericTake 3 spacesTree

它是一棵无限树,但我们可以例如按 DFS 顺序枚举它:

ghci> take 10 (dfs twoDimSpaceTree)
[[],[0],[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7]]

你想要的顺序,用树的话来说,是最佳优先搜索无限树的一种变体,其中假设树节点的子节点是排序的(你不能像往常一样比较所有节点的子节点最佳优先搜索,因为其中有无数个)。幸运的是,这个变体已经实现了:

ghci> take 10 $ bestFirstSearchSortedChildrenOn sum $ genericTake 3 $ spacesTree
[[],[0],[0,0],[0,1],[1],[1,0],[1,1],[0,2],[2],[2,0]]

您可以使用任何您喜欢的规范来扩展 shell,而不是上面的 sum

【讨论】:

【参考方案3】:

使用来自CodeCatalog 的diagonal sn-p:

genFromPair (e1, e2) = diagonal [[x*e1 + y*e2 | x <- [0..]] | y <- [0..]]

diagonal :: [[a]] -> [a]
diagonal = concat . stripe
    where
    stripe [] = []
    stripe ([]:xss) = stripe xss
    stripe ((x:xs):xss) = [x] : zipCons xs (stripe xss)

    zipCons [] ys = ys
    zipCons xs [] = map (:[]) xs
    zipCons (x:xs) (y:ys) = (x:y) : zipCons xs ys

【讨论】:

【参考方案4】:

借鉴 hammar 的回复:他的方法似乎很容易扩展到更高的维度:

Prelude> import Data.List.Ordered
Prelude Data.List.Ordered> import Data.Ord
Prelude Data.List.Ordered Data.Ord> let norm (x,y,z) = sqrt (fromIntegral x^2+fromIntegral y^2+fromIntegral z^2)
Prelude Data.List.Ordered Data.Ord> let mergeByNorm = mergeAllBy (comparing norm)
Prelude Data.List.Ordered Data.Ord> let sorted = mergeByNorm (map mergeByNorm [[[(x,y,z)| x <- [0..]] | y <- [0..]] | z <- [0..]])
Prelude Data.List.Ordered Data.Ord> take 20 sorted
[(0,0,0),(1,0,0),(0,1,0),(0,0,1),(1,1,0),(1,0,1),(0,1,1),(1,1,1),(2,0,0),(0,2,0),(0,0,2),(2,1,0),(1,2,0),(2,0,1),(0,2,1),(1,0,2),(0,1,2),(2,1,1),(1,2,1),(1,1,2)]

【讨论】:

以上是关于无限列表的 Haskell 笛卡尔积的主要内容,如果未能解决你的问题,请参考以下文章

如何迭代列表的笛卡尔积

在 vb.net 中查找列表值的所有组合(笛卡尔积)

DataFrame和列表的笛卡尔积

python中带有元组列表的字符串列表的条件笛卡尔积的单线

一组python的幂集和笛卡尔积

给字典的笛卡尔积