web-dev-qa-db-fra.com

J'aimerais écrire un algorithme "ultime shuffle" pour trier ma collection mp3

Je cherche des suggestions de pseudocode pour triant mes fichiers MP3 d'une manière qui évite la répétition de titre et d'artiste . J'écoute Crooners - Frank Sinatra, Tony Bennett, Ella Fitzgerald, etc., chantant d'anciennes normes. Chaque artiste enregistre de nombreuses mêmes chansons - volez-moi sur la lune, la façon dont vous regardez ce soir, Stardust, etc. Mon objectif est d'organiser les chansons (ou de commander la liste de lecture) avec l'espace maximum entre les artistes et les titres de la chanson. Donc, si j'ai 2000 chansons et 20 sont ella, j'aimerais l'entendre une seule fois sur 100 chansons. Si 10 artistes chantent me voler sur la lune, j'aimerais l'entendre une fois dans 200 chansons. Bien sûr, je souhaite combiner ces deux exigences pour créer mon "sulfle ultime".

Je sais que c'est une question ouverte assez large. Je n'ai pas encore commencé à la programmer, alors je cherche juste des suggestions d'une bonne approche à prendre. En fait, j'ai d'autres exigences concernant un compromis d'espacement uniformément d'autres attributs de chansons, mais je ne vais pas entrer dans cela ici.


Comme point de départ, je modifie le code I trouvé ici pour manipuler des fichiers MP3 et lire des balises ID3.

J'ai écrit une petite application qui satisfait à mes besoins en utilisant la réponse de Parsifal ci-dessous. J'ai aussi écrit un suivi question ici . Merci pour toutes les grandes réponses!

33
DeveloperDan

Voulez-vous exécuter votre programme une fois et générer une playlist ou choisir la prochaine chanson en direct?

Si ce dernier, la réponse est simple:

  • Créez un tableau contenant toutes vos chansons, avec artiste et titre
  • Créez une liste (liste liée préférable) pour contenir des titres de la chanson jouée récemment. Cette liste commence vide et chaque fois que vous jouez une chanson, vous l'ajoutez à la liste. Lorsque la liste frappe votre taille "Aucune répétition Song", déposez la plus ancienne (première) entrée.
  • Idem pour une liste d'artistes.

Choisir une chanson devient alors la séquence suivante des étapes:

  1. Choisissez une chanson au hasard dans le tableau "Tous les chansons". Ceci est juste un nombre aléatoire entre 0 et la taille de la matrice.
  2. Voyez si cette chanson est déjà dans la liste des chansons jouées. Si c'est le cas, retournez à l'étape 1.
  3. Voyez si l'artiste est déjà dans la liste des artistes joués. Si c'est le cas, retournez à l'étape 1.
  4. Ajoutez l'artiste/Titre de la chanson aux listes appropriées, laissant tomber les anciennes entrées si nécessaire.
  5. Jouez la chanson.

