Vorrei scrivere un algoritmo "ultimate shuffle" per ordinare la mia collezione di mp3


33

Sto cercando suggerimenti pseudocodici per ordinare i miei file mp3 in modo da evitare la ripetizione di titoli e artisti . Ascolto crooner: Frank Sinatra, Tony Bennett, Ella Fitzgerald ecc. Che cantano vecchi standard. Ogni artista registra molte delle stesse canzoni: Fly Me To The Moon, The Way You Look Tonight, Stardust ecc. Il mio obiettivo è quello di organizzare le canzoni (o ordinare la playlist) con il massimo spazio tra artisti e titoli delle canzoni. Quindi, se ho 2000 canzoni e 20 sono di Ella, mi piacerebbe ascoltarla solo una volta ogni 100 canzoni. Se 10 artisti cantano Fly Me To The Moon, mi piacerebbe ascoltarlo una volta ogni 200 canzoni. Ovviamente voglio combinare questi due requisiti per creare il mio "shuffle finale".

So che questa è una domanda abbastanza aperta. Non ho ancora iniziato a programmarlo, quindi sto solo cercando suggerimenti su un buon approccio da adottare. In realtà ho altri requisiti per quanto riguarda la spaziatura uniforme degli altri attributi della canzone, ma non entrerò qui.


Come punto di partenza sto modificando il codice che ho trovato qui per manipolare i file mp3 e leggere i tag ID3.

Ho scritto una piccola app che soddisfa le mie necessità usando la risposta di parsifal di seguito. Ho anche scritto una domanda di follow-up qui . Grazie per tutte le belle risposte!


3
Bella domanda, bel problema, qualcuno che conosce davvero bene gli algoritmi avrà probabilmente un'ottima risposta basata su metodi formali per te.
Jimmy Hoffa,

Quindi, se il 50% della tua raccolta musicale proviene dallo stesso artista, ti piacerebbe ascoltare l'artista ogni 2 canzoni, indipendentemente da quanti altri artisti ci sono ... Forse non fino al 50%, ma ottieni il idea. Forse solo la mia opinione, ma non suona come un "ultimo shuffle", a meno che tu non abbia approssimativamente la stessa quantità di canzoni di ogni artista. D'altra parte, se hai solo 1 brano di un artista, non vuoi che suoni troppo. Trovare un equilibrio tra i 2 non dovrebbe essere difficile.
Dukeling,

Vorrei solo fare qualcosa di simile a questo pseudocodice:, while (length(songs) > 0) { x := rand(); addElem(shuffle, songs[x]); remElem(songs, x); }ma dici di volere uno "shuffle finale". Non so cosa tu voglia davvero, anche leggendo la domanda ...
Cole Johnson,

puoi caricare la tua lista dei brani da qualche parte - scheda titolo e artista o pipe separati o XML
tgkprog

Sarebbe bello avere (come plugin o core) in Banshee!
phw,

Risposte:


5

Vuoi eseguire il programma una volta e generare una playlist o scegliere la canzone successiva dal vivo?

In quest'ultimo caso, la risposta è semplice:

  • Crea un array che contenga tutte le tue canzoni, con artista e titolo
  • Crea un elenco (preferibilmente un elenco collegato) per contenere i titoli dei brani riprodotti di recente. Questo elenco inizia vuoto e ogni volta che suoni un brano lo aggiungi all'elenco. Quando l'elenco raggiunge la dimensione desiderata per "nessuna ripetizione del brano", elimina la prima (prima) voce.
  • Idem per un elenco di artisti.

Scegliere un brano diventa quindi la seguente sequenza di passaggi:

  1. Scegli casualmente una canzone dall'array "tutte le canzoni". Questo è solo un numero casuale compreso tra 0 e le dimensioni dell'array.
  2. Verifica se quel brano è già nell'elenco dei brani riprodotti. In tal caso, tornare al passaggio 1.
  3. Verifica se l'artista è già nell'elenco degli artisti riprodotti. In tal caso, tornare al passaggio 1.
  4. Aggiungi la canzone artista / titolo agli elenchi appropriati, lasciando vecchie voci se necessario.
  5. Riproduci la canzone.

