O random.uniform (0,1) pode gerar 0 ou 1?

9

Na documentação , diz-se que há uma chance de uniform(0,1)gerar os valores 0e 1.

Já corri uniform(0, 1)10000 vezes, mas nunca produziu zero. Mesmo no caso de uniform(0, 0.001).

Já pode random.uniform(0,1)gerar 0ou 1?

Venkatesh Gandi
fonte
3
É teoricamente possível, mas praticamente nunca acontecerá. Matematicamente, uma variável aleatória uniforme padrão pode assumir qualquer valor no intervalo de 0 a 1. se X ~ U(0,1), então, P(X=x)é quase certamente 0, para todos os valores de x. (Isso ocorre porque existem infinitos valores possíveis no intervalo.) Se você está procurando exatamente 0 ou 1, deve usar uma função diferente - por exemplorandom.choice
interrompa
11
A @pault quase certamente tem um significado muito específico em matemática, o que realmente não faz sentido aqui, pois temos uma distribuição discreta e não um intervalo contínuo. Há apenas um número finito de carros alegóricos entre 0 e 1.
Wim
2
@pault Então, por que você está falando matematicamente quando o OP está perguntando sobre a implementação do random.uniform?
wim
11
Se essa documentação é precisa, eu sou do tipo curioso para saber como ele é feito, possivelmente, produzir tanto 0 e 1. Parece que [0, 1) seria muito mais fácil (que é como Math.random()funciona em JavaScript, por exemplo).
Ry-
11
50 pontos de recompensa para a primeira pessoa a postar uma semente aleatória que faz random.uniform(0, 1)retornar a 0 na primeira chamada
Wim

Respostas:

13

uniform(0, 1)pode produzir 0, mas nunca produzirá 1.

A documentação informa que o terminal b pode ser incluído nos valores produzidos:

O valor do ponto final bpode ou não ser incluído no intervalo, dependendo do arredondamento do ponto flutuante na equação a + (b-a) * random().

Portanto uniform(0, 1), a fórmula 0 + (1-0) * random(), simplificada para 1 * random(), teria que ser capaz de produzir 1exatamente. Isso só aconteceria se random.random()fosse 1.0 exactly. However,random () *never* produces1.0`.

Citando a random.random()documentação :

Retorne o próximo número de ponto flutuante aleatório no intervalo [0,0, 1,0).

A notação [..., ...)significa que o primeiro valor faz parte de todos os valores possíveis, mas o segundo não. random.random()vontade na maioria dos valores produzem muito perto de 1.0. O floattipo de Python é um valor de ponto flutuante IEEE 754 base64 , que codifica várias frações binárias (1/2, 1/4, 1/5 etc.) que compõem o valor, e o valor random.random()produzido é simplesmente a soma de uma seleção aleatória dessas 53 frações de 2 ** -1(1/2) a 2 ** -53(1/9007199254740992).

No entanto, como ele pode produzir valores muito próximos 1.0, juntamente com os erros de arredondamento que ocorrem quando você multiplica os nubmers de ponto flutuante, é possível produzir bpara alguns valores de ae b. Mas 0e 1não estão entre esses valores.

Observe que random.random() pode produzir 0,0, portanto, aé sempre incluído nos valores possíveis para random.uniform()( a + (b - a) * 0 == a). Como existem 2 ** 53valores diferentes que random.random()podem produzir (todas as combinações possíveis dessas 53 frações binárias), existe apenas uma 2 ** 53chance de 1 em (então 1 em 9007199254740992) de que isso aconteça.

Portanto, o maior valor possível que random.random()pode produzir é 1 - (2 ** -53); basta escolher um valor pequeno o suficiente para b - apermitir o arredondamento quando multiplicado por random.random()valores mais altos. Quanto menor b - a, maiores as chances de isso acontecer:

>>> import random, sys
>>> def find_b():
...     a, b = 0, sys.float_info.epsilon
...     while random.uniform(a, b) != b:
...         b /= 2
...     else:
...         return b
...
>>> print("uniform(0, {0}) == {0}".format(find_b()))
...
uniform(0, 4e-323) == 4e-323

Se você acertar b = 0.0, dividimos 1023 vezes, o valor acima significa que tivemos sorte após 1019 divisões. O valor mais alto que encontrei até agora (executando a função acima em um loop com max()) é 8.095e-320(1008 divisões), mas provavelmente existem valores mais altos. É tudo um jogo de azar. :-)

Também pode acontecer se não houver muitas etapas discretas entre ae b, como quando ae btiver um expoente alto e, portanto, parecer distante. Os valores de ponto flutuante ainda são apenas aproximações e o número de valores que eles podem codificar é finito. Por exemplo, há apenas 1 fração binária de diferença entre sys.float_info.maxe sys.float_info.max - (2 ** 970), portanto, há uma chance de 50-50 random.uniform(sys.float_info.max - (2 ** 970), sys.float_info.max)produz sys.float_info.max:

>>> a, b = sys.float_info.max - (2 ** 970), sys.float_info.max
>>> values = [random.uniform(a, b) for _ in range(10000)]
>>> values.count(sys.float_info.max)  # should be roughly 5000
4997
Martijn Pieters
fonte
5

"Várias vezes" não é suficiente. 10.000 não é suficiente. random.uniformescolhe entre 2 ^ 53 (9.007.199.254.740.992) valores diferentes. Você está interessado em dois deles. Como tal, você deve gerar vários valores aleatórios de quatrilhões antes de obter um valor exatamente 0 ou 1. Portanto, é possível, mas é muito provável que você nunca o observe.

hobbs
fonte
2
Pois uniform(0, 1)é impossível produzir 1como resultado. Isso ocorre porque a função é simplesmente definida como def uniform(a, b): return a + (b - a) * random()e random()nunca pode produzir 1.0.
Martijn Pieters
2
@MartijnPieters Acredito que você esteja correto e votei na sua resposta. Eu suspeitava disso, mas eu não tinha certeza, e foi além do principal impulso da minha resposta, então eu deixá-lo ser :)
hobbs
1

Você pode tentar gerar um loop que conte a quantidade de iterações necessárias para mostrar um 0 exato (não).

Além disso, como Hobbs afirmou, a quantidade de valores uniformlyamostrados é de 9.007.199.254.740.992. O que significa que a probabilidade de ver um 0 é exatamente 1 / 9.007.199.254.740.992. O que, em termos gerais e arredondamento, significa que você precisará, em média, de 10 quatrilhões de amostras para encontrar um 0. É claro que você pode encontrá-lo nas suas primeiras 10 tentativas, ou nunca.

A amostragem de 1 é impossível, pois o intervalo definido para os valores é fechado entre parênteses, portanto, não incluindo 1.

Celius Stingher
fonte
1

Certo. Você já estava no caminho certo ao tentar uniform(0, 0.001). Continue restringindo os limites o suficiente para que isso aconteça mais cedo.

>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
0.0
wim
fonte