Me gustaría escribir un algoritmo "ultimate shuffle" para ordenar mi colección de mp3


33

Estoy buscando sugerencias de pseudocódigo para ordenar mis archivos mp3 de una manera que evite la repetición de títulos y artistas . Escucho cantantes: Frank Sinatra, Tony Bennett, Ella Fitzgerald, etc., cantando viejos estándares. Cada artista graba muchas de las mismas canciones: Fly Me To The Moon, The Way You Look Tonight, Stardust, etc. Mi objetivo es organizar las canciones (u ordenar la lista de reproducción) con el máximo espacio entre artistas y títulos de canciones. Entonces, si tengo 2000 canciones y 20 son de Ella, me gustaría escucharla solo una vez de cada 100 canciones. Si 10 artistas cantan Fly Me To The Moon, me gustaría escucharlo una vez cada 200 canciones. Por supuesto, quiero combinar estos dos requisitos para crear mi "baraja definitiva".

Sé que esta es una pregunta bastante abierta. Todavía no he comenzado a programarlo, así que solo estoy buscando sugerencias de un buen enfoque. De hecho, tengo algunos otros requisitos con respecto al espaciado uniforme de otros atributos de la canción, pero no voy a entrar en eso aquí.


Como punto de partida, estoy modificando el código que encontré aquí para manipular archivos mp3 y leer etiquetas ID3.

Escribí una pequeña aplicación que satisface mi necesidad usando la respuesta de parsifal a continuación. También escribí una pregunta de seguimiento aquí . ¡Gracias por todas las buenas respuestas!


3
Buena pregunta, buen problema, alguien que conozca los algoritmos realmente bien probablemente tendrá una gran respuesta basada en métodos formales para usted.
Jimmy Hoffa

Entonces, si el 50% de su colección de música es del mismo artista, le gustaría escuchar al artista cada 2 canciones, independientemente de cuántos otros artistas hay ... Tal vez no tanto como el 50%, pero obtiene el idea. Tal vez solo sea mi opinión, pero eso no suena como una "mezcla definitiva", a menos que tenga aproximadamente la misma cantidad de canciones de todos los artistas. Por otro lado, si solo tienes 1 canción de un artista, no quieres que suene demasiado. Encontrar un equilibrio entre los 2 no debería ser difícil.
Dukeling

Simplemente haría algo como este pseudocódigo: while (length(songs) > 0) { x := rand(); addElem(shuffle, songs[x]); remElem(songs, x); }pero usted dice que quiere un "shuffle definitivo". No sé lo que realmente quieres con eso, incluso leyendo la pregunta ...
Cole Johnson

¿puedes subir tu lista de canciones en alguna parte? Título y pestaña de artistas o pipe separados o XML
tgkprog

¡Sería maravilloso tenerlo (como complemento o núcleo) en Banshee!
phw

Respuestas:


5

¿Desea ejecutar su programa una vez y generar una lista de reproducción, o elegir la próxima canción en vivo?

Si es lo último, entonces la respuesta es simple:

  • Crea una matriz que contenga todas tus canciones, con artista y título.
  • Cree una lista (es preferible una lista vinculada) para guardar los títulos de las canciones reproducidas recientemente. Esta lista comienza vacía, y cada vez que reproduce una canción la agrega a la lista. Cuando la lista alcanza el tamaño deseado de "no repetir canciones", suelte la entrada más antigua (primera).
  • Lo mismo para una lista de artistas.

Elegir una canción se convierte en la siguiente secuencia de pasos:

  1. Elija aleatoriamente una canción del conjunto "todas las canciones". Este es solo un número aleatorio entre 0 y el tamaño de la matriz.
  2. Vea si esa canción ya está en la lista de canciones reproducidas. Si es así, regrese al paso 1.
  3. Vea si el artista ya está en la lista de artistas reproducidos. Si es así, regrese al paso 1.
  4. Agregue el artista / título de la canción a las listas apropiadas, eliminando las entradas antiguas si es necesario.
  5. Toca la cancion.