Ci sono un paio di possibili problemi, ma dovrebbero importare solo se lo fai come compiti a casa e non come un vero progetto.

  • Come ha detto @Dukeling in un commento, se la tua collezione è drammaticamente sbilanciata a favore di un singolo artista o titolo di una canzone, potresti entrare in un ciclo in cui rifiuti costantemente le canzoni. In pratica, questo non sarà un problema. La soluzione è che è necessario ridurre le dimensioni degli elenchi "già visti". E l'aggiunta di contatori ai passaggi 2 e 3 può dirti se si tratta di un problema (se vedi 10 errori di seguito, genera un avviso e / o riduci le dimensioni dell'elenco).
  • Se stai provando a produrre una playlist che contiene tutti i tuoi brani riprodotti una sola volta, dovrai rimuovere i brani dall'array di origine. Ciò cambierà anche il modo in cui gestisci troppi errori "riprodotti di recente" (perché alla fine potresti finire con un solo artista nella tua matrice di origine).
  • Se i tuoi tag ID3 sono simili ai miei, contengono molti errori di ortografia. "Duke Ellington" deve essere diverso da "Duke Elingten"? Se sì, cerca di usare un abbinatore Levenstein durante la scansione degli elenchi "giocati di recente".

Uso RockBox ( rockbox.org ). Per qualsiasi cartella di brani è possibile creare una playlist dinamica (che può anche essere salvata e aggiunta ai segnalibri). Ho in programma di aggiungere un prefisso a ciascun brano 0001, 0002 e di riprodurli in questo ordine.
DeveloperDan

@DeveloperDan - lo stesso processo funziona, ma come ho notato alla fine avrai potenzialmente delle canzoni che non si adattano alle regole. Hai due possibilità: adattare le regole e rieseguire, oppure (se non ce ne sono molte) inserire le canzoni in modo casuale.
parsifal

Creerei un elenco nel passaggio 1 e lo rimuoverei in 2 e 3. Ciò rende impossibile rimanere bloccati in un ciclo e se l'elenco diventa vuoto, sai che devi cambiare le regole e ripetere la scansione. Modo più robusto per farlo.
Macke,

13

