random.choice from set? Pitão

96

Estou trabalhando em uma parte de IA de um jogo de adivinhação. Eu quero que o AI selecione uma letra aleatória desta lista. Estou fazendo isso como um conjunto para poder remover facilmente as letras da lista à medida que são adivinhadas no jogo e, portanto, não estão mais disponíveis para serem adivinhadas novamente.

diz que o setobjeto não é indexável. Como posso contornar isso?

import random 
aiTurn=True

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



    print (aiGuess) 
Jamyn
fonte
1
Incidentalmente, você não precisa usar set (list ('string')) para obter um conjunto de letras, pois as strings são iteráveis ​​por si mesmas - set ('abc') fará o que você quiser.
Scott Ritchie
5
Para outras pessoas que estão enfrentando esse problema, vale a pena olhar para esta questão sobre como criar um objeto semelhante a um conjunto que permite uma seleção aleatória eficiente. As opções fornecidas aqui são todas O (N). stackoverflow.com/q/15993447/2966723
Joel de

Respostas:

95

Observação (outubro de 2020): a partir da v3.9, o Python tornou oficialmente obsoleto o random.sample() trabalho em conjuntos, com a orientação oficial de converter explicitamente o conjunto em uma lista ou tupla antes de transmiti-lo.


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

Documentação: https://docs.python.org/3/library/random.html#random.sample

NPE
fonte
9
Prenda um [0]no final para que seja basicamente idêntico a random.choice(que não retorna seus valores na forma de uma lista)
Nick T
31
random.samplefaz tuple(population)internamente, então random.choice(tuple(allLetters))pode ser melhor.
utapyngo de
22
Deve-se ressaltar que este processo é O (N).
Joel
@Joel Por que esse processo é O (N)?
ManuelSchneid3r
3
Acho que é realmente ineficiente ... Como você pode ver github.com/python/cpython/blob/2.7/Lib/random.py#L332-L339, a função de exemplo cria uma lista do conjunto sempre que você faz a chamada acima e pega um elemento aleatório dele. Suponha que você tenha um grande conjunto e queira fazer muitas amostras. Se o conjunto não mudar, é melhor convertê-lo em uma lista e usar random.choice. Se o conjunto também mudar enquanto você faz a amostragem, provavelmente você não deveria usar um conjunto. Se você soubesse os hashes ocupados no conjunto e os tamanhos dos baldes, seria fácil escrever uma função de amostragem ...
jakab922
59

Você deve usar random.choice(tuple(myset)), porque é mais rápido e parece mais limpo do que random.sample. Escrevi o seguinte para testar:

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

Pelos números, parece que random.sampleleva 7% a mais.

Scott Ritchie
fonte
2
Na minha máquina, random.choice é 7 vezes mais rápido.
noɥʇʎԀʎzɐɹƆ
5
Não há como selecionar diretamente do conjunto, sem ter que copiá-lo para a tupla?
Youda008
Eu obtenho a amostra sendo aproximadamente 12% (250 ms) mais lento do que escolher em um conjunto de 5000 elementos.
Simon
1
Na minha máquina, random.sampledeixa de ser mais lento do que random.choicepara ser mais rápido do que à medida que o tamanho do conjunto aumenta (o ponto de cruzamento está em algum lugar entre o tamanho do conjunto 100k-500k). Ou seja, quanto maior o conjunto, maior a probabilidade random.samplede ser mais rápido.
jakee