Hay un par de posibles problemas, pero solo deberían importar si lo haces como tarea y no como un proyecto real.

  • Como dijo @Dukeling en un comentario, si su colección está dramáticamente desequilibrada a favor de un solo artista o título de la canción, puede entrar en un bucle donde constantemente rechaza las canciones. En la práctica, esto no será un problema. La solución es que necesita reducir el tamaño de las listas "ya vistas". Y agregar contadores en los pasos 2 y 3 puede decirle si es un problema (si ve 10 fallas seguidas, haga una advertencia y / o reduzca el tamaño de la lista).
  • Si está intentando producir una lista de reproducción que contenga todas sus canciones reproducidas solo una vez, deberá eliminar las canciones de la matriz fuente. Esto también cambiará la forma en que lidias con demasiadas fallas "reproducidas recientemente" (porque eventualmente podrías terminar con solo un artista en tu matriz fuente).
  • Si sus etiquetas ID3 son como las mías, contienen muchas faltas de ortografía. ¿"Duke Ellington" necesita ser diferente de "Duke Elingten"? En caso afirmativo, busque el uso de un emparejador Levenstein al escanear las listas "reproducidas recientemente".

Yo uso RockBox ( rockbox.org ). Para cualquier carpeta de canciones, puede crear una lista de reproducción dinámica (que también se puede guardar y marcar). Planeo prefijar el título de cada canción 0001, 0002 y luego reproducirlas en ese orden.
DesarrolladorDan

@DeveloperDan: el mismo proceso funciona, pero como señalo al final, potencialmente tendrás canciones que no se ajustan a las reglas. Tiene dos opciones: adaptar las reglas y volver a ejecutar, o (si no hay muchas) insertar las canciones al azar.
parsifal

Crearía una lista en el paso 1 y la eliminaría en 2 y 3. Eso hace que sea imposible quedar atrapado en un bucle, y si la lista se vacía, sabes que necesitas cambiar las reglas y volver a escanear. Una forma más robusta de hacerlo.
Macke

13

