Я хотів би написати алгоритм «остаточного перетасування», щоб сортувати свою колекцію mp3


33

Я шукаю псевдокоди щодо сортування моїх mp3-файлів таким чином, щоб уникнути повтору заголовка та виконавця . Я слухаю коронців - Френка Сінатра, Тоні Беннетта, Елла Фіцджеральд тощо, співаючи старі стандарти. Кожен артист записує багато тих самих пісень - Fly Me To The Moon, The Way You Look Tonight, Stardust тощо. Моя мета - упорядкувати пісні (або замовити список відтворення) з максимальним пробілом між виконавцями та назвами пісень. Тож якщо у мене є 2000 пісень, а 20 - від Елли, я хотів би почути її лише раз на кожні 100 пісень. Якщо 10 виконавців співають Fly Me To The Moon, я хотів би почути це раз на кожні 200 пісень. Звичайно, я хочу поєднати ці дві вимоги, щоб створити "остаточне переміщення".

Я знаю, що це досить широке відкрите питання. Я ще не почав її програмувати, тому просто шукаю пропозиції щодо гарного підходу. Насправді у мене є деякі інші вимоги щодо рівномірного розміщення інших атрибутів пісні, але я тут не вникаю в неї.


Як вихідний пункт, я змінюю код, який я знайшов тут, щоб маніпулювати mp3-файлами та читати теги ID3.

Я написав невеликий додаток, який задовольняє мої потреби, використовуючи відповідь parsifal нижче. Тут я також написав додаткове запитання . Дякую за всі чудові відгуки!


3
Класне питання, класна проблема, хтось, хто добре знає алгоритми, швидше за все, отримає чудову відповідь, заснований на формальних методах для вас.
Джиммі Хоффа

Отже, якщо 50% вашої музичної колекції від одного виконавця, ви хочете почути виконавця кожні 2 пісні, незалежно від того, скільки там інших виконавців ... Можливо, не стільки 50%, але ви отримаєте ідея. Можливо, лише моя думка, але це не звучить як "остаточна перетасовка", якщо у вас приблизно однакова кількість пісень від кожного виконавця. З іншого боку, якщо у вас є лише 1 пісня виконавця, ви не хочете, щоб це грало занадто багато. Знайти баланс між двома не повинно бути складно.
Герцогство

Я б просто зробив щось на кшталт цього псевдокоду:, while (length(songs) > 0) { x := rand(); addElem(shuffle, songs[x]); remElem(songs, x); }але ти кажеш, що хочеш "остаточного переміщення". Я не знаю, чого ви насправді цього хочете, навіть читаючи питання ...
Коул Джонсон

ви можете десь завантажити свій список пісень - вкладка та виконавці назви та виконавців або XML
tgkprog

Це було б чудово мати (як плагін або ядро) в Banshee!
phw

Відповіді:


5

Ви хочете запустити свою програму один раз і створити список відтворення, або вибрати наступну пісню в прямому ефірі?

Якщо остання, то відповідь проста:

  • Створіть масив, який містить усі ваші пісні, з виконавцем та заголовком
  • Створіть список (бажано пов’язаний список) для вміщення нещодавно відтворених назв пісень. Цей список починається порожнім, і кожного разу, коли ви граєте пісню, додаєте її до списку. Коли список відображає потрібний розмір "не повторювати пісню", киньте найстаріший (перший) запис.
  • Дітто для списку художників.

Вибір пісні потім стає такою послідовністю кроків:

  1. Випадково виберіть пісню з масиву "всі пісні". Це просто випадкове число між 0 і розміром масиву.
  2. Перевірте, чи ця пісня вже є у списку відтворених пісень. Якщо це так, поверніться до кроку №1.
  3. Перевірте, чи виконавець уже в списку відтворених виконавців. Якщо це так, поверніться до кроку №1.
  4. Додайте виконавця / назву пісні до відповідних списків, відміняючи старі записи, якщо потрібно.
  5. Грайте пісню.

