scala - Scala 中的组合数学 : How to iterate/enumerate all possibilities to merge multiple sequences/lists (riffle shuffle permutations)

标签 scala recursion combinations permutation combinatorics

更新的问题:

在我原来的问题中,我不知道如何引用以下问题。为了澄清我的问题,我添加了 following illustration from Wikipedia :

Riffle Shuffle

原来这个问题也是以此类比命名的:Riffle shuffle permutations 。基于这个术语,我的问题就变成了:在多副牌的一般情况下,如何迭代/枚举所有 Riffle Shuffle 排列?

原始问题:

假设我们有多个序列,并且我们希望将这些序列合并为一个序列。生成的序列应保留原始序列的顺序。考虑通过从任意(随机)中随机抽取一张卡片,将多叠卡片(例如 Seq[Seq[T]])合并为一个单叠(Seq[T]) ) 堆。所有输入堆栈应完全合并到结果堆栈中。我怎样才能迭代或枚举这样一个结果序列的所有可能的组合?

澄清一下:如果我有三个堆栈 A、B、C(每个堆栈有 5 个元素),那么我不仅想要这些堆栈的六种可能的排列,例如“所有 A、所有 B、所有 C”和“所有A,所有C,所有B”等。我宁愿想要所有可能的组合,例如“1. of A,1. of B,2. of A,1. of C,3. of A,2 . B,...”。

由于我今天有点不舒服,我的第一个方法非常丑陋,而且还会产生重复:

def enumerateCompositions[T](stacks: Seq[Seq[T]], prefix: Seq[T]): Seq[Seq[T]] = {
  if (stacks.length == 0) return {
    Seq(prefix)
  }
  stacks.zipWithIndex.flatMap{ case (stack, stackInd) =>
    if (stack.length > 0) {
      val stacksWithHeadRemoved = stacks.indices.map{ i =>
        if (i != stackInd) stacks(i) else stacks(i).drop(1)
      }
      enumerateCompositions(stacksWithHeadRemoved, prefix :+ stack.head)
    } else {
      val remainingStacks = stacks.indices.filterNot(_ == stackInd).map(i => stacks(i))
      enumerateCompositions(remainingStacks, prefix)
    }
  }
}

知道如何使其更加优雅并消除重复项吗?

最佳答案

我们将此操作称为“步枪”。这是一个干净的 idomatic 解决方案:

def allRiffles[T](stack1: List[T], stack2: List[T]): List[List[T]] =
  (stack1, stack2) match {
    case (x :: xs, y :: ys) => {
      allRiffles(xs, stack2).map(x :: _) ++
      allRiffles(stack1, ys).map(y :: _)
    }
    case _ => List(stack1 ++ stack2) // at least one is empty
  }

def allRifflesSeq[T](stacks: Seq[List[T]]): List[List[T]] =
  stacks.foldLeft(List(List[T]())) { (z, x) => 
    z.flatMap(y => allRiffles(y, x))
  }

allRiffles 将产生两个堆栈的所有可能的 riffling。 allRifflesSeq 将采用一系列堆栈并使用折叠生成所有可能的 riffling。例如,如果给定栈 A、B 和 C,则它首先生成 A 和 B 的所有可能的 riffling,然后将 C riffles 到每个 riffling 中。

请注意,allRiffles 消耗的堆栈空间与最短堆栈的长度成正比,而 allRifflesSeq 消耗的堆栈空间受最长堆栈长度的限制。此外,返回的列表可能很大(组合爆炸)并消耗大量堆空间。基于 Iterator 的解决方案更安全,但不太美观:

def allRiffles[T](stacks: List[List[T]]): Iterator[List[T]] = new Iterator[List[T]] {
  type Frame = (List[List[T]], List[List[T]], List[T])
  var stack = List[Frame]((Nil, stacks, Nil))

  var ready = false
  var cachedHasNext: Boolean = _
  var cachedNext: List[T] = _

  def computeNext: Unit = {
    while (stack.nonEmpty) {
      val (doneStacks, stacks, prefix) :: stackTail = stack
      stack = stackTail
      stacks match {
        case Nil => {
          cachedNext = prefix.reverse
          cachedHasNext = true
          return
        }
        case Nil :: rest =>
          stack ::= (doneStacks, rest, prefix)
        case (xs@(x :: xtail)) :: rest =>
          if (rest.nonEmpty)
            stack ::= (xs :: doneStacks, rest, prefix)
          val newStacks = doneStacks.reverse ++ (if (xtail.isEmpty) rest
                                                   else xtail :: rest)
          stack ::= (Nil, newStacks, x :: prefix)

      }
    }
    cachedHasNext = false
  }

  def ensureReady = {
    if (!ready) {
      computeNext
      ready = true
    }
  }

  def next = {
    ensureReady
    if (cachedHasNext) {
      val next = cachedNext
      ready = false
      next
    } else Iterator.empty.next
  }

  def hasNext = {
    ensureReady
    cachedHasNext
  }
}

关于scala - Scala 中的组合数学 : How to iterate/enumerate all possibilities to merge multiple sequences/lists (riffle shuffle permutations),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24287628/

相关文章:

scala - 将 RDD 拆分为没有重复值的 RDD

scala - 无法在已停止的 SparkContext 上调用方法

algorithm - 该算法的最坏情况运行时间(如何证明)?

具有递归回溯返回错误的c数独求解器

algorithm - 将数组拆分为 s 个具有唯一元素的子集

java - 从 Java 访问继承自通用 Java 基类的 Scala 对象

c++ - G++ 编译器不允许递归?

python - python中带有变量的新行

Python生成所有可能的矩阵组合

Scala特征和结构类型: can a trait extend a structural type and then call super?