random.choice z zestawu? pyton

96

Pracuję nad częścią sztucznej inteligencji w grze w zgadywanie. Chcę, żeby SI wybrała losową literę z tej listy. Robię to jako zestaw, więc mogę łatwo usunąć litery z listy, gdy zostaną odgadnięte w grze i dlatego nie można ich już odgadnąć ponownie.

mówi, że setobiekt nie jest indeksowalny. Jak mogę to obejść?

import random 
aiTurn=True

while aiTurn == True:
    allLetters = set(list('abcdefghijklmnopqrstuvwxyz'))
    aiGuess=random.choice(allLetters)



    print (aiGuess) 
jamyn
źródło
1
Nawiasem mówiąc, nie musisz używać set (list ('string')), aby uzyskać zestaw liter, ponieważ łańcuchy są iterowalne same - set ('abc') zrobi, co chcesz.
Scott Ritchie
5
Dla innych borykających się z tym problemem warto przyjrzeć się temu pytaniu o to, jak stworzyć obiekt podobny do zestawu, który pozwala na efektywny losowy wybór. Podane tutaj opcje to O (N). stackoverflow.com/q/15993447/2966723
Joel

Odpowiedzi:

95

Uwaga (październik 2020 r.): Od wersji 3.9 Python oficjalnie wycofał random.sample() pracę z zestawami, przy czym oficjalne wytyczne dotyczą jawnej konwersji zestawu na listę lub krotkę przed przekazaniem go.


>>> random.sample(set('abcdefghijklmnopqrstuvwxyz'), 1)
['f']

Dokumentacja: https://docs.python.org/3/library/random.html#random.sample

NPE
źródło
9
Skoncentruj się na a [0]na końcu, więc jest w zasadzie identyczny z random.choice(który nie zwraca swoich wartości w formie listy)
Nick T
31
random.samplerobi tuple(population)wewnętrznie, więc random.choice(tuple(allLetters))może być lepiej.
utapyngo
22
Należy podkreślić, że ten proces to O (N).
Joel
@Joel Dlaczego ten proces to O (N)?
ManuelSchneid3r
3
Myślę, że to naprawdę nieefektywne ... Jak widać github.com/python/cpython/blob/2.7/Lib/random.py#L332-L339 funkcja przykładowa tworzy listę z zestawu za każdym razem, gdy wykonujesz powyższe wywołanie i pobiera z niej losowy element. Załóżmy, że masz duży zestaw i chcesz wykonać wiele próbek. Jeśli zestaw się nie zmienia, lepiej przekonwertować go na listę i użyć random.choice. Jeśli zestaw zmienia się również podczas samplowania, prawdopodobnie nie powinieneś w ogóle używać zestawu. Gdybyś znał zajmowane hashe w zestawie i rozmiary wiadra, łatwo byłoby napisać funkcję próbkowania ...
jakab922
59

Powinieneś użyć random.choice(tuple(myset)), ponieważ jest szybszy i prawdopodobnie czystszy niż random.sample. Napisałem do przetestowania:

import random
import timeit

bigset = set(random.uniform(0,10000) for x in range(10000))

def choose():
    random.choice(tuple(bigset))

def sample():
    random.sample(bigset,1)[0]

print("random.choice:", timeit.timeit(choose, setup="global bigset", number=10000)) # 1.1082136780023575
print("random.sample:", timeit.timeit(sample, setup="global bigset", number=10000)) # 1.1889629259821959

Z liczb wynika, że random.sampletrwa to 7% dłużej.

Scott Ritchie
źródło
2
Na moim komputerze random.choice jest 7 razy szybsze.
noɥʇʎԀʎzɐɹƆ
5
Nie ma sposobu, aby wybrać bezpośrednio z zestawu bez konieczności kopiowania go do krotki?
Youda008
Próbka jest około 12% (250 ms) wolniejsza niż wybrana na zestawie 5000 elementów.
Simon,
1
Na moim komputerze random.samplezmienia się z wolniejszego niż random.choiceszybszego, gdy ustawiony rozmiar rośnie (punkt przecięcia znajduje się gdzieś pomiędzy ustawionym rozmiarem 100k-500k). Oznacza to, że im większy zestaw, tym większe prawdopodobieństwo, że random.samplebędzie szybszy.
jakee