2011-01-14 13 views
2

Ich programmiere in Java, aber ich könnte auch C++ (oder sogar Pseudo) Code übernehmen, kein Problem. Hier ist, was ich bin:Playlist Shuffle ein/aus

Ich habe so etwas wie eine Playlist wie List<MyPlayListItem> lsMyPlaylist. Jetzt möchte ich dem Benutzer die Möglichkeit geben, die Elemente zu mischen, aber dann auch wieder in die geordnete Liste zu kommen. Ich meine, sagen wir, der Benutzer ist im "Shuffle-Modus", der Spieler z.B. springt von Song 7 zu 5, aber dann schaltet der Benutzer den "Shuffle-Modus" aus, weil er Song 6 als nächstes hören möchte. Wie würden Sie dieses Problem angehen?

Ich habe einige Ideen:

  • zwei Listen zu verwenden, ein Original, ein gemischt (zu viel Speicher)
  • haben eine Liste von int ist, die ich mischen und dann als Index verwenden, um die Elemente zu erhalten (ein wenig besser, vielleicht)
  • verwenden, um eine Hash-Tabelle (die Lösung? ich, dass einige Hinweise brauchen könnte, obwohl)

Oh, und dies ist keine Hausaufgabe (ich wünsche, ich, dass das Alter war wieder :-D).

EDIT:

Ich habe gerade eine Implementierung wie folgt aus:

PlayList<E> implements List { 

    private List<E> lsObjs = null; 
    private List<Integer> lsIdxs = null; 

    boolean bShuffleMode = false; 
    int Pos = 0; 
} 

aber jetzt, ich denke an so etwas wie:

PlayListItem<E> { 

    int iNextItem = 0; 

} 

PlayList<PlayListItem> implements List { 

    private List<PlayListItem> lsObjs = null; 

    boolean bShuffleMode = false; 
    int Pos = 0; 

} 

Nicht sicher dieses ... Könnte immer noch einen Rat brauchen. Kann ich List auch implementieren, wenn ich die Objekte in der Liste spezifiziere? Hm ...

+0

+1 für "Ich wünschte, ich wäre wieder in diesem Alter". Tun wir nicht alle! –

Antwort

2

Ich nehme an, Sie haben ein mobiles Gerät, das nicht ein paar K übrig hat. Bei zwei Listen werden die Elemente der Liste, die viel größer sein werden, nicht dupliziert.

Fügen Sie den ursprünglichen Index als ein Feld zu dem MyPlayListItem hinzu. Nachdem Sie sie gemischt haben, können Sie sie mit einem Komparator im Index sortieren, um sie in die ursprüngliche Reihenfolge zurückzuversetzen. Hinweis: Wenn der Index nicht kleiner als 4 Byte ist, wird so viel Speicher belegt wie zwei Listen.

Auf einem 32-Bit-System verbrauchen ein int [] und eine List fast die gleiche Menge an Speicher. (ca. 16 Bytes Unterschied)

+0

Danke Peter. Ich könnte diesen Algorithmus in allen möglichen Fällen brauchen, deshalb möchte ich, dass es gut gemacht ist. Oh, und ich weiß bereits, dass ich es in einer Telefonanwendung als auch in Tat verwenden werde. Danke für deinen Vorschlag, dass ich auch etwas weg habe ... aber ich frage mich, ob eine Hashtable das Gleiche machen würde, deshalb zögere ich ein bisschen ... – AudioDroid

+0

Eine HashMap oder HashTable wird viel teurer sein als mit zwei ArraysLists. (enthält die gleichen Objekte in verschiedenen Ordnungen) –

+0

Ich lese Ihre Antwort und den obigen Kommentar noch einmal. Ich finde deine Antwort die bisher nützlichste, besonders deine Bemerkungen über das Gedächtnis. Vielen Dank. – AudioDroid

1

