Scala二十四点游戏(6):实现全排列

jerry Scala 2015年11月25日 收藏

穷举法计算二十四的算法的重要一个步骤,是把数字进行全排列,比如对于一个三个数的列表List(1,2,3),其全排列如下:

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

解决这种问题的一个策略是采用“分而治之”的方法,首先把问题分解成小的问题,比如N个数的全排列可以分解成N-1的全排列再加1个数的排列,然后对每个小的问题给出解决方案。
由此前面可以写出如下的一个递归算法:

  1. def permutations(l:List[Int]):List[List[Int]] = {
  2. l match {
  3. case Nil => List(List())
  4. case (head::tail) =>
  5. for(p0 <- permutations(tail);i<-0 to (p0 length);(xs,ys)=p0 splitAt i) yield xs:::List(head):::ys
  6. }
  7. }

空列表的全排列为空,N个数的全排列为N-1个数的全排列和1个数的全排列,对于每个N-1的排列,依次插入剩下的一个数,就构成了一个新的全排列。
测试如下:

  1. scala> permutations(List(1,2,3)).mkString("\n")
  2. res3: String =
  3. List(1, 2, 3)
  4. List(2, 1, 3)
  5. List(2, 3, 1)
  6. List(1, 3, 2)
  7. List(3, 1, 2)
  8. List(3, 2, 1)

再看看1,1,2的情况:

  1. scala> permutations(List(1,1,3)).mkString("\n")
  2. res4: String =
  3. List(1, 1, 3)
  4. List(1, 1, 3)
  5. List(1, 3, 1)
  6. List(1, 3, 1)
  7. List(3, 1, 1)
  8. List(3, 1, 1)

有重复的排列,我们可以直接借助于List的distinct方法过滤掉重复的值。

  1. scala> permutations(List(1,1,3)).distinct.mkString("\n")
  2. res5: String =
  3. List(1, 1, 3)
  4. List(1, 3, 1)
  5. List(3, 1, 1)

这样全排列的算法就成了,其实List自身已经提供了permutations方法,不需要自行实现:-)

  1. scala> List(1,2,3,4).permutations.mkString("\n")
  2. res6: String =
  3. List(1, 2, 3, 4)
  4. List(1, 2, 4, 3)
  5. List(1, 3, 2, 4)
  6. List(1, 3, 4, 2)
  7. List(1, 4, 2, 3)
  8. List(1, 4, 3, 2)
  9. List(2, 1, 3, 4)
  10. List(2, 1, 4, 3)
  11. List(2, 3, 1, 4)
  12. List(2, 3, 4, 1)
  13. List(2, 4, 1, 3)
  14. List(2, 4, 3, 1)
  15. List(3, 1, 2, 4)
  16. List(3, 1, 4, 2)
  17. List(3, 2, 1, 4)
  18. List(3, 2, 4, 1)
  19. List(3, 4, 1, 2)
  20. List(3, 4, 2, 1)
  21. List(4, 1, 2, 3)
  22. List(4, 1, 3, 2)
  23. List(4, 2, 1, 3)
  24. List(4, 2, 3, 1)
  25. List(4, 3, 1, 2)
  26. List(4, 3, 2, 1)