He hecho algo como esto antes de usar un generador (en C #, un bucle infinito que yieldes cada iteración del bucle). Cada iteración analiza su grupo de canciones (o lo que sea) y arroja las que se han reproducido demasiado recientemente (o cualquier criterio negativo). Luego elige uno de la lista filtrada y actualiza su estado. A medida que su estado cambia (toca canciones que no son de Sinatra), el criterio se desmorona y sus canciones excluidas comienzan a volver a incluirse.

Por supuesto, hay casos de esquina para tratar:

  • ¿Qué pasa si tiras todas las canciones? (generalmente solo elige uno al azar, con la esperanza de desestabilizar el estado)
  • ¿Deberían preferirse algunos criterios? (por lo general, tal vez no quieras jugar Fly Me to the Moon de forma consecutiva, y preferirías no jugar Sinatra de forma consecutiva, pero si eso es todo lo que tienes ...)
  • ¿Qué sucede si tu colección de canciones se actualiza a mitad de la pelea? (generalmente fácil de tratar, pero la concurrencia puede tener problemas dependiendo del uso)

11

Ignorando los valores atípicos de su pregunta que plantea Telastyn, parece que tiene una variación en el problema de la mochila . Afortunadamente, es un algoritmo bastante bien documentado.

De Wikipedia

Dado un conjunto de artículos, cada uno con un peso y un valor, determine el número de cada artículo para incluir en una colección de modo que el peso total sea menor o igual a un límite dado y el valor total sea lo más grande posible.

Hay algunas variaciones potencialmente relevantes enumeradas en ese artículo junto con una lista adicional de problemas de mochila


Una variación del problema de la mochila es el problema de la mochila de objetivos múltiples. El algoritmo de la colonia de hormigas se sugiere como un medio para resolver ese problema. El enfoque de la colonia de hormigas podría ser la forma más fácil de evitar los aspectos difíciles de NP de su pregunta.

También pude ver considerando su problema como una variante extrema del problema del vendedor ambulante . Cada ciudad para visitar es realmente una canción que quieres tocar, pero no estoy seguro de cómo especificarías los intervalos entre artistas. Esta sugerencia también está relacionada con / puede resolverse mediante el enfoque de colonias de hormigas.


8

Estoy trabajando bajo el supuesto de que este es un "aquí está mi biblioteca, ejecute este programa y genere un orden para reproducir las canciones".

Esto no se ha implementado y no estoy seguro de qué tan bien preformará su barajado. Puede ser que soy demasiado estricto en el filtro, lo que resultaría (creo) en un orden prescrito para el resto dado un conjunto inicial de canciones.

Uno tiene un ideal_gaphash. Esto se calcula por la densidad de una canción con una propiedad dada (artista, álbum, título). Si una tiene 2000 canciones y 20 de ellas son de una artista llamada Ella, ideal_gap{'artist'}{"ella"}serían 100.

Tener esta información también tiene el máximo de los valores ideal_gap. Vamos a llamar a esto max_gap.

Considere: tenga un ideal_gapvalor máximo para evitar que una canción que solo dos artistas han cantado impida que la otra canción se reproduzca 1000 canciones más tarde, y también aumente drásticamente el valor max_gap, lo que resulta en muchas iteraciones de "retroceder, sin canciones, volver apagado, no hay canciones ".

Examinando las últimas canciones de max_gap reproducidas (esto se puede completar a partir de una ejecución anterior, de modo que si terminó con Frank Sinatra cantando Fly Me To the Moon, la próxima ejecución no comenzará con la misma canción por casualidad), se filtran canciones de la biblioteca resulta en un conjunto de canciones candidatas. Una canción solo estaría en las canciones candidatas si todos sus espacios son menores que los ideal_gapde esas propiedades.

Del conjunto de canciones candidatas, seleccione una al azar.

Considere: ponderar el conjunto para que las canciones que se atribuyen con una brecha máxima más alta sean ponderadas para ser más probables. De esta manera, uno no tiene todas las canciones de brecha máxima más grandes que se acumulan al final de la lista de reproducción.

Considere: en lugar de que las tres propiedades sean mayores que la brecha ideal, solo dos de cada tres. Esto puede significar que algo podría reproducirse antes del ideal ideal, pero aumenta el tamaño del conjunto de canciones candidato, lo que significa que "seleccionar uno al azar" tiene más opciones.

Si no hay canciones que cumplan los requisitos, retroceda en max_gap1 y todos los ideal_gaps en n/max_gapporcentaje, donde nes el número de veces que se ha retrocedido. De esta manera, si hay un max_gap100 y se ha retrocedido 5 veces en esta iteración, un ideal_gap de 100 se ajustará temporalmente a 95, y un ideal_gap de 20 se ajustará temporalmente a 19. Repetir el retroceso del separe hasta que haya al menos una canción candidata, y luego selecciónela como se indica arriba.

Considere: tener un tamaño mínimo de piscina. Esto se suma a la variación, pero puede dar como resultado que se reproduzca una canción antes del intervalo ideal cuando hay otra canción que podría reproducirse.


1

Este es un trabajo de optimización y bastante complejo si está buscando la solución óptima. Afortunadamente, creo que es uno de esos casos donde lo suficientemente bueno será suficiente.

Lo primero que debe hacer es establecer un criterio matemático de calidad, es decir, una fórmula que, dada una permutación de la lista, devolverá un solo número que describe cuán buena o mala es esa permutación.

Una sugerencia de fórmula simple, cada criterio que le gustaría tener en cuenta debe tener un peso, dar un alto peso a los criterios importantes y un bajo peso a los criterios en los que muchas canciones comparten la misma propiedad, para que esas no dominen :

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] )

Cuanto menor sea el valor que produce este procedimiento, mejor será la permutación de la lista.

Haciendo la permutación

Ahora podría llevar esta fórmula a math.stackexchange y hacer que le digan cuán increíblemente difícil y posiblemente prácticamente imposible es encontrar la solución óptima para cualquier cosa que no sea un número trivial de canciones, o simplemente puede lanzarle ciclos de reloj y obtener un buena solución.

Hay muchas formas de hacerlo, aquí hay una:

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

Este es un algoritmo algo derrochador, pero es fácil de implementar y puede manejar tantos criterios como uno desee.

Optimizaciones