Можливі проблеми, але вони мають мати значення лише в тому випадку, якщо ви робите це як домашнє завдання, а не справжній проект.

  • Як зазначив @Dukeling у коментарі, якщо ваша колекція різко незбалансована на користь одного виконавця чи назви пісні, ви можете потрапити в цикл, де ви постійно відхиляєте пісні. На практиці це не буде проблемою. Рішення полягає в тому, що вам потрібно зменшити розмір списків, "які вже бачили". І додавання лічильників на кроках №2 та №3 може сказати вам, чи це проблема (якщо ви бачите 10 помилок підряд, підніміть попередження та / або зменшіть розмір списку).
  • Якщо ви намагаєтеся створити список відтворення, який містить усі ваші пісні, відтворені лише один раз, вам потрібно буде видалити пісні з вихідного масиву. Це також змінить спосіб боротьби з занадто багатьма "нещодавно відтвореними" відмовами (адже в кінцевому підсумку ви можете мати лише одного виконавця у вихідному масиві).
  • Якщо ваші теги ID3 чимось схожі на моє, вони містять багато неправильних написань. Чи потрібно «герцогу Еллінгтону» відрізнятись від «герцога Елінгтена»? Якщо так, то погляньте на використання відповідника Levenstein під час сканування списків "нещодавно відтворених".

Я використовую RockBox ( rockbox.org ). Для будь-якої папки пісень це може створити динамічний список відтворення (який також можна зберегти і зробити закладку). Я планую встановити префікс кожної назви пісні 0001, 0002, а потім відтворити їх у такому порядку.
DeveloperDan

@DeveloperDan - той самий процес працює, але, як зауважу, наприкінці ви, можливо, матимете пісні, які не відповідають правилам. У вас є два варіанти: адаптуйте правила і повторіть, або (якщо їх не багато) вставляйте пісні випадковим чином.
parsifal

Я створив би список на кроці 1 і видаляв з нього в 2 і 3. Це унеможливлює застрягнення в циклі, і якщо список стає порожнім, ви знаєте, що вам потрібно змінити правила та повторно сканувати. Більш надійний спосіб зробити це.
Макке

13

