制作列表,其子列表涵盖两个输入列表的所有组合

时间:2017-07-10 16:52:25

标签: scala

我正在尝试组合(请告诉我,如果我使用的术语可以改进),这样我们结果列表的每个元素都是两个输入列表的组合,结果列表涵盖所有可能的组合。

不允许元素移动,子列表的第一个元素是任一输入List的第一个元素,子列表的第二个元素是任一子列表的第二个元素,依此类推。

示例1:

输入:List(1,1,1)List(0,0,0)(我们可以假设列表长度相同)

输出:

List(List(1, 1, 1),
 List(1, 1, 0),
 List(1, 0, 1),
 List(0, 1, 1),
 List(1, 0, 0),
 List(0, 1, 0),
 List(0, 0, 1),
 List(0, 0, 0))

示例2:

输入:List(1,2,3)List(4,5,6)

输出:

List(List(1, 2, 3),
 List(1, 2, 6),
 List(1, 5, 3),
 List(1, 5, 6),
 List(4, 5, 6),
 List(4, 5, 3),
 List(4, 2, 3),
 List(4, 2, 6))

我目前的解决方案仅适用于简单案例(示例1):

  def makeCombinations(a: List[Int], b: List[Int]): List[List[Int]] = {
    val N = a.length
    (0 to N).map(x => a.slice(x, N) ::: b.slice(0, x)).flatMap(x => x.permutations).toList

  }

最好的方法是什么?

我不希望找到cross product,这会导致:

List(List(1, 4),
     List(1, 5),
     List(1, 6),
     List(2, 4),
     List(2, 5),
     List(2, 6),
     List(3, 4),
     List(3, 5),
     List(3, 6))

此问题与以下内容不同:Cross product in Scala

2 个答案:

答案 0 :(得分:4)

以下是查看它的一种方法:您希望将输入列表压缩在一起,然后您想要查看结果列表,flatMap沿途尝试所有组合。在List上有效执行此操作的方法是使用foldRight

def combine[A](xs: List[A], ys: List[A]): List[List[A]] =
  (xs zip ys).foldRight(List(List[A]()))(
    (xy, zss) => for (z <- List(xy._1, xy._2); zs <- zss) yield (z :: zs)
  )

在REPL尝试:

scala> Test.combine(List(1,2,3), List(4,5,6))
res0: List[List[Int]] = List(List(1, 2, 3), List(1, 2, 6), List(1, 5, 3),
List(1, 5, 6), List(4, 2, 3), List(4, 2, 6), List(4, 5, 3), List(4, 5, 6))

答案 1 :(得分:1)

这可能不如foldRight版本有效,但作为开头更容易理解:

//almost same as your List(0,0,0) List(1,1,1)
def combos(n: Int): List[List[Boolean]] = (1 to n).foldLeft(List(List.empty[Boolean])){
  case (acc, _) => acc.map(false :: _) ::: acc.map(true :: _)
}

//apply combo (like 1,1,0) to choose element from either of lists
def applyCombo(a: List[Int], b: List[Int])(combo: List[Boolean]): List[Int] = 
  a zip b zip combo map { case ((x, y), b) => if (b) x else y }

//get all combinations (as boolean) and apply them to a and b
def combine(a: List[Int], b: List[Int]) = combos(a.size) map applyCombo(a, b)

将它放在一起,摆脱中间List[List[Boolean]]并添加foldRight

def combine(aL: List[Int], bL: List[Int]) = (aL zip bL).foldRight(List(List.empty[Int])){
  case ((a, b), acc) => acc.map(a :: _) ::: acc.map(b :: _)
} 

实验:

a: List[Int] = List(1, 2, 3)
b: List[Int] = List(4, 5, 6)
res73_3: List[List[Int]] = List(
  List(1, 2, 3),
  List(1, 2, 6),
  List(1, 5, 3),
  List(1, 5, 6),
  List(4, 2, 3),
  List(4, 2, 6),
  List(4, 5, 3),
  List(4, 5, 6)
)

注意:

:::效率不高,如果您不关心组合的顺序,可以将其替换为acc.flatMap(x => List(a :: x, b :: x))