Se pueden aplicar cargas de diferentes ajustes y optimizaciones, aquí hay algunos:

En el cálculo del valor de calidad, no se moleste en comparar una canción con cada una de las otras canciones de la lista, en su lugar, simplemente verifíquela con las 100 canciones más cercanas. Para valores comunes, esta optimización de velocidad prácticamente no tiene influencia en la calidad del resultado.

Para un valor raro de una propiedad dada, puede ser más eficiente rastrear las instancias existentes de ese valor que buscarlas.

Si considera que es importante que los valores que tienen pocas instancias estén espaciados de manera pareja, en lugar de estar muy separados, probablemente sea necesario aumentar el peso para esos valores específicos, pero no para otros valores de ese criterio.

Una función pseudoaleatoria que selecciona todos los pares posibles de la lista en igual distribución puede tener una eficiencia ligeramente mejor por selección que una selección aleatoria normal.


Creo que su algoritmo es una forma de recocido simulado que puede ser un lugar para buscar para refinarlo aún más.

@MichaelT No, el recocido simulado utiliza una "temperatura" que le permite regresar a un estado inferior en un intento de evitar quedar atrapado en un máximo local. Esta es solo una búsqueda local , podría modificarse para el recocido simulado, o cualquiera de varios otros algoritmos de búsqueda probabilística con relativa facilidad, pero no creo que haya mucha necesidad de eso. Básicamente, lo que todos los otros algoritmos hacen de manera diferente es tratar de evitar los máximos locales, pero no creo que encuentre un máximo local para este problema que no sea una solución aceptable.
aaaaaaaaaaaa

0

Es interesante qué diferentes enfoques toman las personas. Yo haría lo siguiente:

En función de todas las pistas reproducidas hasta ahora, asigne una puntuación a cada una. Reproduzca la pista con el puntaje más bajo (o, en el caso de puntajes idénticos, uno aleatorio que coincida con el puntaje más bajo). Repetir.

Lo difícil, por supuesto, es dar un puntaje. Para cada pista posible que pueda reproducir a continuación, deberá pasar por cada una (o un número limitado de) pistas que ya haya reproducido. Si la pista [posible siguiente] y la pista [recientemente reproducida] tienen algo en común, se agrega a la puntuación, dependiendo de cuánto tienen en común, qué tienen en común y cuánto tiempo hace que era la pista [recientemente reproducida] jugó. Probablemente desee que "nada en común" sea 0, por lo que puede comenzar con todas las pistas como 0.

Para comenzar, es probable que desee experimentar con algunas listas de reproducción hechas a mano, para obtener las matemáticas correctas: ¿desea el número de palabras en común, o el cuadrado del número de palabras en común, o la raíz cuadrada del número? de palabras en común? Ejecute toda su lista de reproducción, vea cuáles flotan a la cima como "más en común" y modifique manualmente los factores para obtener el equilibrio correcto. Tal vez quieras ir por letra, así que "Duke Ellington" tiene un puntaje alto en comparación con "Duke Elington", pero un puntaje aún mayor en comparación con "King Elle Duton" (si no he perdido ninguna letra :) . Debe considerar con mucho cuidado qué campos desea comparar y si desea comparar entre campos. Incluso podría considerar bigrams (pares de letras; en el caso de Duke ellington, "Du", "

Tenga en cuenta que, si tiene muchos artistas en particular, ese artista puede ser prioritario: puede escuchar una pista de un artista único 5 veces, antes de escuchar las 10 pistas de Duke Ellington. Esto podría o no ser lo que quieres. Puede evitar esto estableciendo un diccionario de todo lo que tiene que comparar y con qué frecuencia ocurren, por lo que si tiene muchas pistas de Duke Ellington, dos pistas de Duke Ellington son "menos similares" que dos de Billy Joe Shaver .

Incluso podría valer la pena calcular previamente una tabla con cada combinación de dos pares de canciones. Además, al considerar qué canción tocar a continuación, solo necesita recordar la mejor canción hasta el momento; Si la siguiente a considerar tiene una puntuación peor que la mejor canción hasta ahora, puede pasar a la siguiente.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.