Wenn Speicher Ihre größte Einschränkung ist, können Sie einfach einen zufälligen Song wählen, der am Ende jedes Songs gespielt wird. Es bedeutet natürlich, dass Sie nicht unbedingt jeden Song abspielen müssen, bevor Sie die Liste erneut starten. :)

Wenn Sie jeden Song spielen müssen, bevor Sie mit der Wiedergabe von Songs beginnen, speichern Sie eine Bitstringlänge, die der Anzahl der Songs entspricht. Während Sie Songs abspielen, stellen Sie das diesem Index entsprechende Bit ein. Spielen Sie niemals einen Song ab, wenn das Bit gesetzt ist, und erhöhen Sie an diesem Punkt den Index, bis Sie ein Bit in der nicht gefundenen Zeichenfolge finden. Wenn alle Bits in der Zeichenfolge gesetzt sind, setzen Sie die Bit-Zeichenfolge zurück.

+0

Oh wow, diese Idee ist mir nie in den Sinn gekommen. Danke für das Teilen. In der Tat möchte ich jedes Lied spielen, bevor ich wieder von vorn anfange. – AudioDroid

+0

Hm. Ich versuche etwas "in der Mitte" zwischen Speicher und Rechenleistung zu finden. seggy weist auf den Nachteil Ihrer Lösung hin. Aber ich mag deine Idee, einfach zu inkrementieren, wenn das Bit bereits gesetzt ist. Wenn Sie jedoch 100 oder mehr Songs haben und sagen wir, dass nur Nummer 1 nicht gespielt wurde und Sie zufällig Pech haben und der Zufallsgenerator "2" zurückgibt, werden Sie die gesamte Liste inkrementieren. :-((Nicht, dass ich denke, dass du das nicht bemerkt hast. Ich wollte es nur Leuten zeigen, die herumkommen.) – AudioDroid

+0

Definitiv wahr, es ist ein Kompromiss, wo dies wenig zusätzlichen Speicherbedarf hätte.Ich nehme an, Sie sind auf einem Android-Gerät, am ehesten eine Java-Datenstruktur-basierte Lösung wäre besser geeignet. Der Bitstring-Ansatz wäre geeigneter für eingebettete oder ähnliches. – gravitron

0

Sie können zwei Elemente haben:
1) Liste der realen Objekte in einer Liste
2) Vektor mit ganzzahligen Indizes

Wenn die Shuffle aktiviert ist, können Sie Indizes mischen. Wenn es aus ist, sortiere Indizes. Verwenden Sie Indizes, wenn Sie auf Elemente in der Liste zugreifen.

etwas wie folgt aus:

std::list<MyListElement> elements = ...; 
std::vector<int> indexes = ...; 

// shuffle off 
sort(indexes.begin(), indexes.end()); 

// get elements 
elements[ indexes[ i ] ]; 
1

Was ist mit etwas einfach?

Verwenden Sie einen Indexzeiger in der Liste, sagen Sie eine Ganzzahl. Dies ist das aktuelle Lied, das gerade gespielt wird. Wenn sich der Player im Shuffle-Modus befindet, wird dieser Zeiger auf eine zufällige Zahl zwischen 0 und der Anzahl der Elemente in der Liste gesetzt - 1. Wenn sich der Player im sequenziellen Modus befindet, wird dieser Zeiger einfach inkrementiert.

Wenn Sie die Wiederholung von Liedern verhindern möchten, gibt es zwei Strategien. Eine besteht darin, eine Bitfolge zu verwenden, wie es eine andere Person oben vorgeschlagen hat. Der andere wäre, einen Satz zu verwenden. Überprüfen Sie jedes Mal, wenn der Zeiger gesetzt ist, auf Mitgliedschaft in der Gruppe. Wenn es bereits im Set ist, holen Sie sich einen anderen Index. Wenn nicht, dann füge es hinzu und spiele das Lied ab. Beide werden den gleichen Effekt erzielen.

Sie werden Probleme bekommen, wenn das Verhältnis von gespielten Liedern zu ungespielten Liedern hoch wird und ein zufälliger Index immer in der Liste ist.

