Chciałbym wygenerować unikalne liczby losowe z zakresu od 0 do 1000, które nigdy się nie powtarzają (tj. 6 nie pojawia się dwukrotnie), ale to nie ucieka się do czegoś w rodzaju wyszukiwania O (N) poprzednich wartości, aby to zrobić. czy to możliwe?
algorithm
math
random
language-agnostic
dicroce
źródło
źródło
O(n)
w czasie lub pamięci), wiele z poniższych odpowiedzi jest błędnych, w tym odpowiedź zaakceptowana.Odpowiedzi:
Zainicjuj tablicę 1001 liczb całkowitych o wartościach od 0 do 1000 i ustaw zmienną max na bieżący maksymalny indeks tablicy (zaczynając od 1000). Wybierz losową liczbę, r, od 0 do max, zamień liczbę na pozycji r liczbą na pozycji max i zwróć liczbę na pozycji max. Zmniejsz maksymalnie o 1 i kontynuuj. Gdy max wynosi 0, ustaw max z powrotem na rozmiar tablicy - 1 i zacznij od nowa bez potrzeby ponownej inicjalizacji tablicy.
Aktualizacja: Chociaż wymyśliłem tę metodę samodzielnie, kiedy odpowiedziałem na pytanie, po kilku badaniach zdaję sobie sprawę, że jest to zmodyfikowana wersja Fisher-Yatesa znana jako Durstenfeld-Fisher-Yates lub Knuth-Fisher-Yates. Ponieważ opis może być trochę trudny do zrozumienia, poniżej podałem przykład (używając 11 elementów zamiast 1001):
Tablica zaczyna się od 11 elementów zainicjowanych w tablicy [n] = n, max zaczyna się od 10:
Przy każdej iteracji wybierana jest losowa liczba r z zakresu od 0 do max, tablica [r] i tablica [max] są zamieniane, zwracana jest nowa tablica [max], a wartość max jest zmniejszana:
Po 11 iteracjach wszystkie liczby w tablicy zostały wybrane, max == 0, a elementy tablicy są tasowane:
W tym momencie max można zresetować do 10 i proces może być kontynuowany.
źródło
N
iteracje (w tym przykładzie 11), aby uzyskać pożądany wynik, nie oznacza, że tak jestO(n)
? Ponieważ musisz wykonaćN
iteracje, aby uzyskaćN!
kombinacje z tego samego stanu początkowego, w przeciwnym razie wyjście będzie tylko jednym ze stanów N.Możesz to zrobić:
Więc to nie wymaga wyszukiwania starych wartości za każdym razem, ale nadal wymaga O (N) do początkowego tasowania. Ale jak zauważył Nils w komentarzach, jest to amortyzowane O (1).
źródło
Użyj rejestru przesunięcia maksymalnego liniowego sprzężenia zwrotnego .
Można go zaimplementować w kilku wierszach C, a w czasie wykonywania robi niewiele więcej niż kilka testów / gałęzi, trochę dodawania i nieco przesunięcia. To nie jest przypadkowe, ale większość ludzi oszukuje.
źródło
Możesz użyć liniowego generatora kongruencji . Gdzie
m
(moduł) byłby najbliższą liczbą pierwszą większą niż 1000. Kiedy otrzymasz liczbę spoza zakresu, po prostu uzyskaj następną. Sekwencja zostanie powtórzona dopiero po wystąpieniu wszystkich elementów i nie musisz używać tabeli. Należy jednak pamiętać o wadach tego generatora (w tym braku losowości).źródło
k
siebie w sekwencji nigdy nie mogą występować razem).Możesz użyć szyfrowania zachowującego format, aby zaszyfrować licznik. Twój licznik po prostu przechodzi od 0 w górę, a szyfrowanie używa wybranego klucza, aby przekształcić go w pozornie losową wartość o dowolnej podstawie i szerokości. Np. Na przykład w tym pytaniu: podstawa 10, szerokość 3.
Szyfry blokowe mają zwykle stały rozmiar bloku, np. 64 lub 128 bitów. Ale szyfrowanie z zachowaniem formatu pozwala ci wziąć standardowy szyfr, taki jak AES, i utworzyć szyfr o mniejszej szerokości, o dowolnej podstawie i szerokości, z algorytmem, który jest nadal niezawodny kryptograficznie.
Gwarantuje to, że nigdy nie wystąpią kolizje (ponieważ algorytmy kryptograficzne tworzą mapowanie 1: 1). Jest również odwracalne (mapowanie dwukierunkowe), więc możesz wziąć wynikową liczbę i wrócić do wartości licznika, od której zacząłeś.
Ta technika nie wymaga pamięci do przechowywania losowej tablicy itp., Co może być zaletą w systemach z ograniczoną pamięcią.
AES-FFX to jedna z proponowanych standardowych metod osiągnięcia tego celu. Eksperymentowałem z podstawowym kodem Pythona, który jest oparty na idei AES-FFX, chociaż nie jest w pełni zgodny - zobacz kod Pythona tutaj . Może np. Zaszyfrować licznik do losowo wyglądającej 7-cyfrowej liczby dziesiętnej lub 16-bitowej. Oto przykład podstawy 10, szerokość 3 (aby podać liczbę od 0 do 999 włącznie) zgodnie z pytaniem:
Aby uzyskać różne niepowtarzalne sekwencje pseudolosowe, zmień klucz szyfrowania. Każdy klucz szyfrowania tworzy inną niepowtarzalną pseudolosową sekwencję.
źródło
k
oddzielne w sekwencji nigdy nie mogą wystąpić razem).k
?1,2,...,N
sekwencją tych samych liczb w innej, ale wciąż stałej kolejności. Liczby są następnie wyciągane z tej sekwencji jeden po drugim.k
to liczba wybranych wartości (OP nie określił dla niej litery, więc musiałem ją wprowadzić).W przypadku niskich liczb, takich jak 0 ... 1000, tworzenie listy zawierającej wszystkie liczby i tasowanie jej jest proste. Ale jeśli zbiór liczb do losowania jest bardzo duży, istnieje inny elegancki sposób: możesz zbudować permutację pseudolosową za pomocą klucza i kryptograficznej funkcji skrótu. Zobacz poniższy przykładowy pseudokod w języku C ++:
Oto
hash
tylko dowolna funkcja pseudolosowa, która odwzorowuje ciąg znaków na prawdopodobnie dużą liczbę całkowitą bez znaku. Funkcjarandperm
jest permutacją wszystkich liczb w zakresie 0 ... pow (2, bity) -1 przy założeniu stałego klucza. Wynika to z konstrukcji, ponieważ każdy krok zmieniający zmiennąindex
jest odwracalny. Jest to inspirowane szyfrem Feistela .źródło
hash()
w powyższym kodzie jest bezpieczną funkcją pseudolosową, ta konstrukcja da wprawdzie (Luby i Rackoff, 1988) permutację pseudolosową , której nie można odróżnić od prawdziwego losowego tasowania przy użyciu znacznie mniejszego wysiłku niż wyczerpująca przeszukiwanie całej przestrzeni klucza, która jest wykładnicza w długości klucza. Nawet w przypadku kluczy o rozsądnych rozmiarach (powiedzmy 128-bitowych), to przekracza całkowitą moc obliczeniową dostępną na Ziemi.hash( key + "/" + int2str(temp) )
powyższą konstrukcję ad hoc HMAC , którego bezpieczeństwo z kolei można w sposób udokumentowany zredukować do poziomu podstawowej funkcji kompresji skrótu. Ponadto użycie HMAC może spowodować, że jest mniej prawdopodobne, że ktoś omyłkowo spróbuje użyć tej konstrukcji z niezabezpieczoną funkcją skrótu inną niż kryptograficzna.)Możesz użyć mojego algorytmu Xincrol opisanego tutaj:
http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html
Jest to czysto algorytmiczna metoda generowania losowych, ale unikalnych liczb bez tablic, list, permutacji lub dużego obciążenia procesora.
Najnowsza wersja pozwala również ustawić zakres liczb, na przykład, jeśli chcę unikalnych liczb losowych z zakresu 0-1073741821.
Praktycznie to wykorzystałem
Jest otwarte, bezpłatne. Spróbuj...
źródło
k
oddzielne w sekwencji nigdy nie mogą wystąpić razem).Nie potrzebujesz nawet tablicy, aby rozwiązać ten problem.
Potrzebujesz maski bitowej i licznika.
Zainicjuj licznik na zero i zwiększaj go przy kolejnych wywołaniach. XOR licznik z maską bitową (wybraną losowo podczas uruchamiania lub ustaloną), aby wygenerować liczbę psuedorandom. Jeśli nie możesz mieć liczb przekraczających 1000, nie używaj maski bitowej szerszej niż 9 bitów. (Innymi słowy, maska bitowa jest liczbą całkowitą nie większą niż 511).
Upewnij się, że gdy licznik przekroczy 1000, zresetujesz go do zera. W tym momencie możesz wybrać inną losową maskę bitową - jeśli chcesz - aby wygenerować ten sam zestaw liczb w innej kolejności.
źródło
Myślę, że liniowy generator kongruencji byłby najprostszym rozwiązaniem.
i są tylko trzy ograniczenia A , C i m wartościami
PS o metodzie już wspomniano, ale post zawiera błędne założenia co do stałych wartości. Poniższe stałe powinny działać dobrze w Twoim przypadku
W twoim przypadku można użyć
a = 1002
,c = 757
,m = 1001
źródło
Oto kod, który napisałem, który wykorzystuje logikę pierwszego rozwiązania. Wiem, że jest to „język agnostyk”, ale chciałem tylko przedstawić to jako przykład w C # na wypadek, gdyby ktoś szukał szybkiego praktycznego rozwiązania.
źródło
Ta metoda jest odpowiednia, gdy limit jest wysoki i chcesz wygenerować tylko kilka liczb losowych.
Zwróć uwagę, że liczby są generowane w kolejności rosnącej, ale możesz później tasować.
źródło
(top,n)=(100,10)
są następujące:(0.01047705, 0.01044825, 0.01041225, ..., 0.0088324, 0.008723, 0.00863635)
. Testowałem w Pythonie, więc niewielkie różnice w matematyce mogą tu odgrywać rolę (upewniłem się, że wszystkie operacje obliczeniower
są zmiennoprzecinkowe).Możesz użyć dobrego generatora liczb pseudolosowych z 10 bitami i odrzucić od 1001 do 1023, pozostawiając od 0 do 1000.
Od tutaj mamy projekt dla 10-bitowego PRNG ..
10 bitów, wielomian sprzężenia zwrotnego x ^ 10 + x ^ 7 + 1 (okres 1023)
użyj Galois LFSR, aby uzyskać szybki kod
źródło
N Niepowtarzające się liczby losowe będą miały złożoność O (n), zgodnie z wymaganiami.
Uwaga: losowość powinna być statyczna z zastosowanym zabezpieczeniem nici.
źródło
Powiedzmy, że chcesz przeglądać potasowane listy w kółko, bez
O(n)
opóźnienia za każdym razem, gdy zaczynasz od początku, aby ponownie tasować, w takim przypadku możemy zrobić to:Utworzenie 2 list A i B, od 0 do 1000, zajmuje
2n
miejsce.Przetasuj listę A przy użyciu Fisher-Yatesa, wymaga
n
czasu.Rysując liczbę, wykonaj jednoetapowe tasowanie Fishera-Yatesa na drugiej liście.
Gdy kursor znajduje się na końcu listy, przełącz się na inną listę.
Przetwarzaj wstępnie
Remis
źródło
[1,3,4,5,2]
da taki sam wynik jak tasowanie[1,2,3,4,5]
.Pytanie Jak skutecznie wygenerować listę K nie powtarzających się liczb całkowitych od 0 do górnej granicy N jest połączone jako duplikat - a jeśli chcesz coś, co jest O (1) na wygenerowaną liczbę losową (bez O (n)) koszt uruchomienia)) istnieje prosta modyfikacja zaakceptowanej odpowiedzi.
Utwórz pustą mapę nieuporządkowaną (pusta mapa uporządkowana pobierze O (log k) na element) od liczby całkowitej do liczby całkowitej - zamiast używać zainicjowanej tablicy. Ustaw max na 1000, jeśli to jest maksimum,
Jedyną różnicą w porównaniu z użyciem zainicjalizowanej tablicy jest to, że inicjalizacja elementów jest odkładana / pomijana - ale wygeneruje dokładnie te same liczby z tego samego PRNG.
źródło
Inna możliwość:
Możesz użyć tablicy flag. I weź następny, gdy jest już wybrany.
Ale uważaj po 1000 wywołań, funkcja nigdy się nie skończy, więc musisz zrobić zabezpieczenie.
źródło
Oto przykładowy kod w języku COBOL, z którym możesz się pobawić.
Mogę wysłać Ci plik RANDGEN.exe, żebyś mógł się nim bawić i zobaczyć, czy chce, żebyś tego chciał.
źródło
Większość odpowiedzi nie gwarantuje, że nie zwrócą dwa razy tego samego numeru. Oto poprawne rozwiązanie:
Nie jestem pewien, czy ograniczenie jest dobrze określone. Zakłada się, że po 1000 innych danych wyjściowych wartość może się powtórzyć, ale to naiwnie pozwala 0 następować natychmiast po zera, o ile oba pojawiają się na końcu i na początku serii 1000. I odwrotnie, podczas gdy można zachować odległość 1000 innych wartości między powtórzeniami, wymusza to sytuację, w której sekwencja powtarza się dokładnie w ten sam sposób za każdym razem, ponieważ żadna inna wartość nie wystąpiła poza tym limitem.
Oto metoda, która zawsze gwarantuje co najmniej 500 innych wartości, zanim wartość będzie mogła zostać powtórzona:
źródło
Gdy N jest większe niż 1000 i musisz narysować K losowych próbek, możesz użyć zestawu, który zawiera próbki do tej pory. Dla każdego losowania używasz próbkowania odrzucania , które będzie operacją "prawie" O (1), więc całkowity czas pracy jest prawie O (K) z pamięcią O (N).
Algorytm ten napotyka na kolizje, gdy K jest „blisko” N. Oznacza to, że czas pracy będzie dużo gorszy niż O (K). Prostym rozwiązaniem jest odwrócenie logiki, tak aby dla K> N / 2 zachować zapis wszystkich próbek, które nie zostały jeszcze narysowane. Każde losowanie usuwa próbkę z zestawu odrzucania.
Innym oczywistym problemem związanym z próbkowaniem odrzucania jest to, że jest to pamięć O (N), co jest złą wiadomością, jeśli N jest w miliardach lub więcej. Istnieje jednak algorytm, który rozwiązuje ten problem. Algorytm ten nazywa się algorytmem Vittera od nazwiska jego wynalazcy. Algorytm został opisany tutaj . Istota algorytmu Vittera polega na tym, że po każdym losowaniu obliczasz losowe pominięcie przy użyciu określonego rozkładu, który gwarantuje jednolite próbkowanie.
źródło
Fisher Yates
W rzeczywistości jest to O (n-1) ponieważ potrzebujesz tylko jednej zamiany na ostatnie dwa
To jest C #
źródło
Zobacz moją odpowiedź na https://stackoverflow.com/a/46807110/8794687
Jest to jedna z najprostszych algorytmów, które mają średni czas złożoność O ( y log s ) y oznaczającą wielkość próbki. Znajdują się tam również linki do algorytmów tablicy skrótów, których złożoność jest określana jako O ( s ).
źródło
Ktoś napisał „tworzenie liczb losowych w programie Excel”. Używam tego ideału. Utwórz strukturę z 2 części, str.index i str.ran; Dla 10 liczb losowych utwórz tablicę 10 struktur. Ustaw indeks str. Od 0 do 9 i str .ran na inną liczbę losową.
Sortuj tablicę według wartości w arr [i] .ran. Indeks str. Ma teraz losową kolejność. Poniżej znajduje się kod c:
źródło