Natknąłem się na to pytanie, badając podobny problem: optymalne dodatki płynów w celu zmniejszenia stratyfikacji. Wygląda na to, że moje rozwiązanie dotyczyłoby również twojej sytuacji.
Jeśli chcesz mieszać ciecze A, B i C w proporcji 30, 20, 10 (to znaczy 30 jednostek A, 20 jednostek B i 10 jednostek C), otrzymujesz rozwarstwienie, jeśli dodasz wszystkie A, potem wszystkie B, a potem wszystkie C. Lepiej mieszaj mniejsze jednostki. Na przykład wykonaj dodawanie pojedynczych jednostek w sekwencji [A, B, A, C, B, A]. To całkowicie zapobiegnie rozwarstwieniu.
Znalazłem sposób, aby to potraktować jako rodzaj scalenia, używając kolejki priorytetowej. Jeśli utworzę strukturę do opisania dodatków:
MergeItem
Item, Count, Frequency, Priority
Częstotliwość jest wyrażana jako „jeden na N”. Zatem A, który jest dodawany trzy z sześciu razy, ma częstotliwość 2 (6/3).
I zainicjuj stertę, która początkowo zawiera:
(A, 3, 2, 2)
(B, 2, 3, 3)
(C, 1, 6, 6)
Teraz usuwam pierwszy element ze sterty i wysyłam go. Następnie zmniejsz jego liczbę o 1 i zwiększ Priorytet o Częstotliwość i dodaj go z powrotem do stosu. Wynikowa sterta to:
(B, 2, 3, 0)
(A, 2, 2, 4)
(C, 1, 6, 6)
Następnie usuń B ze sterty, wydrukuj i zaktualizuj, a następnie dodaj z powrotem do sterty:
(A, 2, 2, 4)
(C, 1, 6, 6)
(B, 1, 3, 6)
Jeśli będę kontynuować w ten sposób, otrzymam pożądaną mieszankę. Korzystam z niestandardowego modułu porównującego, aby upewnić się, że gdy do stosu zostaną wstawione elementy o równym priorytecie, najpierw zostanie zamówiony ten o najwyższej wartości częstotliwości (tj. Najmniejszej częstotliwości).
Na blogu napisałem pełniejszy opis problemu i jego rozwiązania oraz przedstawiłem działający kod C #, który to ilustruje. Zobacz Równomierne rozmieszczenie elementów na liście .
Zaktualizuj po komentarzach
Myślę, że mój problem jest podobny do problemu PO i dlatego moje rozwiązanie jest potencjalnie przydatne. Przepraszam, że nie sformułowałem mojej odpowiedzi bardziej w kontekście pytania PO.
Pierwszy zarzut, że moje rozwiązanie używa A, B i C zamiast 0, 1 i 2, można łatwo naprawić. To po prostu kwestia nomenklatury. Uważam, że łatwiej i mniej myląco jest myśleć i mówić „dwa A” niż „dwa 1”. Ale na potrzeby tej dyskusji zmodyfikowałem swoje wyniki poniżej, aby użyć nomenklatury PO.
Oczywiście mój problem dotyczy pojęcia odległości. Jeśli chcesz „rozłożyć równomiernie”, sugeruje się odległość. Ale znowu to moja wina, że nie pokazałem odpowiednio, jak mój problem jest podobny do problemu PO.
Przeprowadziłem kilka testów z dwoma przykładami dostarczonymi przez PO. To jest:
[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]
W mojej nomenklaturze są one wyrażone odpowiednio jako [2,2,2] i [4,3,2,1]. Oznacza to, że w ostatnim przykładzie „4 elementy typu 0, 3 elementy typu 1, 2 elementy typu 2 i 1 element typu 3.”
Uruchomiłem program testowy (jak opisano bezpośrednio poniżej) i opublikowałem swoje wyniki. Brak wkładu OP, nie mogę powiedzieć, czy moje wyniki są podobne, gorsze lub lepsze od jego. Nie mogę też porównywać moich wyników z wynikami innych osób, ponieważ nikt inny ich nie opublikował.
Mogę jednak powiedzieć, że algorytm stanowi dobre rozwiązanie mojego problemu eliminacji stratyfikacji podczas mieszania cieczy. I wygląda na to, że zapewnia rozsądne rozwiązanie problemu PO.
Do pokazanych poniżej wyników użyłem algorytmu, który opisałem szczegółowo w moim wpisie na blogu, z początkowym priorytetem ustawionym na Frequency/2
, a moduł porównujący sterty został zmodyfikowany, aby faworyzować częstszy element. Zmodyfikowany kod jest pokazany tutaj, z komentarzem zmodyfikowanych linii.
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;
}
}
Uruchamiając mój program testowy z pierwszym przykładem OP, otrzymuję:
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
Mój algorytm działa więc na trywialny problem polegający na tym, że wszystkie liczby są równe.
W przypadku drugiego problemu opublikowanego przez PO otrzymałem:
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
Nie widzę oczywistego sposobu na poprawę tego. Można to zmienić, aby uzyskać odległości dla pozycji 0 [2,3,2,3] lub innego ustawienia 2 i 3, ale to zmieni odchylenia dla pozycji 1 i / lub 2. Naprawdę nie wiem co „optymalne” jest w tej sytuacji. Czy lepiej jest mieć większe odchylenie w przypadku częstszych lub rzadszych przedmiotów?
Nie mając innych problemów z OP, wykorzystałem jego opisy, by stworzyć kilka własnych. W swoim poście powiedział:
Typowa lista zawiera ~ 50 pozycji o ~ 15 różnych wartościach w różnych ilościach.
Więc moje dwa testy to:
[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
A moje wyniki:
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
I dla drugiego przykładu:
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