I coded a function to enumerate all permutations of a given list. What do you think of the code below?
def interleave(x:Int, l:List[Int]):List[List[Int]] = {
Given a Seq, one can already have permutations by invoking the permutations
method.
scala> List(1,2,3).permutations.mkString("\n")
res3: String =
List(1, 2, 3)
List(1, 3, 2)
List(2, 1, 3)
List(2, 3, 1)
List(3, 1, 2)
List(3, 2, 1)
Furthermore there is also a method for combinations
:
scala> List(1,2,3).combinations(2).mkString("\n")
res4: String =
List(1, 2)
List(1, 3)
List(2, 3)
Regarding your implementation I would say three things:
(1) Its good looking
(2) Provide an iterator (which is the std collections approach that allows to discard elements). Otherwise, you can get lists with 1000! elements which may not fit in memory.
scala> val longList = List((1 to 1000):_*)
longList: List[Int] = List(1, 2, 3,...
scala> permutations(longList)
java.lang.OutOfMemoryError: Java heap space
at scala.collection.immutable.List.$colon$colon(List.scala:67)
at .interleave(<console>:11)
at .interleave(<console>:11)
at .interleave(<console>:11)
(3) You should remove duplicated permutations (as observed by Luigi), since :
scala> permutations(List(1,1,3))
res4: List[List[Int]] = List(List(1, 1, 3), List(1, 1, 3), List(1, 3, 1), List(1, 3, 1), List(3, 1, 1), List(3, 1, 1))
scala> List(1,1,3).permutations.toList
res5: List[List[Int]] = List(List(1, 1, 3), List(1, 3, 1), List(3, 1, 1))
def permutator[T](list: List[T]): List[List[T]] = {
def _p(total: Int, list: List[T]): List[List[T]] = {
if (total == 0) {
// End of the recursion tree
list.map(List(_))
} else {
// Controlled combinatorial
// explosion while total > 0
for (i <- list;
j <- _p(total - 1, list))
yield { i :: j }
// It is a recursion tree to generate the
// permutations of the elements
// --------------------------------------
// total = 0 => _p returns 3 elements (A, B, C)
// total = 1 => _p returns 3 * 3 List(List(A, A)...
// total = 2 => _p returns 3 * 3 * 3 elements List(List(A, A, A)...
}
}
_p(list.length - 1, list)
}
permutator(List("A", "B", "C"))
// output:
List(A, A, A),List(A, A, B),List(A, A, C),List(A, B, A),List(A, B, B),
List(A, B, C),List(A, C, A),List(A, C, B),List(A, C, C),List(B, A, A),
List(B, A, B),List(B, A, C),List(B, B, A),List(B, B, B),List(B, B, C),
List(B, C, A),List(B, C, B),List(B, C, C),List(C, A, A),List(C, A, B),
List(C, A, C),List(C, B, A),List(C, B, B),List(C, B, C),List(C, C, A),
List(C, C, B),List(C, C, C)
Consider the difference here: your version
scala> permutations(List(1,1,2)) foreach println
List(1, 1, 2)
List(1, 1, 2)
List(1, 2, 1)
List(1, 2, 1)
List(2, 1, 1)
List(2, 1, 1)
The reference version:
scala> List(1,1,2).permutations foreach println
List(1, 1, 2)
List(1, 2, 1)
List(2, 1, 1)
I guess you are practicing your Scala programming skill. Here is another one, where the idea is to take different elements as head of sequence and repeats are removed through filter
. Complexity of the code shall be fine, since O(n)+O(n or maybe n^2)+O(n)*P(n-1) is dominated by O(n)*P(n-1), where P(n) is the permutation number and cannot be improved, .
def permute(xs:List[Int]):List[List[Int]] = xs match {
case Nil => List(List())
case head::tail => {
val len = xs.length
val tps = (0 to len-1).map(xs.splitAt(_)).toList.filter(tp => !tp._1.contains(tp._2.head))
tps.map(tp => permute(tp._1:::tp._2.tail).map(tp._2.head :: _)).flatten
}
}
This is an implementation which is based on the concept of cycle and a trivial implementation of permute with two elements. It does not handle duplicates and stack overflow aspect in the permute method
object ImmuPermute extends App {
def nextCycle(nums: List[Int]): List[Int] = {
nums match {
case Nil => Nil
case head :: tail => tail :+ head
}
}
def cycles(nums: List[Int]): List[List[Int]] = {
def loop(l: List[Int], acc: List[List[Int]]): List[List[Int]] = {
if (acc.size == nums.size)
acc
else {
val next = nextCycle(l)
loop(next, next :: acc)
}
}
loop(nums, List(nums))
}
def permute(nums: List[Int]): List[List[Int]] = {
nums match {
case Nil => Nil
case head :: Nil => List(List(head))
case first :: second :: Nil => List(List(first, second), List(second, first))
case _ => {
val cycledList = cycles(nums)
cycledList.flatMap { cycle =>
val h = cycle.head
val t = cycle.tail
val permutedList = permute(t)
permutedList map { pList =>
h :: pList
}
}
}
}
}
val l = permute(List(1, 2, 3, 4))
l foreach println
println(l.size)
}