Я зробив щось подібне до використання генератора (у C #, нескінченному циклі, який відповідає yieldкожній ітерації циклу). Кожна ітерація розглядає свою групу пісень (чи що завгодно) та викидає ті, які були відтворені занадто недавно (або за будь-якими негативними критеріями). Потім ви вибираєте один із відфільтрованого списку та оновлюєте свій стан. У міру того, як ваш стан гуляє (ви граєте пісні, що не є синатрами), критерії руйнуються і ваші виключені пісні починають повторно включатися.

Звичайно, тут можна вирішити найголовніші справи:

  • Що станеться, якщо викинеш усі пісні? (як правило, просто виберіть один навмання, сподіваючись дестабілізувати стан)
  • Чи слід віддати перевагу деяким критеріям? (зазвичай це так, можливо, ви не хочете грати Fly Me to the Moon назад до спини, і я б вважав за краще не грати в Синатру спиною до спини, але якщо це все, що у вас є ...)
  • Що станеться, якщо ваша колекція пісень буде оновлена ​​в середині бою? (зазвичай це легко вирішити, але паралельність може мати проблеми залежно від використання)

11

Ігнорування інших людей у ​​вашому запитанні, яке піднімає Теластин, здається, що у вас є різниця щодо проблеми з рюкзаком . На щастя, це досить добре задокументований алгоритм.

З Вікіпедії

Враховуючи набір предметів, кожен з вагою і значенням, визначте кількість кожного предмета, який потрібно включити до колекції, щоб загальна вага була меншою або дорівнює заданій межі, а загальне значення було максимально можливим.

У цій статті є деякі потенційно релевантні варіанти, а також додатковий перелік проблем із рюкзаком


Однією з варіантів проблеми з рюкзаком є ​​багатоцільова проблема рюкзака. Алгоритм колонії мурашок пропонується як засіб вирішення цієї проблеми. Підхід до колонії мурашок може бути для вас найпростішим способом уникнути важких аспектів вашого питання.

Я також міг би розглянути вашу проблему як крайній варіант проблеми мандрівного продавця . Кожне місто, яке потрібно відвідати, - це справді пісня, яку ви хочете зіграти, але я не впевнений, як би ви вказали інтервали між виконавцями. Ця пропозиція також пов'язана з / може бути вирішена підходом до колонії мурашок.


8

Я працюю при припущенні, що це "ось моя бібліотека, запустіть цю програму і створіть замовлення на відтворення пісень".

Це не було здійснено, і я не впевнений, наскільки добре він буде проводити його перемішування. Можливо, я трохи надто суворий у фільтрі, що призвело б (я вважаю) до встановленого порядку для решти, отримавши початковий набір пісень.

Один має ideal_gapхеш. Це обчислюється щільністю пісні з заданою властивістю (виконавцем, альбомом, заголовком). Якщо в одному є 2000 пісень, а 20 з них - артистка на ім'я Елла, то це ideal_gap{'artist'}{"ella"}було б 100.

Маючи цю інформацію, один також має максимальне значення ideal_gap. Давайте назвемо це max_gap.

Подумайте: максимум ideal_gapзначення, щоб запобігти пісні, яку співали лише два виконавці, не дозволяти іншій пісні відтворити 1000 пісень пізніше, а також різко збільшити значення max_gap, що призведе до багатьох ітерацій "відключення, без пісень, назад вимкнено, пісень немає ".

Вивчаючи останні відтворені пісні max_gap (це можна заповнити з попереднього запуску, так що якщо закінчився, коли Франк Сінатра співав Fly Me To the Moon, наступний запуск не розпочнеться з тієї самої пісні випадково), один фільтрує пісні з бібліотека, в результаті якої вийшов набір пісень-кандидатів. Пісня була б у піснях-кандидатах лише тоді, коли всі її прогалини менші, ніж ideal_gapдля цих властивостей.

З набору пісень-кандидатів виберіть одну навмання.

Поміркуйте: зважуйте набір таким чином, щоб пісні, які мають атрибути з більшим максимальним відхиленням, зважувались на більш імовірне значення. Таким чином, не всі більші пісні максимуму розриву накопичуються в кінці списку відтворення.

Поміркуйте: замість того, щоб усі три властивості були більшими за ідеальний зазор, лише два з трьох. Це може означати, що щось можна відтворити швидше, ніж ідеальний ідеал, але збільшується розмір набору кандидатських пісень, тобто "вибір навмання" має більше варіантів.

Якщо немає пісень, які відповідають вимогам, відключіть значення max_gapна 1, а всі idea_gaps на n/max_gapвідсотки, де nкількість копій, яку ви відхилили . Таким чином, якщо є max_gap100, і це було відключено 5 разів у цій ітерації, ideal_gap 100 буде відрегульовано тимчасово до 95, а ideal_gap з 20 буде скориговано тимчасово до 19. Повторіть резервне копіювання розрив, поки не буде хоча б однієї пісні-кандидата, а потім виберіть її як вище.

Поміркуйте: мати мінімальний розмір басейну. Це додає дисперсії, але це може призвести до того, що пісня буде відтворюватися раніше, ніж ідеальний проміжок, коли є ще одна пісня, яку можна було б відтворити.


1

Це завдання оптимізації, і досить складним , якщо ви шукаєте в оптимальному рішенні. На щастя, я вважаю, що це один із тих випадків, коли буде достатньо доброго.

Перше, що потрібно зробити, це встановити математичний критерій якості, тобто формула, яка при перестановці списку поверне єдине число, що описує, наскільки хороша чи погана ця перестановка.

Проста пропозиція формули, кожному критерію, який ви хотіли б взяти до уваги, слід надати вагу, надати велику вагу важливим критеріям і низьку вагу критеріям, коли багато пісень мають однакову властивість, щоб ті не домінували :

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

Чим менше значення дає ця процедура, тим краще перестановка списку.

Виготовлення перестановки

Тепер ви можете взяти цю формулу до math.stackexchange і дати їм розповісти, як шалено важко і, можливо, практично неможливо, знайти оптимальне рішення для будь-якого, окрім тривіальної кількості пісень, або ви можете просто кинути на неї цикли годин і отримати хороше рішення.

Існує багато способів зробити це, ось один:

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

Це дещо марнотратний алгоритм, але його легко здійснити і може мати справу з якоюсь кількістю критеріїв, як одне бажання.

Оптимізація

Можна застосувати різні налаштування та оптимізації, ось декілька:

У підрахунку вартості якості не переймайтеся перевіряти пісню проти будь-якої іншої пісні в списку, а просто перевіряйте її на 100 або близько найближчих пісень. Для загальних значень ця оптимізація швидкості практично не впливає на якість результату.

Для рідкісного значення даної властивості може бути ефективнішим відслідковувати наявні екземпляри цього значення, ніж шукати їх.

Якщо ви вважаєте, що важливо, щоб значення, які мають кілька примірників, розташовувались близько до рівних, а не просто далеко один від одного, ймовірно, необхідно збільшити вагу для цих конкретних значень, але не для інших значень цього критерію.

Псевдовипадкова функція, яка вибирає всі можливі пари зі списку при рівномірному розподілі, може мати дещо кращу ефективність на вибір, ніж звичайна випадкова вибірка.


Я вважаю, що ваш алгоритмічний спосіб є імітованим відпалом, який може шукати для його подальшого вдосконалення.

@MichaelT Ні, імітували відпал, використовуючи "температуру", яка дозволяє їй повернутися до нижчого стану, намагаючись уникнути локального максимуму. Це просто локальний пошук , він може бути модифікований для імітаційного відпалу або будь-якого з ряду інших імовірнісних алгоритмів пошуку порівняно легко, але я не думаю, що в цьому є велика потреба. В основному те, що всі інші алгоритми роблять по-різному, це намагатися уникати локальних максимумів, але я не думаю, що ви знайдете локальні максимуми для цієї проблеми, які не є прийнятним рішенням.
aaaaaaaaaaaa

0

Цікаво, якими різними підходами користуються люди. Я б зробив таке:

Виходячи з усіх відтворених досі треків, дайте кожному бал. Відтворіть трек з найнижчою оцінкою (або, у випадку однакових балів, випадковим, що відповідає найнижчому балу). Повторіть.

Складний біт, звичайно, - це оцінка. Для кожного можливого треку, який ви можете відтворити наступним, вам доведеться пройти кожен (або обмежену кількість) треків, які ви вже відтворили. Якщо трек [можливий наступний] та трек [нещодавно відіграний] мають щось спільне, ви додаєте до оцінки, залежно від того, скільки у них спільного, що у них спільного та як давно трек [нещодавно відтворений] був грав. Ймовірно, ви хочете, щоб "взагалі нічого спільного" не було 0, тому ви можете починати з усіх треків як 0.

Ви, напевно, захочете поекспериментувати з певними списками відтворення, виконаними вручну, щоб правильно зрозуміти математику - чи хочете ви кількість слів спільних, або квадрат кількості загальних слів, або квадратний корінь числа спільних слів? Запустіть увесь список відтворення, подивіться, які з них випливають на вершину як "найбільш загальні", і вручну змініть фактори, щоб вирівняти баланс. Можливо, ви хочете перейти за лист, тому "Дюк Еллінгтон" має високий бал у порівнянні з "Дюк Елінгтон", але ще більший бал у порівнянні з "Кінг Елле Дутон" (якщо я не втратив жодної літери :) . Вам слід дуже уважно розглянути, які поля ви хочете порівняти, а також якщо ви хочете порівняти між ними поля. Можна навіть розглянути біграми (пари букв; у випадку Дюка Еллінгтона: "Du", "

Зауважте, що якщо у вас багато конкретного виконавця, цей художник може бути скинутий у пріоритеті - ви можете почути трек унікального виконавця 5 разів, перш ніж почути всі 10 ваших треків Duke Ellington. Це може бути або не бути тим, що ви хочете. Ви можете уникнути цього, встановивши словник всього, що маєте для порівняння, і як часто вони трапляються, тож якщо у вас багато треків Дюка Еллінгтона, два треки, виконані Дюком Еллінгтоном, "менш схожі", ніж два Біллі Джо Шавер .

Можливо, варто навіть попередньо скласти таблицю з кожною комбінацією двох пар пісень. Також, розглядаючи, яку пісню відтворити далі, потрібно лише згадати найкращу пісню досі; якщо наступний, який слід розглянути, має гірший бал, ніж найкраща пісня поки що, ви можете перейти до наступної.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.