Best Shuffle
Wednesday, June 18, 2025
The “Best shuffle” is a Rosetta Code task that was not yet implemented in Factor:
Task
Shuffle the characters of a string in such a way that as many of the character values are in a different position as possible.
A shuffle that produces a randomized result among the best choices is to be preferred. A deterministic approach that produces the same sequence every time is acceptable as an alternative.
Display the result as follows:
original string, shuffled string, (score)
The score gives the number of positions whose character value did not change.
There are multiple ways to approach this problem, but the way that most solutions seem to take is to shuffle two sets of indices, and then iterate through them swapping the characters in the result if they are different.
I wanted to contribute a solution in Factor, using local variables and short-circuit combinators:
:: best-shuffle ( str -- str' )
str clone :> new-str
str length :> n
n <iota> >array randomize :> range1
n <iota> >array randomize :> range2
range1 [| i |
range2 [| j |
{
[ i j = ]
[ i new-str nth j new-str nth = ]
[ i str nth j new-str nth = ]
[ i new-str nth j str nth = ]
} 0|| [
i j new-str exchange
] unless
] each
] each
new-str ;
And we can write some code to display the result as requested:
: best-shuffle. ( str -- )
dup best-shuffle 2dup [ = ] 2count "%s, %s, (%d)\n" printf ;
And then print some test cases:
IN: scratchpad {
"abracadabra"
"seesaw"
"elk"
"grrrrrr"
"up"
"a"
} [ best-shuffle. ] each
abracadabra, raabaracdab, (0)
seesaw, easwse, (0)
elk, lke, (0)
grrrrrr, rrrrgrr, (5)
up, pu, (0)
a, a, (1)
This is reminiscent to the recent work I had done on derangements and generating a random derangement. While this approach does not generate a perfect derangement of the indices – and happens to be accidentally quadratic – it is somewhat similar with the additional step that we look to make sure not only are the indices different, but that the contents are different as well before swapping.