Il y a quelques problèmes possibles, mais ils ne devraient compter que si vous faites cela comme devoirs et non un véritable projet.

  • Comme @dukeling a dit dans un commentaire, si votre collection est dramatiquement déséquilibrée en faveur d'un seul artiste ou de titre de la chanson, vous pouvez entrer dans une boucle où vous rejetez constamment des chansons. En pratique, cela ne sera pas un problème. La solution est que vous devez réduire la taille des listes "déjà observées". Et ajouter des compteurs aux étapes n ° 2 et n ° 3 peut vous dire si c'est un problème (si vous voyez 10 défaillances d'affilée, soulevez un avertissement et/ou réduisez la taille de la liste).
  • Si vous essayez de produire une liste de lecture contenant toutes vos chansons ne jouées qu'une seule fois, vous devez supprimer des chansons du tableau source. Cela changera également comment vous traiterez de trop d'échecs "récemment joués" (car éventuellement, vous risquez de vous retrouver avec un seul artiste de votre réseau source).
  • Si vos tags ID3 sont comme la mienne, ils contiennent de nombreuses fautes mal orthographiques. "Duke Ellington" doit-il être différent de "Duke Elingten"? Si oui, puis examinez à l'aide d'un correspondeur LevenStein lors de la numérisation des listes "récemment jouées".
5
parsifal

J'ai fait quelque chose comme ça avant d'utiliser un générateur (en C #, une boucle infinie que yields chaque itération de boucle). Chaque itération regarde sa piscine de chansons (ou autre) et jette des celles qui ont été jouées trop récemment (ou quels que soient les critères négatifs). Ensuite, vous en choisissez une dans la liste filtrée et mettez à jour votre état. Lorsque votre état dérive (vous jouez des chansons non-Sinatra), les critères tombent en panne et que vos chansons exclues commencent à être réinvesties.

Bien sûr, il y a des cas d'angle de faire face à:

  • Que se passe-t-il si vous lancez toutes les chansons? (généralement en choisir un au hasard, dans l'espoir de déstabiliser l'état)
  • Certains critères devraient-ils être préférés? (généralement le cas, peut-être que vous ne voulez pas jouer à me voler sur la lune de retour à la lune et préférerais ne pas jouer à Sinatra de retour à l'arrière, mais si c'est tout ce que vous avez ...)
  • Que se passe-t-il si votre collection de chansons est mise à jour de mi-combat? (généralement facile à traiter, mais la concurrence pourrait avoir des problèmes en fonction de l'utilisation)
13
Telastyn

Ignorer les valeurs aberrantes de votre question que Telastyn apporte, il semble que vous ayez une variation sur le problème du knapack . Heureusement, c'est un algorithme assez bien documenté.

De Wikipedia

Compte tenu d'un ensemble d'éléments, chacun d'un poids et d'une valeur, déterminez le nombre de chaque élément à inclure dans une collection de sorte que le poids total est inférieur ou égal à une limite donnée et la valeur totale est aussi importante que possible.

Il existe certaines variations potentiellement pertinentes énumérées dans cet article avec une liste supplémentaire de problèmes de bande de bande


Une variation du problème du knapsack est le problème du knapack multi-objectifs. L'algorithme de la colonie d'ant est suggéré comme moyen de résoudre ce problème. L'approche de la colonie des fourmis pourrait être le moyen le plus simple d'éviter les aspects du NP-dur de votre question.

Je pourrais aussi voir que votre problème est une variante extrême du vendeur de voyage . Chaque ville à visiter est vraiment une chanson que vous voulez jouer, mais je ne sais pas comment vous spécifierez les intervalles entre artistes. Cette suggestion est également liée à/peut être résolue par l'approche de la colonie des fourmis.

11
user53019

Je travaille sous l'hypothèse selon laquelle c'est une "Voici ma bibliothèque, gère ce programme et génère une commande pour jouer les chansons".

Cela n'a pas été mis en œuvre et je suis incertain à quel point cela préformera son mélange. C'est peut-être que je suis un peu TOO strict dans le filtre, ce qui résulterait (je crois) dans un ordre prescrit pour le reste donné à un premier ensemble de chansons.

On a un ideal_gap hachage. Ceci est calculé par la densité d'une chanson avec une propriété donnée (artiste, album, titre). Si l'un a 2 000 chansons et 20 d'entre eux sont par un artiste nommé Ella, le ideal_gap{'artist'}{"ella"} serait 100.

Avoir cette information, on dispose également du max des valeurs idéales_gap. Permet d'appeler ceci max_gap.

Considérer: avoir un maximum pour le ideal_gap Valeur pour empêcher une chanson que seuls deux artistes ont chanté de la prévention de l'autre chanson d'être joué 1000 chansons plus tard et d'augmenter considérablement la valeur max_gap entraînant de nombreuses itérations de "Back off, pas de chansons" .

Examiner les dernières chansons max_gap jouées (cela peut être renseigné à partir d'une exécution précédente de sorte que s'il finit avec Frank Sinatra chantant me voler sur la lune, la prochaine course ne commencera pas avec la même chanson par hasard), on filtre des chansons de la bibliothèque donnant lieu à un ensemble de chansons candidates. Une chanson ne serait que dans les chansons candidates si toutes ses lacunes sont inférieures à la ideal_gap Pour ces propriétés.

Depuis l'ensemble des chansons candidates, sélectionnez-en un au hasard.

Considérez: Pondération de l'ensemble afin que les chansons qui attribuent une fossée maximale supérieure sont pondérées comme plus probables. De cette façon, on n'a pas toutes les plus grandes chansons de gap Max accumulées à la fin de la playlist.

Considérons: au lieu d'avoir les trois propriétés étant supérieures à l'écart idéal, deux sur trois. Cela peut signifier que quelque chose pourrait être joué plus tôt que l'idéal idéal, mais augmente la taille de l'ensemble de la chanson candidate Signification signifie "Sélectionnez-en au hasard" avec plus d'options.

S'il n'y a pas de chansons qui remplissent les exigences, renvoyez le max_gap par 1, et tous idéal_gaps par n/max_gap Pourcentage où n est le nombre de fois que cela a été renvoyé. De cette façon s'il y a un max_gap de 100, et il a été sauvegardé 5 fois dans cette itération, un effet idéal_gap de 100 serait ajusté pour être temporairement 95, et une valeur idéale de 20 serait ajustée pour être temporairement 19. Répétez la recul de l'espace jusqu'à ce qu'il soit au moins une chanson candidate, puis le sélectionnez comme ci-dessus.

Considérez: avoir une taille minimale de la piscine. Cela ajoute à la variance, mais peut entraîner une chanson jouée plus tôt que l'écart idéal lorsqu'il y a une autre chanson qui pourrait être jouée.

8
user40980

Ceci est un travail d'optimisation et un assez complexe si vous recherchez la Solution optimale. Heureusement, je crois que c'est l'un de ces cas où suffisamment bien.

La première chose à faire est d'établir un critère de qualité mathématique, c'est-à-dire une formule qui étant donné une permutation de la liste rendra un nombre unique décrivant la qualité ou la mauvaise mise en permanence.

Une simple suggestion de formule, chaque critère que vous voudriez prendre en compte doit avoir un poids, donner un poids élevé à des critères importants et un poids faible aux critères où beaucoup de chansons partagent la même propriété, de sorte que celles ne dominent pas :

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

La valeur inférieure de cette procédure produise, meilleure est la permutation de la liste.

Faire la permutation

Maintenant, vous pourriez prendre cette formule à Math.StaCkExchange et vous dire à quel point c'est incroyablement difficile et éventuellement impossible, il est de trouver la solution optimale pour tout sauf un nombre trivial de chansons, ou vous pouvez simplement lancer des cycles d'horloge et obtenir une bonne solution.

Il y a beaucoup de façons de faire cela, voici un:

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

C'est un algorithme quelque peu inutile, mais il est facile à mettre en œuvre et peut traiter autant de critères qu'un désir.

Optimisations

Des charges de modifications et d'optimisations différents peuvent être appliquées, voici quelques-uns:

Dans le calcul de la valeur de qualité, ne vous inquiétez pas de vérifier une chanson contre toutes les autres chansons de la liste, ce qui suffit à le vérifier contre les chansons de 100 ou plus les plus proches. Pour les valeurs communes Cette optimisation de la vitesse n'a pratiquement aucune influence sur la qualité du résultat.

Pour une valeur rare d'un bien donné, il peut être plus efficace de suivre les instances existantes de cette valeur que de les rechercher.

Si vous pensez qu'il est important que les valeurs qui ont peu d'instances soient espacées de même, plutôt que de loin, il est probablement nécessaire d'augmenter le poids de ces valeurs spécifiques, mais pas pour d'autres valeurs de ce critère.

Une fonction pseudo-aléatoire qui choisit toutes les paires possibles de la liste dans une distribution égale peut avoir une efficacité légèrement meilleure par choix qu'un choix aléatoire normal.

1
aaaaaaaaaaaa

C'est intéressant quelles différentes approches prennent les gens. Je ferais ce qui suit:

Basé sur toutes les pistes jouées jusqu'à présent, donnez à chacun un score. Jouez sur la piste avec le score le plus bas (ou, dans le cas de scores identiques, un aspect aléatoire correspondant au score le plus bas). Répéter.

Bien sûr, le bit difficile donne un score. Pour chaque piste possible, vous pourriez jouer ensuite, vous devrez passer par chaque (ou un nombre limité de) que vous avez déjà joué. Si la piste [Possible suivante] et la piste [récemment jouée] ont quelque chose en commun, vous ajoutez au score, en fonction de leur nombre en commun, de ce qu'ils ont en commun et de la fréquentation [récemment jouée]. joué. Vous voudriez probablement que "rien du tout en commun" soit 0, vous pouvez donc commencer avec toutes les pistes comme 0.

Vous voudrez probablement expérimenter des listes de lecture fabriquées à la main pour commencer, pour obtenir les mathématiques à droite - voulez-vous le nombre de mots en commun, ou le carré du nombre de mots en commun, ou la racine carrée du nombre des mots en commun? Exécutez toute votre playlist à travers, voyez ceux qui flottent au sommet comme étant "le plus commun" et modifient à la main les facteurs pour obtenir le bon équilibre. Peut-être que vous voulez aller par lettre, alors "Duke Ellington" a un score élevé par rapport à "Duke Elington", mais un score encore plus élevé par rapport au "roi d'elle Duton" (si je n'ai perdu aucune lettre :) . Vous devriez considérer très soigneusement les champs que vous souhaitez comparer, et si vous souhaitez comparer entre les champs. Vous pourriez même considérer les bigrams (paires de lettres; dans le cas de Duke Ellington, "Du", "Royaume-Uni", "Ke", "EE", et ainsi de suite.

Notez que, si vous avez beaucoup d'artiste particulier, cet artiste pourrait être déposé en priorité - vous pourriez entendre une piste par un artiste unique 5 fois, avant d'entendre les 10 pistes de Duke Ellington. Cela pourrait ou pourrait ne pas être ce que vous voulez. Vous pouvez éviter cela en mettant en place un dictionnaire de tout ce que vous avez à comparer, et combien de fois ils se produisent, donc si vous avez beaucoup de pistes de duc Ellington, deux pistes qui sont de Duke Ellington sont "moins similaires" que deux par Billy Joe Shaver .

Cela pourrait même valoir la peine de pré-associer une table avec chaque combinaison de deux paires de chansons. En outre, lorsque vous envisagez quelle chanson à jouer ensuite, vous n'avez besoin que de vous souvenir de la meilleure chanson jusqu'à présent; Si le prochain à considérer a un score pire que la meilleure chanson jusqu'à présent, vous pouvez passer à la suivante.

0
AMADANON Inc.