Я зіткнувся з цим питанням, досліджуючи подібну проблему: оптимальні добавки рідин для зменшення стратифікації. Здається, моє рішення також застосовно до вашої ситуації.
Якщо ви хочете змішати рідини A, B і C у пропорції 30,20,10 (тобто 30 одиниць A, 20 одиниць B і 10 одиниць С), ви закінчите стратифікацією, якщо додати всі то А, то всі В, а потім усі С. Тобі краще змішувати менші одиниці. Наприклад, зробіть однорядні доповнення в послідовності [A, B, A, C, B, A]. Це взагалі запобіжить розшаруванню.
Я знайшов це зробити - це трактувати це як своєрідне злиття, використовуючи чергу пріоритету. Якщо я створю структуру для опису доповнень:
MergeItem
Item, Count, Frequency, Priority
Частота виражається як «кожен N». Отже A, який додається три з шести разів, має частоту 2 (6/3).
І ініціалізуйте купу, яка спочатку містить:
(A, 3, 2, 2)
(B, 2, 3, 3)
(C, 1, 6, 6)
Тепер я виймаю перший елемент із купи і вивожу його. Потім зменшіть його кількість на 1 і збільште пріоритет за частотою та додайте його до купи. Отримана купа:
(B, 2, 3, 0)
(A, 2, 2, 4)
(C, 1, 6, 6)
Далі вийміть B з купи, виведіть і оновіть її, а потім додайте назад до купи:
(A, 2, 2, 4)
(C, 1, 6, 6)
(B, 1, 3, 6)
Якщо я продовжую так, я отримаю бажану суміш. Я використовую спеціальний порівняльник, щоб переконатися, що коли в групу вставляються рівні елементи Пріоритету, спочатку впорядковується найвище значення частоти (тобто найменше частота).
Я написав більш повний опис проблеми та її рішення у своєму блозі та представив деякий робочий код C #, який це ілюструє. Див. Рівномірний розподіл елементів у списку .
Оновлення після коментарів
Я думаю, що моя проблема схожа на проблему ОП, і тому моє рішення може бути корисним. Прошу вибачення за те, що більше не поставив свою відповідь в плані питання ОП.
Перше заперечення про те, що моє рішення використовує A, B і C, а не 0, 1 і 2, легко усувається. Це просто питання номенклатури. Мені легше і менш заплутано думати і говорити "два А", а не "два 1". Але для цілей цієї дискусії я змінив свої результати нижче, щоб використовувати номенклатуру ОП.
Звичайно, моя проблема стосується концепції відстані. Якщо ви хочете "рівномірно розподілити речі", мається на увазі відстань. Але, знову ж таки, це було моїм невдачею не адекватно показати, наскільки моя проблема схожа на проблему ОП.
Я провів кілька тестів з двома прикладами, які надав ОП. Це є:
[1,1,2,2,3,3] // which I converted to [0,0,1,1,2,2]
[0,0,0,0,1,1,1,2,2,3]
У моїй номенклатурі це виражено як [2,2,2] та [4,3,2,1] відповідно. Тобто в останньому прикладі "4 предмети типу 0, 3 елементи 1 типу, 2 елементи типу 2 та 1 предмет типу 3".
Я запустив свою тестову програму (як описано одразу нижче) та опублікував свої результати. Відсутній вхід з ОП, я не можу сказати, чи мої результати схожі на гірші, або кращі, ніж його. Я також не можу порівнювати свої результати з чиїмись результатами, тому що ще ніхто не публікував.
Я можу сказати, однак, що алгоритм забезпечує хороше рішення моєї проблеми усунення стратифікації при змішуванні рідин. І схоже, це забезпечує розумне вирішення проблеми ОП.
Для результатів, показаних нижче, я використав алгоритм, який я детально описав у своєму записі в блозі, з початковим пріоритетом, встановленим на Frequency/2
, і групу порівняння змінено на користь більш частого пункту. Тут показаний модифікований код із коментованими зміненими рядками.
private class HeapItem : IComparable<HeapItem>
{
public int ItemIndex { get; private set; }
public int Count { get; set; }
public double Frequency { get; private set; }
public double Priority { get; set; }
public HeapItem(int itemIndex, int count, int totalItems)
{
ItemIndex = itemIndex;
Count = count;
Frequency = (double)totalItems / Count;
// ** Modified the initial priority setting.
Priority = Frequency/2;
}
public int CompareTo(HeapItem other)
{
if (other == null) return 1;
var rslt = Priority.CompareTo(other.Priority);
if (rslt == 0)
{
// ** Modified to favor the more frequent item.
rslt = Frequency.CompareTo(other.Frequency);
}
return rslt;
}
}
Запускаючи свою тестову програму з першого прикладу ОП, я отримую:
Counts: 2,2,2
Sequence: 1,0,2,1,0,2
Distances for item type 0: 3,3
Stddev = 0
Distances for item type 1: 3,3
Stddev = 0
Distances for item type 2: 3,3
Stddev = 0
Отже, мій алгоритм працює для тривіальної задачі, коли всі рахунки рівні.
Для другої проблеми, яку опублікувала ОП, я отримав:
Counts: 4,3,2,1
Sequence: 0,1,2,0,1,3,0,2,1,0
Distances for item type 0: 3,3,3,1
Stddev = 0.866025403784439
Distances for item type 1: 3,4,3
Stddev = 0.471404520791032
Distances for item type 2: 5,5
Stddev = 0
Distances for item type 3: 10
Stddev = 0
Standard dev: 0.866025403784439,0.471404520791032,0,0
Я не бачу очевидного способу покращити це. Можна змінити відстані для пункту 0 [2,3,2,3] або іншого розташування 2 і 3, але це змінить відхилення для пунктів 1 та / або 2. Я дійсно не знаю, що "Оптимум" є в цій ситуації. Чи краще мати більші відхилення на більш частих або на менш частих предметах?
Не маючи інших проблем з ОП, я використовував його описи, щоб скласти кілька моїх власних. Він сказав у своєму дописі:
Типовий список містить ~ 50 предметів з ~ 15 різними значеннями в різних кількостях.
Тож два мої тести були:
[8,7,6,5,5,4,3,3,2,2,2,1,1,1,1] // 51 items, 15 types
[12,6,5,4,4,3,3,3,2,2,2,1,1] // 48 items, 13 types
І мої результати:
Counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sequence: 0,1,2,3,4,5,7,6,0,1,2,8,9,10,4,3,0,1,5,2,0,1,3,4,6,7,14,11,13,12,0,2,5,1,0,3,4,2,8,10,9,1,0,7,6,5,3,4,2,1,0
Distances for item type 0: 8,8,4,10,4,8,8,1
Stddev = 2.82566363886433
Distances for item type 1: 8,8,4,12,8,8,3
Stddev = 2.76272565797339
Distances for item type 2: 8,9,12,6,11,5
Stddev = 2.5
Distances for item type 3: 12,7,13,11,8
Stddev = 2.31516738055804
Distances for item type 4: 10,9,13,11,8
Stddev = 1.72046505340853
Distances for item type 5: 13,14,13,11
Stddev = 1.08972473588517
Distances for item type 6: 17,20,14
Stddev = 2.44948974278318
Distances for item type 7: 19,18,14
Stddev = 2.16024689946929
Distances for item type 8: 27,24
Stddev = 1.5
Distances for item type 9: 28,23
Stddev = 2.5
Distances for item type 10: 26,25
Stddev = 0.5
Distances for item type 11: 51
Stddev = 0
Distances for item type 12: 51
Stddev = 0
Distances for item type 13: 51
Stddev = 0
Distances for item type 14: 51
Stddev = 0
А для другого прикладу:
Counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sequence: 0,1,2,0,3,4,7,5,6,0,1,8,9,10,0,2,0,3,4,1,0,2,6,7,5,12,11,0,1,0,3,4,2,0,1,10,8,9,0,7,5,6,0,
4,3,2,1,0
Distances for item type 0: 3,6,5,2,4,7,2,4,5,4,5,1
Stddev = 1.68325082306035
Distances for item type 1: 9,9,9,6,12,3
Stddev = 2.82842712474619
Distances for item type 2: 13,6,11,13,5
Stddev = 3.44093010681705
Distances for item type 3: 13,13,14,8
Stddev = 2.34520787991171
Distances for item type 4: 13,13,12,10
Stddev = 1.22474487139159
Distances for item type 5: 17,16,15
Stddev = 0.816496580927726
Distances for item type 6: 14,19,15
Stddev = 2.16024689946929
Distances for item type 7: 17,16,15
Stddev = 0.816496580927726
Distances for item type 8: 25,23
Stddev = 1
Distances for item type 9: 25,23
Stddev = 1
Distances for item type 10: 22,26
Stddev = 2
Distances for item type 11: 48
Stddev = 0
Distances for item type 12: 48
Stddev = 0