2012-05-07 5 views
7

W innym wątku widziałem, że złożoność czasowa losowej próbki ważonej binarnymi stertami jest równa O (n * log (m)), gdzie n jest liczbą wyborów, a m jest liczbą węzłów do wyboru.Złożoność czasowa random.sample

Zastanawiam się nad złożonością czasową nieważonej próbki losowej, która jest używana przez Python jako random.sample. Czy złożoność czasu po prostu O (n) czy jest czymś zupełnie innym?

+5

To może być pouczające przeczytać [kod źródłowy dla 'random' moduł] (http://hg.python.org/cpython/file/ab500b297900/Lib/random.py). Funkcja 'random.sample()' jest zdefiniowana w linii 267. –

Odpowiedz

2

Źródło w języku Python: random.py (wiersz 267).

Oto odnośny bit:

315    selected = set() 
    316    selected_add = selected.add 
    317    for i in range(k): 
    318     j = randbelow(n) 
    319     while j in selected: 
    320      j = randbelow(n) 
    321     selected_add(j) 
    322     result[i] = population[j] 

To w zasadzie "rzuca kośćmi" random indeksem population. Jeśli otrzyma indeks, który jest już w zestawie selected, zostanie ponownie załadowany. Opłukać, spienić i powtórzyć k razy (gdzie k jest liczba próbek prosiłeś.)

Wydaje się O(n) w wielkości wymaganej liczby próbek. Istnieją pewne optymalizacje dla małych zestawów, ale mięso z rzeczy jest główną pętlą powyżej.


Edit:

wierzę linia 305-313 są szczególnym przypadku, gdy liczba próbek wymagane, k, to duża część całej populacji n. Zamiast toczyć losowe elementy z całej populacji (i przewracać, jeśli zderzamy się z elementem, który już wybraliśmy), jawnie utrzymujemy listę elementów, które musimy jeszcze wybrać. Gwarantujemy, że otrzymamy nowy element za każdym razem, ale kompromisem jest to, że musimy utrzymywać tę listę.

Jeśli źle zinterpretuję tę wypowiedź, prosimy o komentarz poniżej.

303   result = [None] * k 
    304   setsize = 21  # size of a small set minus size of an empty list 
    305   if k > 5: 
    306    setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets 
    307   if n <= setsize: 
    308    # An n-length list is smaller than a k-length set 
    309    pool = list(population) 
    310    for i in range(k):   # invariant: non-selected at [0,n-i) 
    311     j = randbelow(n-i) 
    312     result[i] = pool[j] 
    313     pool[j] = pool[n-i-1] # move non-selected item into vacancy 
    314   else: 
    315    selected = set() 
    316    selected_add = selected.add 
    317    for i in range(k): 
    318     j = randbelow(n) 
    319     while j in selected: 
    320      j = randbelow(n) 
    321     selected_add(j) 
    322     result[i] = population[j] 
    323   return result 
+1

Nie sądzę, że jest to O (n). Kiedy próbujesz pobrać próbkę blisko n elementów z listy n-elementowej, wówczas "wybrane" wypełnia się, powodując konieczność ponownego indeksowania matrycy w celu znalezienia nowego indeksu. Nie jestem do końca pewien co do złożoności tego, ale moim przeczuciem jest to, że spodziewany czas wewnętrznej pętli to O (n), co powoduje, że całość jest kwadratowa. –

+0

To nie wydaje się być "O (k)", jak mówisz, ze względu na pętlę 'while'. W rzeczywistości, jeśli 'k' jest bliskie' n', to zajmie to średnio 'O (n * logn)'. Najgorszy przypadek jest oczywiście nieskończony. – interjay

+0

@larsmans: Najgorszy przypadek to wybór elementów 'n' z populacji' n'. Uważam, że kod ma specjalny przypadek, który utrzymuje "pulę pozostałych elementów" (więc nie ma "chybionych pamięci podręcznych") zamiast "listy już wylosowanych elementów". Nie mogę jednak zgadnąć, co próbuje zrobić. –