在我原来的问题中,我不知道如何参考以下问题。为了澄清我的问题,我添加了following illustration from Wikipedia:
事实证明,这个问题也是以这个类比命名的:Riffle shuffle permutations。根据这个术语,我的问题就变成:如何在多层甲板的一般情况下迭代/枚举所有的riffle shuffle排列?
假设我们给出了多个序列,我们希望将这些序列合并为一个单一的序列。生成的序列应保留原始序列的顺序。考虑通过从任意(随机)堆栈中随机抽取卡片将多个堆栈卡(例如Seq[Seq[T]]
)合并到一个堆栈(Seq[T]
)中。所有输入堆栈应完全合并到生成的堆栈中。如何迭代或枚举这样一个结果序列的所有可能成分?
澄清一下:如果我有三个堆栈A,B,C(每个都有5个元素),那么我不仅希望这些堆栈的六种可能的排列方式如“所有A,所有B,全部C”和“所有的A,所有的C,所有的B”等。我宁愿想要所有可能的组合,如“1. A,1。B,2。A,1。C,3。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)
}
}
}
任何想法如何让这更优雅,摆脱重复?
答案 0 :(得分:2)
让我们把这个操作称为“反叛”。这是一个干净的自我解决方案:
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。例如,如果{(1}}给出了堆栈A,B和C,它首先产生A和B的所有可能的riffling,然后将C反复进入每个riffling。
请注意,allRifflesSeq
消耗与最短堆栈长度成比例的堆栈空间,allRiffles
消耗由最长堆栈长度限制的堆栈空间。此外,返回的列表可能是巨大的(组合爆炸)并消耗大量的堆空间。基于allRifflesSeq
的解决方案更安全,但不那么漂亮:
Iterator
答案 1 :(得分:1)
我用Java写的。代码如下。
import java.util.*;
import java.io.*;
public class RiffleShufflePermutation {
protected static ArrayList<String> a1 = null;
protected static ArrayList<String> a2 = null;
protected static ArrayList<ArrayList<String>> a = new <ArrayList<ArrayList<String>>();
private static int getStartingPosition(ArrayList<String> inA, String inS) {
for(String s : inA)
if (s.equals(inS))
return inA.indexOf(s)+1;
return 0;
}
private static void shootRiffle(int previous, int current) {
ArrayList<ArrayList<String>> newAA = new ArrayList<ArrayList<String>>();
ArrayList<String> newA;
int start;
for(ArrayList<String> al : a) {
start = (previous < 0)?0:getStartingPosition(al,a2.get(previous));
for(int i=start; i<=al.size(); i++) {
newA = new ArrayList<String>();
newA.addAll(al);
newA.add(i,a2.get(current));
newAA.add(newA);
}
}
a.clear();
a.addAll(newAA);
}
public static void main(String[] args) {
a1 = new ArrayList(Arrays.asList("a1","a2","a3"));
a2 = new ArrayList(Arrays.asList("b1","b2"));
a.add(a1);
for (int i=0; i<a2.size(); i++)
shootRiffle(i-1,i);
int i = 0;
for (ArrayList<String> s : a)
System.out.println(String.format("%2d",++i)+":"+s);
}
}
这是输出:
1:[b1, b2, a1, a2, a3]
2:[b1, a1, b2, a2, a3]
3:[b1, a1, a2, b2, a3]
4:[b1, a1, a2, a3, b2]
5:[a1, b1, b2, a2, a3]
6:[a1, b1, a2, b2, a3]
7:[a1, b1, a2, a3, b2]
8:[a1, a2, b1, b2, a3]
9:[a1, a2, b1, a3, b2]
10:[a1, a2, a3, b1, b2]
希望这很有用。