Oto rozwiązanie O (n log n) w Javie.
long merge(int[] arr, int[] left, int[] right) {
int i = 0, j = 0, count = 0;
while (i < left.length || j < right.length) {
if (i == left.length) {
arr[i+j] = right[j];
j++;
} else if (j == right.length) {
arr[i+j] = left[i];
i++;
} else if (left[i] <= right[j]) {
arr[i+j] = left[i];
i++;
} else {
arr[i+j] = right[j];
count += left.length-i;
j++;
}
}
return count;
}
long invCount(int[] arr) {
if (arr.length < 2)
return 0;
int m = (arr.length + 1) / 2;
int left[] = Arrays.copyOfRange(arr, 0, m);
int right[] = Arrays.copyOfRange(arr, m, arr.length);
return invCount(left) + invCount(right) + merge(arr, left, right);
}
To prawie normalne sortowanie przez scalanie, cała magia jest ukryta w funkcji scalania. Zauważ, że podczas sortowania algorytm usuń inwersje. Podczas scalania algorytm zlicza liczbę usuniętych inwersji (można powiedzieć, że są uporządkowane).
Jedynym momentem, w którym usuwa się inwersje, jest to, że algorytm pobiera element z prawej strony tablicy i łączy go z tablicą główną. Liczba inwersji usuniętych przez tę operację to liczba elementów pozostałych do scalenia z lewej tablicy. :)
Mam nadzieję, że to wystarczająco wyjaśniające.
left.length - i
do licznika inwersji? Sądzę, że sensowne byłoby dodanie 1, ponieważ wpadłeś w logiczny przypadek, w którym porównanie między dwiema podtablicami ma większy lewy element tablicy niż prawy. Ktoś może mi to wyjaśnić, jakbym miał 5 lat?arr
. Ale to nie jest jedna inwersja. Znalazłeś inwersje dla wszystkich elementów w lewej tablicy, które są większe niż 6. W naszym przypadku zawiera również 8. Więc dodaje się 2count
, co jest równeleft.length - i
.Znalazłem to w czasie O (n * log n) w następujący sposób.
Weź A [1] i znajdź jego pozycję w posortowanej tablicy B za pomocą wyszukiwania binarnego. Liczba inwersji tego elementu będzie o jeden mniejsza niż numer indeksu jego pozycji w B, ponieważ każda mniejsza liczba pojawiająca się po pierwszym elemencie A będzie inwersją.
2a. akumulują liczbę inwersji, aby zliczać zmienną num_inversions.
2b. usuń A [1] z tablicy A, a także z odpowiadającej jej pozycji w tablicy B
Oto przykład uruchomienia tego algorytmu. Oryginalna tablica A = (6, 9, 1, 14, 8, 12, 3, 2)
1: Scal sortowanie i kopiuj do tablicy B.
B = (1, 2, 3, 6, 8, 9, 12, 14)
2: Weź A [1] i wyszukaj binarne, aby znaleźć go w tablicy B
A [1] = 6
B = (1, 2, 3, 6 , 8, 9, 12, 14)
6 znajduje się na czwartej pozycji tablicy B, więc są 3 inwersje. Wiemy o tym, ponieważ 6 znajdowało się na pierwszej pozycji w tablicy A, więc każdy element o niższej wartości, który później pojawia się w tablicy A, miałby indeks j> i (ponieważ i w tym przypadku wynosi 1).
2.b: Usuń A [1] z tablicy A, a także z odpowiadającej jej pozycji w tablicy B (elementy pogrubione są usuwane).
A = ( 6, 9, 1, 14, 8, 12, 3, 2) = (9, 1, 14, 8, 12, 3, 2)
B = (1, 2, 3, 6, 8, 9, 12, 14) = (1, 2, 3, 8, 9, 12, 14)
3: Uruchom ponownie od kroku 2 na nowych macierzach A i B.
A [1] = 9
B = (1, 2, 3, 8, 9, 12, 14)
9 znajduje się teraz na piątej pozycji tablicy B, więc są 4 inwersje. Wiemy o tym, ponieważ 9 znajdowało się na pierwszej pozycji w tablicy A, więc każdy element o niższej wartości, który się później pojawi, miałby indeks j> i (ponieważ i w tym przypadku jest znowu 1). Usuń A [1] z tablicy A, a także z odpowiadającej jej pozycji w tablicy B (elementy pogrubione są usuwane)
A = ( 9 , 1, 14, 8, 12, 3, 2) = (1, 14, 8, 12, 3, 2)
B = (1, 2, 3, 8, 9 , 12, 14) = (1, 2, 3, 8, 12, 14)
Kontynuując ten wątek, uzyskamy całkowitą liczbę inwersji dla tablicy A po zakończeniu pętli.
Krok 1 (sortowanie przez scalanie) wymagałoby wykonania O (n * log n). Krok 2 byłby wykonywany n razy i przy każdym wykonaniu wykonywałoby wyszukiwanie binarne, które wymaga O (log n) do uruchomienia w sumie O (n * log n). Całkowity czas pracy wyniósłby zatem O (n * log n) + O (n * log n) = O (n * log n).
Dzięki za pomoc. Zapisanie przykładowych tablic na kartce papieru naprawdę pomogło w wizualizacji problemu.
źródło
W Pythonie
źródło
Zastanawiam się, dlaczego nikt jeszcze nie wspomniał o drzewach indeksowanych binarnie . Możesz użyć jednego, aby zachować sumy przedrostków na wartościach elementów permutacji. Następnie możesz po prostu przejść od prawej do lewej i policzyć dla każdego elementu liczbę elementów mniejszą niż po prawej:
Złożoność wynosi O (n log n), a stały współczynnik jest bardzo niski.
źródło
i -= i & -i
linii? I podobniei += i & -i
timeit
porównuje wszystkie odpowiedzi Pythona na to pytanie, więc zawiera Twój kod. Możesz być zainteresowany spojrzeniem na wyniki synchronizacji.Miałem podobne pytanie do pracy domowej. Ograniczono mnie, że musi mieć wydajność O (nlogn).
Skorzystałem z zaproponowanego przez ciebie pomysłu użycia Mergesort, ponieważ ma on już właściwą wydajność. Właśnie wstawiłem kod do funkcji scalającej, która w zasadzie brzmiała: Za każdym razem, gdy liczba z tablicy po prawej stronie jest dodawana do tablicy wyjściowej, dodaję do całkowitej liczby inwersji liczbę liczb pozostałych w lewej tablicy.
Ma to dla mnie dużo sensu teraz, kiedy wystarczająco o tym myślałem. Twoje liczenie, ile razy jest większa liczba poprzedzająca jakiekolwiek liczby.
hth.
źródło
Podstawowym celem tej odpowiedzi jest porównanie prędkości różnych wersji języka Python znalezionych tutaj, ale mam też kilka własnych uwag. (FWIW, właśnie odkryłem to pytanie podczas powtórnego wyszukiwania).
Względne szybkości wykonywania algorytmów zaimplementowanych w CPythonie mogą różnić się od tych, których można by oczekiwać po prostej analizie algorytmów i doświadczeniach z innymi językami. Dzieje się tak, ponieważ Python zapewnia wiele zaawansowanych funkcji i metod zaimplementowanych w C, które mogą działać na listach i innych kolekcjach z prędkością bliską szybkości, jaką można uzyskać w całkowicie skompilowanym języku, więc te operacje działają znacznie szybciej niż równoważne algorytmy zaimplementowane „ręcznie” w Pythonie kod.
Kod korzystający z tych narzędzi może często przewyższać teoretycznie lepsze algorytmy, które próbują zrobić wszystko za pomocą operacji Pythona na poszczególnych elementach kolekcji. Oczywiście ma na to również wpływ faktyczna ilość przetwarzanych danych. Jednak w przypadku średnich ilości danych kod wykorzystujący algorytm O (n²) działający z szybkością C może z łatwością pokonać algorytm O (n log n), który wykonuje większość swojej pracy z pojedynczymi operacjami w języku Python.
Wiele z opublikowanych odpowiedzi na to pytanie polegające na liczeniu inwersji używa algorytmu opartego na łączeniu. Teoretycznie jest to dobre podejście, chyba że rozmiar tablicy jest bardzo mały. Ale wbudowany w Pythona TimSort (hybrydowy stabilny algorytm sortowania, wywodzący się z sortowania przez scalanie i sortowanie przez wstawianie) działa z szybkością C, a scalanie zakodowane ręcznie w Pythonie nie może mieć nadziei na konkurowanie z nim pod względem szybkości.
Jedno z bardziej intrygujących rozwiązań tutaj, w odpowiedzi opublikowanej przez Niklasa B , wykorzystuje wbudowane sortowanie do określenia rankingu elementów tablicy oraz binarne drzewo indeksowane (aka drzewo Fenwicka) do przechowywania skumulowanych sum wymaganych do obliczenia inwersji liczyć. Próbując zrozumieć tę strukturę danych i algorytm Niklasa, napisałem kilka własnych odmian (zamieszczonych poniżej). Ale odkryłem również, że w przypadku umiarkowanych rozmiarów list w rzeczywistości szybciej jest używać wbudowanej
sum
funkcji Pythona niż uroczego drzewa Fenwicka.W końcu, gdy rozmiar listy osiągnie około 500, aspekt O (n²) wywołania
sum
wewnątrz tejfor
pętli powraca do góry nogami i wydajność zaczyna spadać.Mergesort nie jest jedynym sortowaniem O (nlogn), a kilka innych można wykorzystać do wykonywania zliczania inwersji. Odpowiedź prasadvka używa binarnego sortowania drzewa, jednak jego kod wydaje się być w C ++ lub jednej z jego pochodnych. Więc dodałem wersję Pythona. Pierwotnie użyłem klasy do implementacji węzłów drzewa, ale odkryłem, że dyktowanie jest zauważalnie szybsze. Ostatecznie użyłem listy, która jest jeszcze szybsza, chociaż sprawia, że kod jest trochę mniej czytelny.
Jedną z zalet treeort jest to, że dużo łatwiej jest wdrożyć iteracyjnie niż scalanie. Python nie optymalizuje rekurencji i ma limit głębokości rekurencji (chociaż można go zwiększyć, jeśli naprawdę tego potrzebujesz). Oczywiście wywołania funkcji w Pythonie są stosunkowo wolne, więc kiedy próbujesz zoptymalizować pod kątem szybkości, dobrze jest unikać wywołań funkcji, jeśli jest to praktyczne.
Kolejny rodzaj O (nlogn) to czcigodny rodzaj radix. Jego dużą zaletą jest to, że nie porównuje kluczy ze sobą. Wadą jest to, że działa najlepiej na ciągłych sekwencjach liczb całkowitych, najlepiej na permutacji liczb całkowitych,
range(b**m)
gdzieb
zwykle jest 2. Dodałem kilka wersji opartych na sortowaniu radix po próbie odczytania zliczania inwersji, liczenia ortogonalnego zakresu offline i powiązanych problemów, czyli związane z obliczaniem liczby „inwersji” w permutacji .Aby efektywnie używać sortowania radix do liczenia inwersji w ogólnej sekwencji
seq
o długości n, możemy utworzyć permutację,range(n)
która ma taką samą liczbę inwersji jakseq
. Możemy to zrobić w (w najgorszym) czasie O (nlogn) za pośrednictwem TimSort. Sztuczka polega na permutowaniu indeksówseq
przez sortowanieseq
. Łatwiej to wyjaśnić na małym przykładzie.wynik
Sortując pary (wartość, indeks)
seq
, permutowaliśmy indeksyseq
z taką samą liczbą swapów, które są wymagane do ułożeniaseq
w pierwotnym porządku z sortowanego porządku. Możemy utworzyć tę permutację, sortującrange(n)
za pomocą odpowiedniej funkcji klucza:wynik
Możemy tego uniknąć
lambda
stosującseq
„s.__getitem__
metody:Jest to tylko trochę szybsze, ale szukamy wszystkich ulepszeń szybkości, jakie możemy uzyskać. ;)
Poniższy kod przeprowadza
timeit
testy wszystkich istniejących algorytmów Pythona na tej stronie, a także kilku moich własnych: kilka wersji brutalnej siły O (n²), kilka odmian algorytmu Niklasa B i oczywiście jeden oparty na połączeniu (które pisałem bez odwoływania się do istniejących odpowiedzi). Zawiera również mój kod treeort oparty na liście, z grubsza pochodzący z kodu prasadvk, i różne funkcje oparte na sortowaniu radix, niektóre używają strategii podobnej do podejścia scalania, a niektóre używająsum
drzewa Fenwicka.Ten program mierzy czas wykonania każdej funkcji na szeregu losowych list liczb całkowitych; może również sprawdzić, czy każda funkcja daje takie same wyniki, jak inne i czy nie modyfikuje listy wejściowej.
Każde
timeit
wywołanie daje wektor zawierający 3 wyniki, które sortuję. Główną wartością patrzeć tutaj jest jeden minimalny, inne wartości jedynie wskazywać na ile wiarygodne jest to wartość minimalna, jak omówiono w nocie w tychtimeit
docs modułowych .Niestety, wynik tego programu jest zbyt duży, aby uwzględnić go w tej odpowiedzi, więc zamieszczam go w jego własnej odpowiedzi (wiki społeczności) .
Wynik pochodzi z 3 uruchomień na mojej starożytnej 32-bitowej jednordzeniowej maszynie 2GHz z Pythonem 3.6.0 na starej dystrybucji pochodnej Debiana. YMMV. Podczas testów zamknąłem przeglądarkę internetową i rozłączyłem się z routerem, aby zminimalizować wpływ innych zadań na procesor.
Pierwsze uruchomienie testuje wszystkie funkcje o rozmiarach list od 5 do 320, z rozmiarami pętli od 4096 do 64 (gdy rozmiar listy się podwoi, rozmiar pętli zmniejsza się o połowę). Pula losowa użyta do skonstruowania każdej listy jest o połowę mniejsza od samej listy, więc prawdopodobnie otrzymamy wiele duplikatów. Niektóre algorytmy liczenia inwersji są bardziej wrażliwe na duplikaty niż inne.
Drugi przebieg wykorzystuje większe listy: od 640 do 10240 i stałą pętlę o rozmiarze 8. Aby zaoszczędzić czas, eliminuje kilka najwolniejszych funkcji z testów. My brute-force O funkcje (n²) to tylko sposób zbyt powolny w tych rozmiarach, jak wspomniano wcześniej, mój kod, który używa
sum
, która robi tak dobrze na małych i średnich list, po prostu nie może nadążyć na dużych listach.Ostatnie uruchomienie obejmuje rozmiary list od 20480 do 655360 i stały rozmiar pętli 4, z 8 najszybszymi funkcjami. W przypadku list o rozmiarach poniżej 40 000 lub więcej kod Tima Babycha jest wyraźnym zwycięzcą. Dobra robota Tim! Kod Niklasa B jest również dobrym wszechstronnym narzędziem, chociaż na mniejszych listach jest pokonany. Kod „python” oparty na bisekcji również radzi sobie całkiem nieźle, chociaż wydaje się być trochę wolniejszy w przypadku ogromnych list z wieloma duplikatami, prawdopodobnie z powodu tej liniowej
while
pętli, której używa do przechodzenia przez powtórzenia.Jednak w przypadku list o bardzo dużych rozmiarach algorytmy oparte na bisekcji nie mogą konkurować z prawdziwymi algorytmami O (nlogn).
Proszę zobaczyć tutaj wyjście
źródło
bisect
jest C? Jestem prawie pewien, że to Python.Liczbę inwersji można znaleźć, analizując proces scalania w sortowaniu przez scalanie:
Podczas kopiowania elementu z drugiej tablicy do tablicy merge (9 w tym przykładzie), zachowuje on swoje miejsce względem innych elementów. Podczas kopiowania elementu z pierwszej tablicy do tablicy merge (tutaj 5) jest on odwracany, a wszystkie elementy pozostają w drugiej tablicy (2 inwersje z 3 i 4). Tak więc niewielka modyfikacja sortowania przez scalanie może rozwiązać problem w O (n ln n).
Na przykład, po prostu odkomentuj dwa # wiersze w kodzie Pythona scalonego, aby uzyskać liczbę.
EDYCJA 1
To samo zadanie można osiągnąć dzięki stabilnej wersji szybkiego sortowania, znanej jako nieco szybsza:
Wybierając pivot jako ostatni element, inwersje są dobrze zliczane, a czas wykonania o 40% lepszy niż w przypadku scalenia powyższego.
EDYCJA 2
Aby uzyskać wydajność w Pythonie, wersja numpy & numba:
Najpierw część numpy, która używa argsort O (n ln n):
I część numba dla wydajnego podejścia BIT :
źródło
timeit
porównuje wszystkie odpowiedzi Pythona na to pytanie, więc zawiera Twój kod. Możesz być zainteresowany spojrzeniem na wyniki synchronizacji.timeit
kolekcji byłoby nieuczciwe.Zauważ, że odpowiedź Geoffreya Irvinga jest błędna.
Weźmy na przykład sekwencję {3, 2, 1}. Istnieją trzy inwersje: (3, 2), (3, 1), (2, 1), więc liczba inwersji to 3. Jednak zgodnie z cytowaną metodą odpowiedź brzmiałaby 2.
źródło
Sprawdź to: http://www.cs.jhu.edu/~xfliu/600.363_F03/hw_solution/solution1.pdf
Mam nadzieję, że da ci właściwą odpowiedź.
źródło
Oto jedno możliwe rozwiązanie ze zmiennością drzewa binarnego. Dodaje pole o nazwie rightSubTreeSize do każdego węzła drzewa. Kontynuuj wstawianie liczby do drzewa binarnego w kolejności, w jakiej pojawiają się w tablicy. Jeśli liczba idzie w lewo od węzła, licznik inwersji dla tego elementu wyniesie (1 + rightSubTreeSize). Ponieważ wszystkie te elementy są większe niż bieżący element i pojawiłyby się wcześniej w tablicy. Jeśli element trafi na prawą stronę węzła, po prostu zwiększ jego rightSubTreeSize. Poniżej znajduje się kod.
źródło
if(p->data < q->data)
przeciwnym razie porównanie to musi być duplikatem, który nie jest obsługiwany poprawnie. I nie ma potrzeby testowaniaq
na początku pętli, bezwarunkowawhile
pętla działa dobrze. Poza tym zapomniałeś wspomnieć, jaki to język. :) I wydaje się, że twoja funkcja utraciła swój nagłówek.źródło
Ponieważ jest to stare pytanie, udzielę odpowiedzi w C.
źródło
Oto rozwiązanie C ++
źródło
Ta odpowiedź zawiera wyniki
timeit
testów wygenerowanych przez kod w mojej głównej odpowiedzi . Zobacz tę odpowiedź, aby poznać szczegóły!źródło
Oto kod C do inwersji zliczania
Szczegółowe wyjaśnienie podano tutaj: http://www.geeksforgeeks.org/counting-inversions/
źródło
O (n log n) czas, O (n) rozwiązanie przestrzenne w java.
Scalanie z poprawką pozwalającą zachować liczbę inwersji wykonanych podczas etapu scalania. (aby uzyskać dobrze wyjaśnione połączenie, zajrzyj na http://www.vogella.com/tutorials/JavaAlgorithmsMergesort/article.html )
Ponieważ scalanie można wykonać na miejscu, złożoność przestrzeni można poprawić do O (1).
Przy takim sortowaniu inwersje zachodzą tylko w kroku scalania i tylko wtedy, gdy musimy wstawić element drugiej części przed elementami z pierwszej połowy, np.
połączony z
mamy 3 + 2 + 0 = 5 inwersji:
Po wykonaniu 5 inwersji nasza nowa połączona lista to 0, 1, 5, 6, 10, 15, 22
W witrynie Codility znajduje się zadanie demonstracyjne o nazwie ArrayInversionCount, w którym można przetestować swoje rozwiązanie.
źródło
Oto implementacja O (n * log (n)) perla:
źródło
Moja odpowiedź w Pythonie:
1- Najpierw posortuj tablicę i zrób jej kopię. W moim programie B reprezentuje posortowaną tablicę. 2- Powtórz oryginalną tablicę (nieposortowaną) i znajdź indeks tego elementu na posortowanej liście. Zanotuj również indeks elementu. 3- Upewnij się, że element nie ma żadnych duplikatów, jeśli tak, musisz zmienić wartość swojego indeksu o -1. Warunek while w moim programie dokładnie to robi. 4- Kontynuuj liczenie inwersji, która będzie wartością indeksu i usuń element po obliczeniu jego inwersji.
źródło
timeit
porównuje wszystkie odpowiedzi Pythona na to pytanie, więc zawiera Twój kod. Możesz być zainteresowany spojrzeniem na wyniki synchronizacji.Cóż, mam inne rozwiązanie, ale obawiam się, że zadziałaby tylko dla różnych elementów tablicy.
Aby wyjaśnić mój kod, dodajemy elementy od końca tablicy. Dla każdego przychodzącego elementu tablicy znajdujemy indeks pierwszego elementu w wektorze v, który jest większy niż nasz element przychodzący i przypisujemy tę wartość do inwersji liczby indeksu przychodzącego elementu Następnie wstawiamy ten element do wektora v w jego prawidłowej pozycji, tak aby wektor v pozostał w posortowanej kolejności.
źródło
Kolejne rozwiązanie Pythona, krótkie. Korzysta z wbudowanego modułu z połówką, który zapewnia funkcje wstawiania elementu w jego miejsce w posortowanej tablicy i znajdowania indeksu elementu w posortowanej tablicy.
Chodzi o to, aby przechowywać elementy po lewej stronie n-tej w takiej tablicy, co pozwoliłoby nam łatwo znaleźć ich liczbę większą niż n-tą.
źródło
timeit
porównuje wszystkie odpowiedzi Pythona na to pytanie, więc zawiera Twój kod. Możesz być zainteresowany spojrzeniem na wyniki synchronizacji. : DProstą odpowiedzią O (n ^ 2) jest użycie zagnieżdżonych pętli for i zwiększanie licznika dla każdej inwersji
Przypuszczam, że chcesz bardziej wydajnego rozwiązania, pomyślę o tym.
źródło
Jedno możliwe rozwiązanie w C ++ spełniające wymaganie złożoności czasowej O (N * log (N)) byłoby następujące.
Różni się od zwykłego sortowania przez scalanie tylko licznikiem.
źródło
Oto moje rozwiązanie O (n log n) w Rubim:
I kilka przypadków testowych:
źródło
Najlepszym zoptymalizowanym sposobem będzie rozwiązanie tego problemu przez sortowanie przez scalanie, gdzie po scaleniu możemy sprawdzić, ile inwersji jest wymaganych, porównując lewą i prawą tablicę. Ilekroć element po lewej tablicy jest większy niż element po prawej tablicy, będzie to inwersja.
Metoda sortowania przez scalanie: -
Oto kod. Kod jest dokładnie taki sam jak sortowanie przez scalanie, z wyjątkiem fragmentu kodu w
mergeToParent
metodzie, w której liczę inwersję w warunkach else(left[leftunPicked] < right[rightunPicked])
Inne podejście, w którym możemy porównać tablicę wejściową z tablicą posortowaną: - Ta implementacja odpowiedzi Diablo. Chociaż nie powinno to być preferowane podejście, ponieważ usunięcie n elementów z tablicy lub listy to log (n ^ 2).
źródło
Maksymalną liczbę możliwych inwersji dla listy rozmiarów
n
można uogólnić za pomocą wyrażenia:Więc dla tablicy o rozmiarze
6
maksymalne możliwe inwersje będą równe15
.Aby osiągnąć złożoność
n logn
, moglibyśmy zastosować algorytm inwersji podczas sortowania przez scalanie.Oto ogólne kroki:
inversionCount += leftSubArray.length
Otóż to!
To jest prosty przykład, który zrobiłem przy użyciu JavaScript:
źródło
Implementacja liczenia inwersji w tablicy z sortowaniem przez scalanie w Swift:
Zauważ, że liczba swapów jest zwiększana o
(czyli względna długość lewej strony tablicy pomniejszona o indeks bieżącego elementu po lewej stronie)
... ponieważ jest to liczba elementów, które element po prawej stronie tablicy musiał pominąć (liczba inwersji), aby zostać posortowanym.
źródło
Większość odpowiedzi opiera się na nich,
MergeSort
ale nie jest to jedyny sposób rozwiązania tego problemuO(nlogn)
Omówię kilka podejść.
Użyć
Balanced Binary Search Tree
Coś takiego.
Binary Indexed Tree
Segment Tree
[0, a[i]-1]
i updatea[i] with 1
Również podczas używania
BIT
lubSegment-Tree
dobrym pomysłem jest to zrobićCoordinate compression
źródło
C ++ Θ (n lg n) Rozwiązanie z wypisaniem pary składającej się na inwersję.
źródło
Użyj scalania, w kroku scalania incremeant licznika, jeśli liczba kopiowana do wyjścia pochodzi z prawej tablicy.
źródło
Ostatnio musiałem to zrobić w R:
źródło