I have a huge music playlist and, while some artists have many albums, others have just one song. I wanted to sort the playlist so the same artist won't play twice in a row, or his songs won't end up mostly in the beginning or end of the playlist.Example playlist:$ cat /tmp/playlist.m3uAnna A. - Song 1Anna A. - Song 2I--Rock - Song 1John B. - Song 1John B. - Song 2John B. - Song 3John B. - Song 4John B. - Song 5Kyle C. - Song 1U--Rock - Song 1Output from sort -R or shuf:$ sort -R /tmp/playlist.m3uAnna A. - Song 1 #U--Rock - Song 1Anna A. - Song 2 # Anna's songs are all in the beginning.John B. - Song 2I--Rock - Song 1John B. - Song 1Kyle C. - Song 1John B. - Song 4 #John B. - Song 3 #John B. - Song 5 # Three of John's songs in a row.What I am expecting:$ some_command /tmp/playlist.m3uJohn B. - Song 1Anna A. - Song 1John B. - Song 2I--Rock - Song 1John B. - Song 3Kyle C. - Song 1Anna A. - Song 2John B. - Song 4U--Rock - Song 1John B. - Song 5
Shuffle file randomly with some additional constraints
text processing;sort;random
If I had to apply that shuffling to a deck of playing card, I think I'd first shuffle the deck, then display the cards in a row before my eyes and processing from left to right, wherever there are adjacent clubs or heart... move all but one of those at random somewhere else (though not next to another one of the same type).For example, with a hand like               After basic shuffling:     <  >< >                      1  2       3two groups of adjacent spades, we need to relocate 1, 2 and 3. For 1, the choices are:                                                       We pick one at random from those 4. Then we repeat the process for 2 and 3.Implemented in perl that would be:shuf list | perl -e '  @songs = map {/(.*?)-/; [$1,$_]} <>;  for ($i = 0; $i < @songs; $i++) {    if (($author = $songs[$i]->[0]) eq $previous) {      my @reloc_candidates, $same;      for($j = 0; $j < @songs; $j++) {        # build a list of positions where we could move that song to        if ($songs[$j]->[0] eq $author) {$same = 1} else {          push @reloc_candidates, $j unless $same;          $same = 0;        }      }      push @reloc_candidates, $j unless $same;      if (@reloc_candidates) {        # now pick one of them at random:        my $chosen = $reloc_candidates[int(rand(@reloc_candidates))];        splice @songs, $chosen - ($chosen > $i), 0, splice @songs, $i, 1;        $i -= $chosen > $i;      }    }    $previous = $author;  }  print map {$_->[1]} @songs'It will find a solution with non-adjacent artists if it exists (unless more than half the songs are from the same artist), and should be uniform AFAICT.