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.)
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With