Próbuję wygenerować losowy ciąg w Go i oto kod, który napisałem do tej pory:
package main
import (
"bytes"
"fmt"
"math/rand"
"time"
)
func main() {
fmt.Println(randomString(10))
}
func randomString(l int) string {
var result bytes.Buffer
var temp string
for i := 0; i < l; {
if string(randInt(65, 90)) != temp {
temp = string(randInt(65, 90))
result.WriteString(temp)
i++
}
}
return result.String()
}
func randInt(min int, max int) int {
rand.Seed(time.Now().UTC().UnixNano())
return min + rand.Intn(max-min)
}
Moja implementacja przebiega bardzo wolno. Rozstawianie przy użyciu time
przynosi tę samą liczbę losową przez pewien czas, więc pętla jest powtarzana wielokrotnie. Jak mogę ulepszyć swój kod?
Odpowiedzi:
Za każdym razem, gdy ustawisz to samo ziarno, otrzymasz tę samą sekwencję. Więc oczywiście, jeśli ustawiasz ziarno na czas w szybkiej pętli, prawdopodobnie będziesz go wielokrotnie wywoływać tym samym ziarnem.
W twoim przypadku, gdy wywołujesz swoją
randInt
funkcję, aż uzyskasz inną wartość, czekasz, aż czas (zwrócony przez Nano) się zmieni.Tak jak w przypadku wszystkich bibliotek pseudolosowych , ziarno należy ustawić tylko raz, na przykład podczas inicjowania programu, chyba że konkretnie potrzebujesz odtworzyć daną sekwencję (co jest zwykle wykonywane tylko w celu debugowania i testowania jednostkowego).
Następnie po prostu zadzwoń,
Intn
aby uzyskać następną losową liczbę całkowitą.Przenieś
rand.Seed(time.Now().UTC().UnixNano())
linię z funkcji randInt na początek main i wszystko będzie szybsze.Zwróć też uwagę, że myślę, że możesz uprościć tworzenie ciągów:
źródło
rand.Seed(...)
do funkcjiinit()
.init()
jest wywoływana automatycznie przedmain()
. Pamiętaj, że nie musisz dzwonićinit()
zmain()
!math/rand
tak nie jest bezpieczne kryptograficznie. Jeśli jest to wymagane,crypto/rand
należy użyć.Nie rozumiem, dlaczego ludzie wysiewają wartość czasu. Z mojego doświadczenia wynika, że nigdy nie był to dobry pomysł. Na przykład, podczas gdy zegar systemowy może być przedstawiany w nanosekundach, dokładność zegara systemu nie jest nanosekundami.
Ten program nie powinien być uruchamiany na placu zabaw Go, ale jeśli uruchomisz go na swojej maszynie, uzyskasz zgrubne oszacowanie, jakiego rodzaju precyzji możesz się spodziewać. Widzę przyrosty około 1000000 ns, więc przyrosty 1 ms. To 20 bitów entropii, które nie są używane. Przez cały czas wysokie bity są w większości stałe.
Stopień, w jakim ma to dla ciebie znaczenie, będzie różny, ale możesz uniknąć pułapek związanych z wartościami nasion opartymi na zegarze, po prostu używając
crypto/rand.Read
jako źródła nasion. Da ci to niedeterministyczną jakość, której prawdopodobnie szukasz w swoich liczbach losowych (nawet jeśli sama implementacja jest ograniczona do zestawu odrębnych i deterministycznych losowych sekwencji).Na marginesie, ale w odniesieniu do twojego pytania. Możesz tworzyć własne
rand.Source
za pomocą tej metody, aby uniknąć kosztów posiadania blokad chroniących źródło. Funkcjerand
narzędziowe pakietu są wygodne, ale używają również blokad pod maską, aby zapobiec jednoczesnemu używaniu źródła. Jeśli tego nie potrzebujesz, możesz tego uniknąć, tworząc własnąSource
i używaj jej w sposób inny niż współbieżny. Niezależnie od tego, NIE powinieneś przestawiać swojego generatora liczb losowych między iteracjami, nigdy nie został on zaprojektowany do takiego wykorzystania.źródło
tylko po to, aby wyrzucić go dla potomności: czasami lepiej jest wygenerować losowy ciąg przy użyciu początkowego zestawu znaków. Jest to przydatne, jeśli łańcuch ma być wprowadzony ręcznie przez człowieka; wykluczenie 0, O, 1 i l może pomóc w zmniejszeniu błędu użytkownika.
i zazwyczaj umieszczam ziarno wewnątrz
init()
bloku. Są udokumentowane tutaj: http://golang.org/doc/effective_go.html#initźródło
-1
wrand.Intn(len(alpha)-1)
. Dzieje się tak, ponieważrand.Intn(n)
zawsze zwraca liczbę, która jest mniejsza niżn
(innymi słowy: od zera don-1
włącznie).-1
inlen(alpha)-1
gwarantowałoby, że liczba 9 nigdy nie została użyta w sekwencji.OK, dlaczego takie skomplikowane!
Jest to oparte na kodzie dystroy, ale dopasowane do moich potrzeb.
It's die six (rands ints
1 =< i =< 6
)Powyższa funkcja to dokładnie to samo.
Mam nadzieję, że te informacje były przydatne.
źródło
3 5 2 5 4 2 5 6 3 1
rand.Intn()
, w przeciwnym razie zawsze otrzymasz ten sam numer za każdym razem, gdy uruchomisz program.var bytes int
? Jaka jest różnica w zmianie powyższegobytes = rand.Intn(6)+1
nabytes := rand.Intn(6)+1
? Oba wydają się działać dla mnie, czy jeden z nich z jakiegoś powodu nie jest optymalny?To nano sekundy, jakie są szanse na dwukrotne zdobycie tego samego ziarna.
W każdym razie dzięki za pomoc, oto moje końcowe rozwiązanie oparte na wszystkich danych wejściowych.
źródło
what are the chances of getting the exact the exact same [nanosecond] twice?
Doskonale. Wszystko zależy od wewnętrznej precyzji wykonania środowiska wykonawczego golang. Mimo że jednostki są nanosekundami, najmniejszy przyrost może wynosić milisekundę lub nawet sekundę.Jeśli twoim celem jest po prostu wygenerowanie żądła liczby losowej, myślę, że nie jest konieczne komplikowanie go za pomocą wielu wywołań funkcji lub resetowania nasion za każdym razem.
Najważniejszym krokiem jest wywołanie funkcji seed tylko raz przed faktycznym uruchomieniem
rand.Init(x)
. Seed używa podanej wartości inicjatora do zainicjowania domyślnego źródła do stanu deterministycznego. Dlatego sugeruje się wywołanie go raz przed właściwym wywołaniem funkcji generatora liczb pseudolosowych.Oto przykładowy kod tworzący ciąg liczb losowych
Powodem, dla którego użyłem Sprintf, jest to, że umożliwia proste formatowanie ciągów.
Ponadto In
rand.Intn(7)
Intn zwraca, jako liczbę int, nieujemną liczbę pseudolosową w [0,7).źródło
@ [Denys Séguret] wysłał poprawnie. Ale w moim przypadku potrzebuję nowego ziarna za każdym razem stąd poniższy kod;
Jeśli potrzebujesz szybkich funkcji. Używam w ten sposób.
źródło
źródło
Mała aktualizacja spowodowana zmianą golang api, pomiń .UTC ():
już czas(). UTC () .UnixNano () -> czas.Now (). UnixNano ()
źródło