I've been a zone leader with DZone since 2008, and I'm crazy about community. Every day I get to work with the best that JavaScript, HTML5, Android and iOS has to offer, creating apps that truly make at difference, as principal front-end architect at Avego. James is a DZone Zone Leader and has posted 639 posts at DZone. You can read more from them at their website. View Full User Profile

Thursday Code Puzzler: Playlist Shuffle Algorithm

09.06.2012
| 4055 views |
  • submit to reddit

Thursday is code puzzler day here at DZone. The idea is simple: solve the coding problem as efficiently as you can, in any language or framework that you find suitable.

Note: Even though there really is nothing stopping you from finding a solution to this on the internet, try to keep honest, and come up with your own answer.  It's all about the participation!

Do you have code puzzlers that you'd like to share with the DZone community?  If so, please submit here. 

A Reliable Playlist Shuffle Algorithm

One thing I hate is when an implementation of shuffle on a media player results in a song repeating sooner than I would like, or earlier than it should. The challenge today is to write a method that takes a list of Song objects (with title, album and artist fields) and ensures that the space between a song repeating is as much as possible without having the same pattern used if the shuffle goes into a second (or third) run. 

Catch up on all our previous puzzlers here.

Comments

Vijay Nathani replied on Sat, 2012/09/08 - 2:44am

Groovy Soluion:

@groovy.transform.Immutable class Song { String title } //album,artist,etc may be added
class Shuffle {
	private def runsTillNow = new LinkedList()
	private int RUN_HISTORY = 20
	def Shuffle(songList) { runsTillNow.add(songList) }
	private int differenceFromOlderRuns(songs) {
		int c = runsTillNow.indexOf(songs) + 1
		c?c:(RUN_HISTORY+1)	//return 1, if songs were just played; return 2, if songs were played before last run; so on
	}
	private int howFurtherIsSongJustPlayed(songs) {
		def s = runsTillNow.first.size()
		runsTillNow.first.collect{ s-- + songs.indexOf(it) }.min()
	}
	def getNextRun() { 
		runsTillNow.addFirst(runsTillNow.first.permutations().max { differenceFromOlderRuns(it) + howFurtherIsSongJustPlayed(it) })
		if (runsTillNow.size() > RUN_HISTORY) runsTillNow.pop()
		runsTillNow.first
	}
}
def s = new Shuffle(['t1','t2','t3','t4','t5'].collect { new Song(it) })
20.times { println s.nextRun }

Mohamed Al-habshi replied on Tue, 2012/09/11 - 5:32am

I've made two solutions in Java (Please don't mind the song names. I just made them up).

public class ShuffleSongs {

    static List<Song> songsList;

    public static void main(String[] args) {
        //init list
        initSongsList();
        System.out.println("Before Shuffle");
        printSongsList(songsList);
        
        //Solution # 1
        printSongsList(shuffle1(songsList));
        
        initSongsList();
        //Solution # 2
        printSongsList(shuffle2(songsList));
    }

    private static void initSongsList() {
        songsList = new LinkedList<Song>();
        songsList.add(new Song("Moon Light", "Nature", "AL"));
        songsList.add(new Song("Go Next", "Future", "B. J"));
        songsList.add(new Song("Realllllly", "K", "CCC"));
        songsList.add(new Song("Fear", "Hell G.", "SD"));
        songsList.add(new Song("My Dear", "Future", "B. J"));
        songsList.add(new Song("To the School", "K", "Mike"));
        songsList.add(new Song("My name is", "Future", "Khan"));
        songsList.add(new Song("How want some", "Hell G.", "S.Fire"));
        songsList.add(new Song("My Lord", "K", "Ali"));
    }
    
    private static void printSongsList(List<Song> list){
        for (int i = 0; i < list.size(); i++) {
            Song song = list.get(i);
            System.out.printf("# %d - Title: %13s | Album: %10s | Artist: %10s\n", i+1, song.title, song.album, song.artist);
        }
        
        System.out.println("--------------------------------------------------------");
    }
    
    private static List shuffle1(List list){
        List res = new LinkedList();
        int size = list.size();
        int rand = 0;
        Object[] temp = list.toArray();
        int count = 0;;
        
        while(count != size){
            rand = (int)(Math.random()*list.size());
            if(!res.contains(temp[rand])){
                res.add(temp[rand]);
                count++;
            }
        }
        
        return res;
    }
    
    private static List shuffle2(List list){
        if(list.size() == 1){
            return list;
        }else{
            int rand = (int)(Math.random()*list.size());
            Object o = list.get(rand);
            list.remove(rand);
            list = shuffle2(list);
            list.add(o);
            return list;
        }
    }
    
    private static class Song {
        private String title;
        private String album;
        private String artist;

        public Song() {
        }

        public Song(String title, String album, String artist) {
            this.title = title;
            this.album = album;
            this.artist = artist;
        }       
    }

Vijay Nathani replied on Tue, 2012/09/11 - 7:37am in response to: Mohamed Al-habshi

Is your solution really for the question put up here? How are you ensuring that a song just played is not getting repeated?

Joe Colburn replied on Thu, 2012/09/13 - 9:15pm

Here's the function:

$playlists = array();
$tracks = array();

function makePlaylist($tracks) {
    $played = array();

    while (count($tracks) > 0) {
        $rnd = array_rand($tracks);
        $played[] = $tracks[$rnd];

        unset($tracks[$rnd]);
    }

    return $played;
} 

 

 

And here's the rest I added in for track list building and testing the function: 

 

class Track {
    public function setTrack($track_info) {
        $this->title = $track_info["title"];
        $this->album = $track_info["album"];
        $this->artist = $track_info["artist"];

        return $this;
    }
}

for ($i=1; $i < 10; $i++) {
    $t = new Track();
    $tracks[$i] = $t->setTrack(array("title"=>"song $i","album"=>"album $i","artist"=>"artist $i"));
}

for ($i=1; $i < 8; $i++) {
    $playlists[] = makePlaylist($tracks);
}

foreach ($playlists as $key => $pl) {
    echo "<b>Playlist #$key</b><br />";

    foreach ($pl as $idx => $track) {
        echo " - ".$track->title." by ".$track->artist." from the album ".$track->album."<br />";
    }
}

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.