【问题标题】:Cartesian product "generator" (not list) in HaskellHaskell中的笛卡尔积“生成器”(未列出)
【发布时间】:2016-02-25 06:10:51
【问题描述】:

我是函数式编程的新手,我正在尝试找出一个函数,它可以递归地生成一组列表的笛卡尔积的元素。

我正在寻找的功能与sequence 完全一样(如此处所述:Calculate n-ary Cartesian Product),只是我不想将整个内容表达为一个列表。 我目前正在使用sequence 并遇到此处描述的问题的变体:Summing a large list of numbers is too slow

例如,sequence [[1,2,3],[1,2,3]] 产生 [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3]]。处理每个组合是完全可以接受的(即[1,2]等,作为一个列表,我只是想避免建立长的外部列表,而是在计算数据时递归处理数据。我将如何去做?

我目前正在做类似于这个快速 ghci 示例的事情:

> let stuff = sequence $ replicate 10 [0..9]
> let morestuff = map (sum . take 2 . reverse . sort) stuff
> sum morestuff

最后一个命令太慢了。

【问题讨论】:

  • 在计算数据时递归处理数据”到底是什么意思?这正是 Haskell 中惰性求值的工作原理。
  • 最后一个命令将强制计算 each 组合(其中有 10^10 个) - 我认为你应该专注于你的算法而不是这里的评估细节首先
  • 例如:对于每个组合,您只使用两个最大的数字并将它们相加 - 所以您不需要购物车。完全没有产品(您只需再次排序)......只需寻找一种以已经排序的方式生成它们的方法(并将总和乘以之后的排列数)......我认为这实际上更像是一个数学问题(这是一个欧拉项目吗?)我不想破坏太多,因为它可能是一些竞赛^^
  • 顺便说一句:即使您不知道该怎么做,您也可以从优化 sum . take 2 .reverse . sort 的东西开始 - 即使您只遍历列表一次并寻找两个最大的元素并求和起来,你会节省很多
  • 尝试在每个let 之间强制列出列表,您会发现在所有阶段都花费了很多时间——懒惰只是意味着您只能在除非你早点强迫,否则结束。在 repl 处强制执行的一种方法是使用来自Control.DeepSeqrnf。也就是说,您正在生成和汇总大约 10^10 个东西,也就是说 100 亿。因此,无论您如何编写代码,我都无法想象这会顺利进行。

标签: performance haskell functional-programming cartesian-product


【解决方案1】:

将我的评论移至答案:

最后一行显得特别慢的唯一原因是它迫使所有其他行的工作,否则这些工作是懒惰的。因此,每个子列表 都会根据需要进行处理(尽管每个子列表的排序都会强制整个子列表)。

要观察到所有阶段都需要时间,而不仅仅是最后一个阶段,我们可以在中间结果上使用 rnf from Control.DeepSeq 来强制它们进行。

但是生成和汇总大约 100 亿个事物需要一些时间。正如其他人所观察到的那样,在这种情况下,您应该更多地考虑一种更聪明的方法来获得您想要的结果(即可以在不改变结果的情况下删除或转换哪些中间步骤,以及您可以利用哪些对称性),而不仅仅是更有效地进行暴力破解。

【讨论】:

    猜你喜欢
    • 2016-05-07
    • 2016-10-08
    • 2021-09-05
    • 2022-11-17
    • 2015-12-05
    • 1970-01-01
    • 2021-05-06
    • 2023-04-10
    • 2011-02-06
    相关资源
    最近更新 更多