Szukam algorytmu do dystrybucji wartości z listy, aby powstała lista była jak najbardziej „zrównoważona” lub „równomiernie rozłożona” (w cudzysłowie, ponieważ nie jestem pewien, czy są to najlepsze sposoby na opisanie jej ... później przedstawię sposób pomiaru, czy wynik jest lepszy niż inny).
Tak więc dla listy:
[1, 1, 2, 2, 3, 3]
Jednym z najlepszych rezultatów po ponownej dystrybucji wartości jest:
[1, 2, 3, 1, 2, 3]
Mogą występować inne wyniki tak dobre jak ten i oczywiście komplikuje się to z mniej jednolitym zestawem wartości.
Oto jak zmierzyć, czy wynik jest lepszy niż inny:
Policz odległości między każdym przedmiotem a następnym przedmiotem o tej samej wartości.
Oblicz odchylenie standardowe dla tego zestawu odległości. Niższa dyspersja oznacza lepszy wynik.
Obserwacje:
- Przy obliczaniu odległości i osiągnięciu końca listy bez znalezienia elementu o tej samej wartości wracamy do początku listy. Co najwyżej ten sam element zostanie znaleziony, a odległość dla tego elementu będzie długością listy. Oznacza to, że lista jest cykliczna ;
- Typowa lista zawiera ~ 50 pozycji o ~ 15 różnych wartościach w różnych ilościach.
Więc:
- W rezultacie
[1, 2, 3, 1, 2, 3]
odległości są[3, 3, 3, 3, 3, 3]
, a odchylenie standardowe wynosi0
; - W rezultacie
[1, 1, 2, 2, 3, 3]
odległości są[1, 5, 1, 5, 1, 5]
, a odchylenie standardowe wynosi2
; - Co sprawia, że pierwszy wynik jest lepszy od drugiego (niższe odchylenie jest lepsze).
Biorąc pod uwagę te definicje, proszę o wskazówkę, których algorytmów lub strategii powinienem szukać.
Odpowiedzi:
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:
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:
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:
Następnie usuń B ze sterty, wydrukuj i zaktualizuj, a następnie dodaj z powrotem do sterty:
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:
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.Uruchamiając mój program testowy z pierwszym przykładem OP, otrzymuję:
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:
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ł:
Więc moje dwa testy to:
A moje wyniki:
I dla drugiego przykładu:
źródło
To „pachnie”, jakby mogło być trudne do NP. Co robisz, gdy masz problem z NP? Rzuć na nią heurystykę, algorytm aproksymacyjny lub użyj solvera SAT.
W twoim przypadku, jeśli nie potrzebujesz absolutnie optymalnego rozwiązania, jednym rozsądnym punktem wyjścia może być próba symulowanego wyżarzania . Istnieje naturalny sposób, aby wziąć dowolne rozwiązanie kandydata i przenieść je do pobliskiego rozwiązania kandydata: losowo wybierz dwa elementy z listy i zamień je. Symulowane wyżarzanie będzie iteracyjnie próbowało ulepszyć rozwiązanie. Możesz znaleźć wiele zasobów na temat symulowanego wyżarzania, jeśli nie znasz go. Możesz także eksperymentować z innymi zestawami „lokalnych ruchów”, które wprowadzają niewielkie zmiany do rozwiązania kandydującego, z nadzieją na stopniowe jego ulepszanie (tj. Zmniejszanie standardowego odchylenia odległości).
Ale proponuję zacząć od symulowanego wyżarzania. To pierwsza rzecz, której spróbuję, ponieważ myślę, że to może po prostu zadziałać.
źródło
Szkic algorytmu heurystycznego
Nie mam dokładnego rozwiązania tego problemu. Ale ponieważ komentarz Raphaela sugeruje, że wygląda to na problem z podziałem, dla którego opracowano algorytmy heurystyczne, spróbuję zastosować podejście heurystyczne. To tylko szkic algorytmu heurystycznego.
To poprowadzi nasz algorytm.
Na początku może to być wartość z bardzo małą liczbą wystąpień. Myślę, że tak naprawdę to nie robi różnicy, ponieważ ograniczenia tworzone przez zajmowanie miejsc są proporcjonalne do liczby dobrze umiejscowionych wartości (?).
Pierwszą rozważaną wartość można umieścić bez żadnych ograniczeń. Następnie pozostałe wartości należy umieścić w taki sposób, aby zminimalizować ich udział w odchyleniu standardowym, ale tylko w miejscach wolnych od dowolnych wcześniej wprowadzonych wartości.
Umieszczenie wystąpień wartości w pozostałych gniazdach można wykonać za pomocą algorytmu programowania dynamicznego, aby scalić obliczenia, które umieszczają tę samą liczbę wartości między dwiema pozycjami, zachowując tylko te, które mają minimalny udział w odchyleniu standardowym (tj. minimalna wartość sumy kwadratu ich odchyleń).
Następnie umieszczasz wartości singletonów w pozostałych gniazdach.
Uważam, że powinno to ogólnie dać rozsądne rozwiązanie, ale nie mam jeszcze pojęcia, jak to udowodnić lub oszacować lukę za pomocą optymalnego rozwiązania.
źródło
[0, 0, 0, 0, 1, 1, 1, 2, 2, 3]
i v4
umieścilibyśmy pierwsze wartości1
(10/3 = 3.33
najbliższe v), a następnie2
(10/2 = 5
najbliższe najbliższe), a następnie0
(10/4 = 2.5
)? Lub: czy możesz podać przykład „malejącego średniego odchylenia odległości od wartości v”?Wygląda na to, że jestem bardzo spóźniony na imprezę, ale wysyłam pocztę na wypadek, gdyby ktoś znów się na to natknął. Moje rozwiązanie jest podobne do plus @ babou. Wcześniej miałem problem z harmonogramem w systemie osadzonym, który zaprowadził mnie do tego wątku. Mam implementację specyficzną dla mojego problemu w C, ale pomyślałem, że opublikuję tutaj bardziej ogólne rozwiązanie w Pythonie (wersja C jest skomplikowana przez to, że ograniczyłem się do małego, stałego rozmiaru stosu i bez pamięci alokacje, więc wykonuję cały algorytm na miejscu). Technika wygładzania zastosowana poniżej to coś, czego możesz użyć do narysowania linii na ekranie w 2-bitowym kolorze. Algorytm tutaj osiąga niższy wynik (tj. Lepszy), mierzony za pomocą sumy standardowego odchylenia dla danych wejściowych używanych przez Jima Mischela niż to konkretne rozwiązanie.
wyniki dla
Jeśli dane wejściowe formularza określone przez @moraes, można przekonwertować go do formularza używanego przez tę funkcję w krokach O (n) przy użyciu bitów pamięci Big Omega (n * log (n)), gdzie n jest liczbą elementów ( na liście zawierającej 255 elementów nie będziesz potrzebował więcej niż 255 dodatkowych bajtów), utrzymując równoległą tablicę z liczbą powtórzeń. Alternatywnie można wykonać parę sortowań na miejscu z dodatkową pamięcią O (1).
PS
Edycja: Wiem, że to rozwiązanie nie zapewnia optymalnej wydajności przez kontrprzykład. Wkład
[6, 2, 1]
produkcji[0, 1, 0, 0, 2, 0, 0, 1, 0]
; lepszym rozwiązaniem jest[0, 0, 1, 0, 2, 0, 0, 1, 0]
.źródło
Ten algorytm działa z tablicą liczb całkowitych, gdzie każda liczba całkowita reprezentuje inną kategorię. Tworzy osobne tablice dla każdej kategorii. Na przykład, jeśli tablica początkowa to [1, 1, 1, 2, 2, 3], utworzy trzy tablice, [3], [2, 2], [1, 1, 1].
Stamtąd rekurencyjnie łączy dwie najmniejsze tablice (w tym przykładzie [3] i [2,2]) i rozmieszcza rozmieszczenie elementów mniejszej tablicy w drugiej najmniejszej tablicy w oparciu głównie o stosunek liczby wystąpień większych i mniejszych kategorii. W tym przykładzie zakończymy z [2,3,2]. Następnie użyłby tej tablicy jako mniejszej tablicy, która zostanie połączona w następną większą tablicę, dopóki nie zostanie tylko jedna tablica.
źródło
KOD ANSI C
Ten kod działa, wyobrażając sobie linię prostą w n przestrzeni wymiarowej (gdzie n jest liczbą kategorii) przechodzącą przez początek z wektorem kierunkowym (v1, v2, ..., vi, ... vn), gdzie vi jest liczbą pozycje w kategorii i. Zaczynając od początku, celem jest znalezienie następnego najbliższego punktu do linii. Na przykładzie [0 0 0 0 0 1 1 1 2 2 2 3] daje wynik [0 1 2 0 3 1 0 2 0 1 2 0]. Korzystając z przykładu Lungja [0 0 0 0 0 0 1 1 2] otrzymujemy [0 1 0 0 2 0 0 1 0], co jest dokładnie takie samo jak wynik Lungja.
Algorytm jest bardziej wydajny dzięki zastosowaniu tylko arytmetyki liczb całkowitych i uwzględnianiu tylko delt między odległościami od każdego punktu do linii.
# zdefiniować MAXCATEGORIES 100
int main () {int i = 0; int j = 0; int catsize = 0; wektor wewnętrzny [MAXCATEGORIES]; punkt początkowy [MAXCATEGORIES]; int kategorii = 0; int totalitems = 0; int best = 0; długie d2 = 0 l; długie vp = 0L; długie v2 = 0 l; długa delta = 0L; długa beta = 0L;
}
źródło
moje rozwiązanie:
źródło