+0

Das ist eine gute Idee, aber in der Tat - wie gesagt - mag ich nicht die Tatsache, dass mein Zufallsgenerator sehr beschäftigt sein wird, wenn - sagen wir mal - es nur einen Song von 100 (oder mehr) übrig ist. – AudioDroid

+0

Ein anderer Ansatz wäre, diese Logik umzukehren. Erstellen Sie eine Liste, die anfänglich alle Indizes von 0 bis Länge der Songliste enthält - 1. Der Indexzeiger wird auf die gleiche Weise wie der ursprüngliche Post festgelegt, indiziert jedoch nicht in die ursprüngliche Songliste, sondern indexiert in die Liste der Indizes . Wenn ein Index ausgewählt ist (entweder im Shuffle- oder im sequenziellen Modus), wird die Nummer im Index verwendet, um auf das Lied in der Liste zu verweisen, und wird dann entfernt. Dies kümmert sich um das Problem, das ich am Ende meines Posts erwähnt habe. – seggy

+0

Ja, das ist mehr oder weniger dasselbe wie das, was Peter vorgeschlagen hat. Ich denke, ich gehe dafür. Wenn ich die Indexliste getrennt habe, könnte ich eine Liste irgendeines Typs haben. Ich denke sogar daran, meinen eigenen Container/Template-Kurs zu schreiben ... – AudioDroid

2

Ich würde vorschlagen, dass Sie einen Hauptcontainer für eine Liste von Songs (Library) und einen Container pro Playlist haben. Natürlich sollte die Wiedergabeliste auf Elemente in der Bibliothek verweisen.

Danach gibt es viele Möglichkeiten zum Mischen, eine der besten, die ich mag, ist eine Liste von Liedern in einem Container zu haben, in dem man Lieder zufällig auswählt und ausgewählte Lieder entfernt. Es wird also zufällig sein, aber nicht wiederholendes Spiel.

Es ist eine lange Zeit her, seit ich in Java programmiert habe, also werde ich ein funktionierendes C++ - Beispiel geben. Ich hoffe, es ist einfach und selbsterklärend:

// --*-- C++ --*-- 

#include <ctime> 
#include <cassert> 
#include <cstdlib> 
#include <cstdio> 
#include <string> 
#include <vector> 
#include <list> 

struct Song 
{ 
    std::string name; 

    Song (const std::string & name) : 
     name (name) 
    { 
    } 

    void play() 
    { 
     printf ("Playing '%s'...\n", name.c_str()); 
    } 
}; 

typedef std::vector<Song> Library; 
typedef std::vector<Song *> Playlist; 
typedef std::vector<size_t> SongIDSet; 

int 
main() 
{ 
    srand (time (NULL)); 

    Library library; 

    library.push_back (Song ("Lady Gaga - Bad Romance")); 
    library.push_back (Song ("Rihanna - Only Girl")); 
    library.push_back (Song ("Nelly - Just a Dream")); 
    library.push_back (Song ("Animal - Neon Trees")); 
    library.push_back (Song ("Eminem ft. Rihanna - Love The Way You Lie")); 

    Playlist playlist; 

    for (Library::iterator it = library.begin(), 
      end_it = library.end(); it != end_it; ++it) 
    { 
     printf ("Added song -> %s\n", it->name.c_str()); 
     playlist.push_back (&(*it)); 
    } 

    SongIDSet shuffle; 
    for (size_t i = 0, end_i = playlist.size(); i < end_i; ++i) 
    { 
     shuffle.push_back (i); 
    } 

    size_t nowPlaying = 0; 

    while (!shuffle.empty()) 
    { 
     size_t songIndex = 0; 
      printf ("Shuffle? [Y/n] "); 
     switch (fgetc (stdin)) 
     { 
      case 'N': 
      case 'n': 
       songIndex = nowPlaying + 1; 
       fgetc (stdin); // Skip newline. 
       break; 
      case 'Y': 
      case 'y': 
       fgetc (stdin); // Skip newline. 
      default: 
      { 
       printf ("Shuffling...\n"); 
       size_t index = rand() % shuffle.size(); 
       assert (index >= 0); 
       assert (index < shuffle.size()); 
       songIndex = shuffle[index]; 
       shuffle.erase (shuffle.begin() + index); 
      } 
     } 
     assert (songIndex >= 0); 
     if (songIndex < playlist.size()) 
     { 
      nowPlaying = songIndex; 
      playlist[nowPlaying]->play(); 
     } 
     else 
     { 
      break; // Done playing.. Repeat maybe? 
     } 
    } 
} 