Ho fatto qualcosa del genere prima di usare un generatore (in C #, un ciclo infinito che yieldè ogni iterazione di ciclo). Ogni iterazione esamina il suo pool di brani (o qualsiasi altra cosa) e ne lancia di quelli che sono stati suonati troppo di recente (o qualunque criterio negativo). Quindi sceglierne uno dall'elenco filtrato e aggiornare il tuo stato. Man mano che il tuo stato va alla deriva (suoni canzoni non Sinatra) i criteri si interrompono e le tue canzoni escluse iniziano a essere incluse nuovamente.

Naturalmente ci sono casi angolari da affrontare:

  • Cosa succede se butti via tutte le canzoni? (di solito basta sceglierne uno a caso, sperando di destabilizzare lo stato)
  • Alcuni criteri dovrebbero essere preferiti? (di solito, forse non vuoi giocare a Fly Me to the Moon back to back, e preferisci non giocare a Sinatra back to back, ma se è tutto quello che hai ...)
  • Cosa succede se la tua raccolta di canzoni viene aggiornata a metà combattimento? (di solito facile da gestire, ma la concorrenza potrebbe avere problemi a seconda dell'uso)

11

Ignorando gli outlier della tua domanda che Telastyn solleva, sembra che tu abbia una variazione sul problema dello zaino . Fortunatamente, è un algoritmo abbastanza ben documentato.

Da Wikipedia

Dato un insieme di articoli, ciascuno con un peso e un valore, determina il numero di ciascun articolo da includere in una raccolta in modo che il peso totale sia inferiore o uguale a un determinato limite e il valore totale sia il più grande possibile.

Ci sono alcune varianti potenzialmente rilevanti elencate in quell'articolo insieme a un ulteriore elenco di problemi con lo zaino


Una variazione del problema dello zaino è il problema dello zaino multi-obiettivo. L' algoritmo della colonia di formiche è suggerito come mezzo per risolvere quel problema. L'approccio della colonia di formiche potrebbe essere il modo più semplice per evitare gli aspetti NP-difficili della tua domanda.

Potrei anche vedere il tuo problema come una variante estrema del problema del commesso viaggiatore . Ogni città da visitare è davvero una canzone che vuoi suonare, ma non sono sicuro di come potresti specificare gli intervalli tra gli artisti. Questo suggerimento è anche correlato / può essere risolto con l'approccio della colonia di formiche.


8

Sto lavorando partendo dal presupposto che questo è un "ecco la mia libreria, esegui questo programma e genera un ordine in cui riprodurre le canzoni".

Questo non è stato implementato e non sono sicuro di come preformerà il suo mescolamento. Può darsi che io sia un po ' troppo severo nel filtro, il che comporterebbe (credo) un ordine prescritto per il resto dato un set iniziale di canzoni.

Uno ha un ideal_gaphash. Questo è calcolato dalla densità di una canzone con una determinata proprietà (artista, album, titolo). Se uno ha 2000 canzoni e 20 di loro sono di un artista di nome Ella, ideal_gap{'artist'}{"ella"}sarebbero 100.

Con queste informazioni si ha anche il massimo dei valori ideal_gap. Chiamiamolo max_gap.

Considera: avere un ideal_gapvalore massimo per impedire a una canzone che solo due artisti hanno cantato di impedire che l'altra canzone venga riprodotta 1000 canzoni dopo, e anche aumentare drasticamente il valore max_gap con il risultato di molte iterazioni di "back off, no song, back spento, niente canzoni ".

Esaminando le ultime canzoni di max_gap riprodotte (questa può essere popolata da una corsa precedente in modo che se finisse con Frank Sinatra che cantava Fly Me To the Moon, la corsa successiva non inizierà con la stessa canzone per caso), si filtrano le canzoni per caso la libreria risultante in una serie di canzoni candidate. Una canzone sarebbe presente nelle canzoni candidate solo se tutte le sue lacune sono inferiori a ideal_gapquelle per quelle proprietà.

Dal set di brani candidati, selezionane uno a caso.

Considera: ponderare il set in modo che le canzoni che attribuiscono un gap massimo più elevato siano ponderate per avere maggiori probabilità. In questo modo, non ci sono tutti i brani con gap massimo più grandi che si accumulano alla fine della playlist.

Considera: invece di avere tutte e tre le proprietà maggiori dello spazio ideale, solo due su tre. Ciò può significare che qualcosa potrebbe essere suonato prima dell'ideale ideale, ma aumenta le dimensioni del set di canzoni candidato, il che significa che "selezionane uno a caso" ha più opzioni.

Se non ci sono brani che soddisfano i requisiti, max_gapfai un arretramento di 1 e tutti gli ideal_gaps in n/max_gappercentuale dov'è nil numero di volte in cui questo è stato interrotto. In questo modo, se è presente un valore max_gapdi 100 ed è stato ripristinato 5 volte in questa iterazione, un intervallo ideale di 100 verrebbe regolato temporaneamente su 95 e un intervallo ideale di 20 verrebbe regolato temporaneamente su 19. Ripetere il backup del gap fino a quando non vi è almeno un brano candidato, quindi selezionarlo come sopra.

Considerare: avere una dimensione minima del pool. Ciò aumenta la varianza, ma può comportare la riproduzione di una canzone prima del gap ideale quando è possibile riprodurre un'altra canzone.


1

Questo è un lavoro di ottimizzazione e piuttosto complesso se stai cercando la soluzione ottimale. Fortunatamente credo che sia uno di quei casi in cui andrà bene.

La prima cosa da fare è stabilire un criterio matematico di qualità, ovvero una formula che, data una permutazione dell'elenco, restituisca un singolo numero che descriva quanto sia buona o cattiva quella permutazione.

Un semplice suggerimento formula, ogni criterio che si desidera prendere in considerazione dovrebbe avere un peso, dare un peso elevato a criteri importanti e un peso basso a criteri in cui molti brani condividono la stessa proprietà, in modo che quelli non dominino :

For each song on the list
    For each other song on the list
        For each criteria
            If the two songs share that criteria
                Add to the quality value: square root( [criteria weight]/[distance between the two songs] )

Più basso è il valore prodotto da questa procedura, migliore è la permutazione dell'elenco.

Effettuare la permutazione

Ora potresti prendere questa formula per math.stackexchange e farti dire quanto è follemente difficile e forse praticamente impossibile trovare la soluzione ottimale per tutto tranne che un numero banale di brani, oppure puoi semplicemente lanciare cicli di clock e ottenere un buona soluzione

Esistono molti modi per farlo, eccone uno:

Start with a random permutation of the list.
Several million times do the following:
    Select two entries at random
    For each of those two entries calculate their contribution to the quality value
    Swap the positions of the two entries
    Calculate the contribution to the quality value of the two entries at their new position
    If the sum of the calculations in the new positions is greater than the sum in the old positions
        Swap back

Questo è un algoritmo un po 'dispendioso, ma è facile da implementare e può gestire tutti i criteri di un desiderio.

ottimizzazioni

È possibile applicare carichi di diverse modifiche e ottimizzazioni, eccone alcuni:

Nel calcolo del valore di qualità, non preoccuparti di controllare un brano rispetto a tutti gli altri brani dell'elenco, invece di confrontarlo con i 100 brani più vicini. Per valori comuni questa ottimizzazione della velocità non ha praticamente alcuna influenza sulla qualità del risultato.

Per un valore raro di una determinata proprietà potrebbe essere più efficiente tenere traccia delle istanze esistenti di quel valore piuttosto che cercarle.

Se ritieni che sia importante che i valori con poche istanze siano distanziati vicino, anche piuttosto che molto distanti, è probabilmente necessario aumentare il peso per quei valori specifici, ma non per altri valori di quel criterio.

Una funzione pseudo-casuale che seleziona tutte le coppie possibili dall'elenco in una distribuzione uguale può avere un'efficienza leggermente migliore per selezione rispetto a una normale scelta casuale.


Credo che il tuo algoritmo sia una forma di ricottura simulata che potrebbe essere un luogo in cui cercare di perfezionarla ulteriormente.

@MichaelT No, la ricottura simulata usa una "temperatura", che gli consente di regredire a uno stato inferiore nel tentativo di evitare di essere catturati al massimo locale. Questa è solo una ricerca locale , potrebbe essere modificata in ricottura simulata, o in una serie di altri algoritmi di ricerca probabilistica relativamente facilmente, ma non credo che ce ne sia molto bisogno. Fondamentalmente ciò che tutti gli altri algoritmi fanno diversamente è cercare di evitare i massimi locali, ma non penso che troverai un massimo locale per questo problema che non è una soluzione accettabile.
aaaaaaaaaaaa,

0

È interessante conoscere i diversi approcci adottati dalle persone. Farei quanto segue:

Basato su tutti i brani riprodotti finora, dai a ciascuno un punteggio. Riproduci la traccia con il punteggio più basso (o, nel caso di punteggi identici, uno casuale corrispondente al punteggio più basso). Ripetere.

La parte difficile, ovviamente, è dare un punteggio. Per ogni possibile traccia che potresti riprodurre in seguito, dovresti passare attraverso ciascuna (o un numero limitato di) tracce che hai già riprodotto. Se la traccia [possibile successiva] e la traccia [recentemente riprodotta] hanno qualcosa in comune, aggiungi alla partitura, a seconda di quanto hanno in comune, cosa hanno in comune e quanto tempo fa era la traccia [recentemente riprodotta] giocato. Probabilmente vorresti che "niente in comune" fosse 0, quindi puoi iniziare con tutte le tracce come 0.

Probabilmente vorrai sperimentare alcune playlist create a mano per cominciare, per ottenere la matematica giusta: vuoi il numero di parole in comune o il quadrato del numero di parole in comune o la radice quadrata del numero di parole in comune? Esegui l'intera playlist, vedi quali galleggiano verso l'alto come "i più comuni" e modifica a mano i fattori per ottenere il giusto equilibrio. Forse vuoi andare per lettera, quindi "Duke Ellington" ha un punteggio elevato rispetto a "Duke Elington", ma un punteggio ancora più alto rispetto a "King Elle Duton" (se non ho perso lettere :) . Dovresti considerare con molta attenzione quali campi vuoi confrontare e se vuoi confrontare tra campi. Potresti anche considerare i bigram (coppie di lettere; nel caso di Duke ellington, "Du", "

Nota che, se hai molti artisti in particolare, quell'artista potrebbe essere abbandonato in priorità: potresti ascoltare un brano di un artista unico 5 volte, prima di ascoltare tutti e 10 i tuoi brani di Duke Ellington. Questo potrebbe o non potrebbe essere quello che vuoi. Puoi evitarlo impostando un dizionario di tutto ciò che devi confrontare e con quale frequenza si verificano, quindi se hai molte tracce di Duke Ellington, due tracce di Duke Ellington sono "meno simili" rispetto a due di Billy Joe Shaver .

Potrebbe anche valere la pena pre-calcolare un tavolo con ogni combinazione di due coppie di canzoni. Inoltre, quando si considera quale canzone suonare dopo, è necessario solo ricordare la migliore canzone finora; se il prossimo da considerare ha un punteggio peggiore rispetto alla migliore canzone finora, puoi saltare al successivo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.