生成列表的所有排列,包括不同的大小和重复的元素

时间:2018-11-05 15:40:20

标签: list haskell functional-programming permutation

我想创建函数genAllSize ::[a] -> [[a]],该函数接收一个列表l并生成按大小排序的所有列表,这些列表可以使用列表l的元素构建;即

> genAllSize [2,4,8] 
[[],[2],[4],[8],[2,2],[4,2],[8,2],[2,4],[4,4],[8,4],[2,8],[4,8],[8,8],[2,2,2],[4,2,2],[8,2,2], ...

你会怎么做?我想出了一个使用Data.List中的排列的解决方案,但我不想使用它。

5 个答案:

答案 0 :(得分:4)

  • 给出输入列表xs,以不确定的方式选择其前缀
  • 对于前缀中的每个元素,以不确定的方式将其替换为xs的任何元素

结果:

> xs = [2,4,8]
> inits xs >>= mapM (const xs)
[[],[2],[4],[8],[2,2],[2,4],[2,8],[4,2],[4,4],[4,8],[8,2],[8,4],
[8,8],[2,2,2],[2,2,4],[2,2,8],[2,4,2],[2,4,4],[2,4,8],[2,8,2],
[2,8,4],[2,8,8],[4,2,2],[4,2,4],[4,2,8],[4,4,2],[4,4,4],[4,4,8],
[4,8,2],[4,8,4],[4,8,8],[8,2,2],[8,2,4],[8,2,8],[8,4,2],[8,4,4],
[8,4,8],[8,8,2],[8,8,4],[8,8,8]]

答案 1 :(得分:3)

其他答案似乎有点复杂。我会这样:

> [0..] >>= flip replicateM "abc"
["","a","b","c","aa","ab","ac","ba","bb","bc","ca","cb","cc","aaa","aab",...

答案 2 :(得分:2)

嗯,我想您需要一个无限的循环子序列列表。一种幼稚的方式可能是

<div id="attachmentFileNameList1"></div>
<div id="attachmentFileNameList2"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

答案 3 :(得分:1)

一个简单而高效的选择:

genAllSize [] = [[]]
genAllSize [a] = iterate (a:) []
genAllSize xs =
  [] : [x:q|q<-genAllSize xs,x<-xs]

(感谢威尔·尼斯(Will Ness)进行了小而精的简化。)

此解决方案利用了以下事实:有效的解决方案列表为空或参数列表的元素被限制在较短的有效解决方案列表中。与Daniel Wagner的解决方案不同,该解决方案无需求助。我的测试表明,它在典型条件下的性能非常好。

为什么一个元素列表需要特殊情况?一般情况下,这样做的效果极差,因为它一遍又一遍地映射到同一列表,而没有对数减慢。

但是使用相同的参数对genAllSizes的调用有什么用呢?保存结果以增加共享会更好吗?

genAllSize [] = [[]]
genAllSize xs = p
  where
    p = [] : [x:q|q<-p,x<-xs]

实际上,在具有无限恒定时间内存的理论计算机上,这是最佳选择:在列表上遍历每个缺点要花费最坏的O(1)时间。实际上,要实现并保留很多条目只是一个好主意。否则,就会出现问题:大多数列表条目将被无限期保留,从而显着增加了内存驻留时间以及垃圾收集器需要完成的工作量。上面的非粗体共享版本仍然每个cons提供摊销 O(1)时间,但是它只需要很少的内存(对数而不是线性)。

示例

genAllSize "ab" =
 ["","a","b","aa","ba"
 ,"ab","bb","aaa","baa"
 ,"aba","bba","aab","bab"
 ,"abb","bbb","aaaa",...]

genAllSize "abc" =
  ["","a","b","c","aa","ba"
  ,"ca","ab","bb","cb","ac"
  ,"bc","cc","aaa","baa"
  ,"caa","aba","bba","cba"
  ,"aca",...]

显式选项

您还可以使用两个累加器:

genAllSize [] = [[]]
genAllSize [a] = iterate (a:) []
genAllSize (x:xs) = go ([], []) where
  go (curr, remain) = curr : go (step curr remain)
  step [] [] = ([x], [xs])
  step (_:ls) ((r:rs):rss) =
    (r:ls, rs:rss)
  step (_:ls) ([] : rs) =
    (x : ls', xs : rs')
    where
      !(ls', rs') = step ls rs

此版本跟踪每个位置的当前“单词”以及其余可用的“字母”。该性能总体上看似可比,但在内存驻留方面要好一些。这也很难理解!

答案 4 :(得分:0)

这将在每个长度内以与示例不同的顺序生成元素,但符合问题文本的定义。更改订单很容易-您必须用自己制作的稍有不同的运算符替换<*>

import Control.Applicative
import Control.Monad

rinvjoin :: Applicative both => both a -> both (both a)
rinvjoin = fmap pure

extendBranches options branches = (<|>) <$> options <*> branches
singletonBranchExtensions = rinvjoin

genAllSize [] = []
genAllSize xs = join <$> iterate (extendBranches extensions) $ initialBranches
  where extensions = singletonBranchExtensions xs
        initialBranches = pure empty