Hier ist ein Beispiel run/Ausgabe:

$ ./test 
Added song -> Lady Gaga - Bad Romance 
Added song -> Rihanna - Only Girl 
Added song -> Nelly - Just a Dream 
Added song -> Animal - Neon Trees 
Added song -> Eminem ft. Rihanna - Love The Way You Lie 
Shuffle? [Y/n] 
Shuffling... 
Playing 'Eminem ft. Rihanna - Love The Way You Lie'... 
Shuffle? [Y/n] 
Shuffling... 
Playing 'Nelly - Just a Dream'... 
Shuffle? [Y/n] 
Shuffling... 
Playing 'Rihanna - Only Girl'... 
Shuffle? [Y/n] 
Shuffling... 
Playing 'Animal - Neon Trees'... 
Shuffle? [Y/n] 
Shuffling... 
Playing 'Lady Gaga - Bad Romance'... 
$ ./test 
Added song -> Lady Gaga - Bad Romance 
Added song -> Rihanna - Only Girl 
Added song -> Nelly - Just a Dream 
Added song -> Animal - Neon Trees 
Added song -> Eminem ft. Rihanna - Love The Way You Lie 
Shuffle? [Y/n] 
S huffling... 
Playing 'Nelly - Just a Dream'... 
Shuffle? [Y/n] n 
Playing 'Animal - Neon Trees'... 
Shuffle? [Y/n] n 
Playing 'Eminem ft. Rihanna - Love The Way You Lie'... 
Shuffle? [Y/n] n 
+0

Danke. Ihre Lösung wurde vorgeschlagen. Aber beeindruckend, wie Sie das alles in nicht Zeit (+1) dafür in Code setzen. Und ich mag dich C++ "Stil". Warum deklarierst du Song eher eine Struktur als eine Klasse? Ich würde struct nur verwenden, wenn das "Ding" keine Mitgliedsfunktionen hat. Was ist deine Konvention? – AudioDroid

+0

@AudioDroid: Ich benutze struct nur aus Gründen der Einfachheit, keine Zugriffsebenen zu spezifizieren und keine Getter/Setter usw. zu erstellen. Natürlich sollte 'Song' eine Klasse sein. Das einzige technische Problem in diesem Code ist, dass das Entfernen von nichtletzten Elementen aus 'std :: vector' nicht sehr effizient ist. Es wäre besser, einen Container mit wahlfreiem Zugriff zu verwenden. –

2

Wie wäre es Nextsong Eigenschaft auf MyPlayListItem hinzufügen, die einen Verweis zum nächsten Lied in Originalplaylist halten . Jedes Mal, wenn der Benutzer die Wiedergabeliste mischt, wird die Liste gemischt, aber die ursprüngliche Wiedergabeliste bleibt erhalten. Aber natürlich brauchen Sie etwas, um einen Verweis auf die erste MyPlayListItem in Original-Playlist zu halten.

+0

Das ist auch eine nette Idee. Aber ich dachte mir, dass es schön ist, eine Vorlage zu haben: PlayList implementiert List {...}. So kann ich jede Art von Objekt in die Liste stellen. Das ist etwas Schönes zu haben. Dann funktioniert dein Ansatz nicht. Oder sehen Sie auch dafür eine Lösung? ... vielleicht PlayList >? Hm... – AudioDroid