Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this permutations function work (Scala)?

I'm looking at Pavel's solution to Project Euler question 24, but can't quite work out how this function works - can someone explain what it's doing? Its purpose is to return the millionth lexicographic permutation of the digits 0 to 9.

def ps(s: String): Seq[String] = if(s.size == 1) Seq(s) else 
  s.flatMap(c => ps(s.filterNot(_ == c)).map(c +))

val r = ps("0123456789")(999999).toLong

I understand that when the input String is length 1, the function returns that character as a Seq, and I think then what happens is that it's appended to the only other character that was left, but I can't really visualise how you get to that point, or why this results in a list of permutations.

(I already solved the problem myself but used the permutations method, which makes it a fairly trivial 1-liner, but would like to be able to understand the above.)

like image 468
Luigi Plinge Avatar asked Dec 13 '22 12:12

Luigi Plinge


2 Answers

For each letter (flatMap(c => ...)) of the given string s, ps generates a permutation by permutating the remaining letters ps(s.filterNot(_ == c)) and prepending the taken letter in front of this permutation (map(c +)). For the trivial case of a one letter string, it does nothing (if(s.size == 1) Seq(s)).

Edit: Why does this work?

Let’s begin with shuffling a one letter string:

[a]
-> a   # done.

Now for two letters, we split the task into subtasks. Take each character in the set, put it to the first position and permutate the rest.

a [b]
-> b
b [a]
-> a

For three letters, it’s the same. Take each character and prepend it to each of the sub-permutations of the remaining letters.

a [b c]
-> b [c]
   -> c
-> c [b]
   -> b
b [a c]
-> a [c]
   -> c
-> c [a]
# ... and so on

So, basically the outermost function guarantees that each letter gets to the first position, the first recursive call guarantees the same for the second position and so on.

like image 98
Debilski Avatar answered Dec 28 '22 06:12

Debilski


Let's write it out in pseudocode:

for each letter in the string
  take that letter out
  find all permutations of what remains
  stick that letter on the front

Because it's working for each letter in the string, what this effectively does is move each letter in turn to the front of the string (which means that the first letter can be any of the letters present, which is what you need for a permutation). Since it works recursively, the remainder is every remaining permutation.

Note that this algorithm assumes all the letters are different (because filterNot is used to remove the selected letter); the permutations method in the collections library does not assume this.

like image 36
Rex Kerr Avatar answered Dec 28 '22 07:12

Rex Kerr