Reputation: 1821
As this question is dedicated to the Scala programming language, a solution in Java would do just as well. One minor thing to notice, however, is that in a Scala solution tail recursion (Scaladocs) is definitely favorable over any other, and in Java, a non-recursive solution is favorable. This is due to the fact that it needs work on quite large structures of data so that each item contained by the "preferably lazy Stream or similar" can be handled accordingly, without receiving a StackOverflowError (Javadocs) (how ironic).
The type of data that should be permuted is the Scala List (Scaladoc), but it’s completely alright to respond with a solution using Java arrays, and a somewhat intricate looping structure.
In Scala, the following would be a completely valid way to retrieve all permutations of given collection type. This following approach returns an iterator containing all the permutations, in a lazy manner:
val permutations = List(1, 2, 3, 4, 5, 6, 7).permutations.take(5)
This would, as mentioned before, yield an iterator containing the permutations of that particular List. Since it’s lazy, we’re able to take only five from the sequence. This is fine, from my understanding, as long as we are not calling toString on the iterator, as that would result in the iterator computing all data available and returning it as a String.
Although lazy, this does not happen to solve my problem. What I’m after is with the following:
// List of lists
val lists = List(
List(1, 2, 3),
List(3, 2),
List(4, 3, 2, 4))
Calculate all the possible permutations of the inner List(s), whilst maintaining the same ordering for the List(s) inside the outer List, with the same number of elements contained by each List inside the outer List. Meaning, the inner List(s) should be permuted, in all ways possible, together with the other inner List(s) as if though they had been flattened, whilst still maintaining the same ordering and containing the same number of elements as before.
One permutation might thus yield:
List(List(1, 3, 2), List(3, 2), List(3, 4, 4, 2))
It appears as though I’m not quite clever enough to conquer the concepts listed here on my own. Any pointers, if not complete code, would be highly appreciated! Should you have any questions, do write a comment and I’ll do my very best to clarify, either in a comment or by making minor edits to the already existing question.
If you have an answer to this question, but in a language not listed by this question, feel free to attempt to answer nonetheless, it’s mainly the concept of this type of permutations I’m after!
Upvotes: 3
Views: 813
Reputation: 51271
If the size of the outer List
is known and constant then it can be done as follows.
val ls = List(List(1, 3, 2), List(7, 8), List(9, 4, 4, 5))
val allPerms: Iterator[List[List[Int]]] = for {
a <- ls(0).permutations
b <- ls(1).permutations
c <- ls(2).permutations
} yield List(a,b,c)
In this example the resulting Iterator
has 144 elements, which is what we should expect: 6 X 2 X 12 = 144
If the outer List
length is fluid then the task is a bit more complicated.
update
In this case "more complicated" means I had to work it over for a while before coming up with a possible solution.
def getAllPerms[A]( in: List[List[A]]
, acc: List[List[A]] = List()
): Iterator[List[List[A]]] = in match {
case end :: Nil => end.permutations.map(acc :+ _)
case hd :: tl => hd.permutations.flatMap(x => getAllPerms(tl, acc :+ x))
case Nil => Iterator() // just to be complete
}
This is recursive but not tail recursive. It does pass my limited set of tests. Give it a try.
Upvotes: 1
Reputation: 37832
Tail-recursive, lazily evaluated solution:
@tailrec
def tailRecCombineWith(permutatedLists: Iterator[List[List[Int]]], remainingLists: List[List[Int]]): Iterator[List[List[Int]]] = remainingLists match {
case Nil => permutatedLists
case head :: tail => tailRecCombineWith(for {
a: List[List[Int]] <- permutatedLists
b: List[Int] <- head.permutations
} yield a :+ b, tail)
}
val result: Iterator[List[List[Int]]] =
tailRecCombineWith(lists.head.permutations.map(List(_)), lists.tail)
Upvotes: 1