랜덤은 거의 랜덤하지 않습니까?


79

randint의 무작위성을 테스트하기 위해 이렇게했습니다.

>>> from random import randint
>>>
>>> uniques = []
>>> for i in range(4500):  # You can see I was optimistic.
...     x = randint(500, 5000)
...     if x in uniques:
...         raise Exception('We duped %d at iteration number %d' % (x, i))
...     uniques.append(x)
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
Exception: We duped 887 at iteration number 7

나는 약 10 번 더 시도했고 가장 좋은 결과는 리피터 이전에 121 번의 반복이었다. 이것이 표준 라이브러리에서 얻을 수있는 최상의 결과입니까?


56
"실용적인 프로그래머", 규칙 26. "선택"은 깨지지 않습니다. OS 나 컴파일러 또는 타사 제품이나 라이브러리에서 버그를 찾는 경우는 거의 없습니다. 버그는 응용 프로그램에있을 가능성이 높습니다. 또는이 경우 확률 이론의 적용.

11
nitpicking : uniques = set () 및 uniques.add (x)가 더 적절할 것입니다 (효율적).
Eric O Lebigot

22
생일 역설의 주요 속성 중 하나는 반 직관적이라는 것입니다. 당신이 그것을 알고 있거나 확률 이론에 대한 배경 지식이 없다면, 당신은 그것을 위해 키워드 검색을 할 이유가 반드시 없을 것입니다. USP의 Q & A 사이트 중 하나는 무엇을 검색해야할지 모르고 순수 키워드 검색을 수행하면 실제로 질문에 대한 답변과 일치하지 않는 용어로 질문 할 수 있다는 것입니다.
ConcernedOfTunbridgeWells

7
@okoku : (ConcernedOfTunbridge에 대한 귀하의 답변에 관하여) : 당신이 말하는 것은 완전히 다른 문제입니다. 하나는 같은 카드를 연속으로 두 번받을 확률입니다. 다른 하나는지고의 확률을 모든 N 픽 후 이전의 N-1 카드. 두 번째 문제에 대한 완벽한 RNG 의 평균 카드 수는 약 67 개 여야합니다. 당신이 8에서 121 사이에 있다는 것을 고려할 때, 그것은 맞습니다.
BlueRaja-Danny Pflughoeft

5
Random과 Evenly Distributed를 혼동하고 있습니다. 무작위 생성기가 정확히 동일한 값을 여러 번 반환하는 것은 완벽하게 유효합니다. 완전히 다른 문제인 균등 분산 난수 생성기를 원한다면 생성기 문제가 아닌 셔플 링 문제입니다.

답변:


287

생일 패러독스 또는 PRNG가 생각보다 더 자주 중복을 생성하는 이유.


OP의 문제에는 몇 가지 문제가 있습니다. 하나는 위에서 언급 한 생일 역설 이고 두 번째는 생성하는 항목의 특성으로, 주어진 숫자가 반복되지 않는다는 것을 본질적으로 보장하지는 않습니다.

Birthday Paradox는 주어진 값이 생성기 기간 동안 두 번 이상 발생할 수있는 경우 적용됩니다. 따라서 값 샘플 내에서 중복이 발생할 수 있습니다. 생일 패러독스의 효과는 그러한 복제물을 얻을 수있는 실제 가능성이 상당히 중요하고 그 사이의 평균 기간이 생각보다 짧다는 것입니다. 지각 된 확률과 실제 확률 사이의 이러한 불협화음으로 인해 Birthday Paradox 는 순진한 직관적 추정이 매우 잘못 될 가능성 이있는 인지 편향 의 좋은 예입니다 .

의사 난수 생성기 (PRNG)에 대한 간단한 입문서

문제의 첫 번째 부분은 난수 생성기의 노출 된 값을 훨씬 더 작은 숫자로 변환하여 가능한 값의 공간이 줄어든다는 것입니다. 일부 의사 난수 생성기 는 해당 기간 동안 값을 반복하지 않지만이 변환은 도메인을 훨씬 더 작은 도메인으로 변경합니다. 더 작은 도메인은 '반복 없음'조건을 무효화하므로 상당한 반복 가능성을 기대할 수 있습니다.

예를 들면 같은 일부 알고리즘, 선형 합동 PRNG는 ( A'=AX|M) 전체 기간 동안 보증 고유성을. LCG에서 생성 된 값에는 누산기의 전체 상태가 포함되며 추가 상태는 유지되지 않습니다. 생성기는 결정적이며 기간 내에서 숫자를 반복 할 수 없습니다. 주어진 누산기 값은 가능한 연속 값을 하나만 의미 할 수 있습니다. 따라서 각 값은 생성기 기간 내에 한 번만 발생할 수 있습니다. 그러나 이러한 PRNG의 기간은 상대적으로 작으며 (LCG 알고리즘의 일반적인 구현의 경우 약 2 ^ 30) 고유 값의 수보다 클 수 없습니다.

모든 PRNG 알고리즘이이 특성을 공유하는 것은 아닙니다. 일부는 기간 내에 주어진 값을 반복 할 수 있습니다. OP의 문제에서 Mersenne Twister 알고리즘 (Python의 임의 모듈 에서 사용됨 )은 2 ^ 32보다 훨씬 긴 기간을 갖습니다. Linear Congruential PRNG와 달리 누산기에 추가 상태가 포함되어 있으므로 결과는 순수하게 이전 출력 값의 함수가 아닙니다. 32 비트 정수 출력과 ~ 2 ^ 19937의 기간으로 이러한 보장을 제공 할 수 없습니다.

Mersenne Twister는 우수한 통계적 및 기하학적 특성과 매우 오랜 기간 동안 시뮬레이션 모델에 사용되는 PRNG에 바람직한 특성을 가지고 있기 때문에 PRNG에 널리 사용되는 알고리즘입니다.

  • 좋은 통계적 속성 은 알고리즘에 의해 생성 된 숫자가 다른 숫자보다 나타날 확률이 훨씬 높은 숫자없이 균등하게 분포되어 있음을 의미합니다. 통계적 속성이 좋지 않으면 결과에 원치 않는 왜곡이 발생할 수 있습니다.

  • 좋은 기하학적 특성 은 N 개의 숫자 집합이 N 차원 공간의 초평면에 있지 않음을 의미합니다. 잘못된 기하학적 특성은 시뮬레이션 모델에서 잘못된 상관 관계를 생성하고 결과를 왜곡 할 수 있습니다.

  • 기간이 길다는 것은 시퀀스가 ​​시작될 때까지 많은 숫자를 생성 할 수 있음을 의미합니다. 모델에 많은 반복이 필요하거나 여러 시드에서 실행되어야하는 경우 일반적인 LCG 구현에서 사용할 수있는 2 ^ 30 정도의 이산 숫자로는 충분하지 않을 수 있습니다. MT19337 알고리즘은 2 ^ 19337-1 또는 약 10 ^ 5821이라는 매우 긴 기간을 갖습니다. 이에 비해 우주의 총 원자 수는 약 10 ^ 80 개로 추정됩니다.

MT19337 PRNG에 의해 생성 된 32 비트 정수는 그러한 긴 기간 동안 반복을 피하기 위해 충분한 이산 값을 나타낼 수 없습니다. 이 경우 중복 값이 ​​발생할 가능성이 높으며 샘플이 충분히 크면 불가피합니다.

간단히 말해서 생일 역설

이 문제는 원래 방에있는 두 사람이 같은 생일을 공유 할 확률로 정의됩니다. 요점은 방에 있는 두 사람이 생일을 같이 할 수 있다는 것입니다. 사람들은 방에있는 누군가가 특정 개인과 생일을 공유 할 확률로 문제를 순진하게 오해하는 경향이 있으며, 이는 사람들이 확률을 과소 평가하게 만드는 인지 편향의 원인입니다. 이것은 잘못된 가정입니다. 특정 개인과 일치해야 할 필요가 없으며 두 개인이 일치 할 수 있습니다.

이 그래프는 방에있는 사람들의 수가 증가함에 따라 생일을 공유 할 확률을 보여줍니다.  23 명의 경우 두 사람이 생일을 함께 할 확률은 50 %를 약간 넘습니다.

두 개인간에 일치가 발생할 확률은 특정 날짜에 일치 할 필요가 없기 때문에 특정 개인과 일치 할 확률보다 훨씬 높습니다. 오히려 생일이 같은 사람을 두 명만 찾으면됩니다. 이 그래프 (주제에 대한 Wikipedia 페이지에서 찾을 수 있음)에서 우리는 방에 23 명만 있으면 이런 식으로 일치하는 두 사람을 찾을 확률이 50 %임을 알 수 있습니다.

주제에 대한 Wikipedia 항목에서 멋진 요약을 얻을 수 있습니다 . 영업 이익의 문제에서, 우리는 우리의 확률을 알고 싶어 ( '사람'으로 동일시) 생성 임의의 값의 주어진 숫자에 대해 4,500 가능한 '생일'보다는 365이 어떤 순서에서 나타나는 두 개의 동일한 값을.

OP 문제에 대한 Birthday Paradox의 가능한 영향 계산

100 개의 숫자 시퀀스에 대해 잠재적으로 일치 할 수있는 (100 * 99) / 2 = 4950 쌍 ( 문제 이해 참조 )이 있습니다 (즉, 첫 번째는 두 번째, 세 번째 등과 일치 할 수 있고 두 번째는 세 번째, 네 번째 등과 일치 할 수 있음). 잠재적으로 일치 할 수있는 조합 의 수는 100 개가 아닙니다.

에서 확률을 계산 우리의 식을 얻을 1-(4500! / (4500 ** 100 * (4500-100)!) . 아래의 Python 코드 스 니펫은 일치하는 쌍이 발생할 확률에 대한 순진한 평가를 수행합니다.

# === birthday.py ===========================================
#
from math import log10, factorial

PV=4500          # Number of possible values
SS=100           # Sample size

# These intermediate results are exceedingly large numbers;
# Python automatically starts using bignums behind the scenes.
#
numerator = factorial (PV)          
denominator = (PV ** SS) * factorial (PV - SS)

# Now we need to get from bignums to floats without intermediate
# values too large to cast into a double.  Taking the logs and 
# subtracting them is equivalent to division.
#  
log_prob_no_pair = log10 (numerator) - log10 (denominator)

# We've just calculated the log of the probability that *NO*
# two matching pairs occur in the sample.  The probability
# of at least one collision is 1.0 - the probability that no 
# matching pairs exist.
#
print 1.0 - (10 ** log_prob_no_pair)

이것은 4500 개의 가능한 값의 모집단에서 샘플링 된 100 개의 숫자 내에서 발생하는 일치에 대해 p = 0.669 의 현명한 결과를 생성 합니다. (아마도 누군가가 이것을 확인하고 잘못된 경우 댓글을 게시 할 수 있습니다). 이것으로부터 우리는 OP에 의해 관찰 된 일치하는 숫자 사이의 실행 길이가 상당히 합리적으로 보인다는 것을 알 수 있습니다.

각주 : 셔플 링을 사용하여 고유 한 의사 난수 시퀀스 얻기

보장 된 고유 한 난수 집합을 얻는 방법은 S. Mark 의이 답변을 참조하십시오 . 포스터가 참조하는 기술은 숫자 배열 (사용자가 제공하여 고유하게 만들 수 있음)을 가져 와서 임의의 순서로 섞습니다. 셔플 된 배열에서 순서대로 숫자를 그리면 반복되지 않는 일련의 의사 난수를 얻을 수 있습니다.

각주 : 암호화 보안 PRNG

MT 알고리즘은 일련의 숫자를 관찰하여 생성기의 내부 상태를 추론하기가 상대적으로 쉽기 때문에 암호 학적으로 안전 하지 않습니다 . Blum Blum Shub 와 같은 다른 알고리즘 은 암호화 응용 프로그램에 사용되지만 시뮬레이션 또는 일반 난수 응용 프로그램에는 적합하지 않을 수 있습니다. 암호 학적으로 안전한 PRNG는 비용이 많이 들거나 (아마도 큰 계산이 필요함) 좋은 기하학적 속성이 없을 수 있습니다. 이러한 유형의 알고리즘의 경우 주요 요구 사항은 일련의 값을 관찰하여 생성기의 내부 상태를 추론하는 것이 계산적으로 실행 불가능해야한다는 것입니다.


한 가지 수정 사항 : LCG 기반 PRNG를 올바르게 사용 하면 전체주기 동안 고유 한 출력이 보장 되지 않습니다 . 예를 들어, 기존의 Turbo Pascal LCG는 (IIRC) 31 비트의 내부 상태를 가지고 있지만 단일 사이클 내에서 반복 할 수 있고 반복 할 수있는 15 비트 숫자 만 생성합니다.
Porculus 2010

46

파이썬을 비난하기 전에, 확률 및 통계 이론을 실제로 정리해야합니다. 에 대해 읽어 시작생일 역설

그건 그렇고, randomPython 의 모듈은 매우 좋은 것으로 간주되는 Mersenne twister PRNG를 사용 하며 엄청난 기간을 가지고 있으며 광범위하게 테스트되었습니다. 그러니 당신이 좋은 손에 있다는 것을 확신하십시오.


42

반복적 인 배열을 원하지 않으면 순차 배열을 생성하고 random.shuffle을 사용 하십시오.


3
내가 사랑하는 신 random.shuffle 합니다. : 내 프로젝트의 코어 그것의 하나
PizzAzzra


15

진정한 임의성은 가능한 값의 전체 집합이 고갈되기 전에 값의 반복을 확실히 포함합니다. 값이 반복되지 않는 기간을 예측할 수 있으므로 그렇지 않으면 무작위가 아닙니다.

주사위를 굴려 본 적이 있다면 분명히 3 개의 6 개를 꽤 자주 연속으로 얻었습니다.



4

리피터가 아닙니다. 반복기는 동일한 순서 를 반복 할 때 입니다. 하나의 숫자가 아닙니다.


4

4500범위에서 난수를 생성 하고 있습니다 500 <= x <= 5000. 그런 다음 각 번호가 이전에 생성되었는지 여부를 확인합니다. 생일 문제는 확률이 주어진 일치하도록 그 숫자의 두에 대해 무엇을 우리에게 알려줍니다 n범위 밖으로 시도를 d.

또한 수식을 반전하여 중복을 생성 할 가능성이보다 많을 때까지 생성해야하는 숫자의 수를 계산할 수 있습니다 50%. 이 경우 반복 >50%후 중복 번호를 찾을 수 79있습니다.


1

4501 값 (500-5000)의 임의 공간을 정의했으며 4500 번 반복합니다. 기본적으로 작성한 테스트에서 충돌이 발생하는 것이 보장됩니다.

다른 방식으로 생각하려면 :

  • 결과 배열이 비어있는 경우 P (dupe) = 0
  • 배열 P (중복)의 1 개 값 = 1/4500
  • 배열 P (중복)의 2 개 값 = 2/4500
  • 기타

따라서 45/4500에 도달 할 때까지 해당 인서트는 1 %의 중복 가능성을 가지며 그 확률은 후속 인서트마다 계속 증가합니다.

무작위 함수의 능력을 진정으로 테스트하는 테스트를 만들려면 가능한 무작위 값의 범위를 늘리십시오 (예 : 500-500000). 속일 수도 있고 그렇지 않을 수도 있습니다. 그러나 평균적으로 훨씬 더 많은 반복을 얻을 수 있습니다.


3
생일 문제 때문에 수학이 잘못되었습니다. 다른 답변을 참조하십시오. 45 개의 삽입 후 첫 번째 삽입을 반복 할 확률은 1 %이지만 반복했을 수있는 다른 44 개의 삽입도 있습니다.
jcdyer

0

이 문제를 가진 다른 사람에게는 uuid.uuid4 ()를 사용했으며 매력처럼 작동합니다.


3
질문은 "나는 반복되지 않는 일련의 숫자를 생성하고 싶습니다. 파이썬의 randint ()는 그렇게하지 않는 것 같습니다. 무엇을합니까?"라고 더 잘 표현되었을 것입니다. "Python의 난수 생성기는 나쁘다"보다는 :-) uuid4 ()가 진정으로 임의적이라고 가정하면 여전히 반복 될 수 있습니다. 숫자에서 원하는 실제 속성은 무엇입니까? 반복되지 않습니까? 무작위? (하나를 선택하십시오.) 자주 반복되지 않습니까? (효과적으로 모든 uuid4이다, 보인다, 더 큰 INT 범위를 사용합니다.) 정확하게 숫자를 사용하여 원하는 작업 을 위해 진짜 문제입니다.
agnoster

@agnoster 실제로 파이썬을 모욕하려는 의도는 없었지만 Random : 체계적인 패턴이없는 예측 가능성 부족, 반복 패턴 : 반복해서 반복되는 항목 그룹의 패턴입니다. 랜덤 생성기는 패턴이 있기 때문에 반복하면 랜덤이 아닙니다.
orokusaki

9
"무작위"에 대한 정의가 잘못되었습니다. 진지하게 돌아가서 생일 패러독스에 대한 내용을 다시 읽어보십시오. 진정한 난수 생성기는 직관으로 예상하는 것보다 훨씬 더 자주 반복됩니다. @ConcernedOfTunbridgeW가 지적했듯이, 처음 100 개의 숫자 내에서 500-5000 범위에서 반복 될 확률은 ~ 66 %이며, 관찰 한 것과 전혀 일치하지 않습니다. 무작위성은 되지 "반복없이"평균, 그것은 단지 수단 ... 음, 임의. 사실, 반복이 없다는 것을 보장한다면 제너레이터는 그것을 강제하기 위해 무작위 적 이어야합니다 .
agnoster

1
이 숫자가 무엇을 원하는지에 대한 질문은 여전히 ​​존재합니다. 반복되지 않는 숫자를 구체적으로 원한다면 그 이유는 무엇입니까? uuid4 ()는 매우 큰 범위를 가진 randint ()와 다르지 않습니다. 시퀀스를 추측하기 어렵게하려면 반복을 제거하는 것이 실제로 당신에게 상처를줍니다. 왜냐하면 일단 33 번과 같은 숫자를 보면 다음에 오는 것은 33 이 없다는 것을 알기 때문입니다. 그래서 비 반복을 적용하면 실제로 시퀀스를 예측할 수 있습니다. 알 겠어요?
agnoster

0

생일 역설이 있습니다. 이것을 고려하면 "764, 3875, 4290, 4378, 764"같은 것을 찾는 것은 그 시퀀스의 숫자가 반복되기 때문에 그다지 임의적이지 않다는 것을 알게됩니다. 이를 수행하는 진정한 방법은 시퀀스를 서로 비교하는 것입니다. 이를 위해 파이썬 스크립트를 작성했습니다.

from random import randint
y = 21533456
uniques = []
for i in range(y):  
    x1 = str(randint(500, 5000))
    x2 = str(randint(500, 5000))
    x3 = str(randint(500, 5000))
    x4 = str(randint(500, 5000))
    x = (x1 + ", " + x2 + ", " + x3 + ", " + x4)
if x in uniques:
    raise Exception('We duped the sequence %d at iteration number %d' % (x, i))
else:
    raise Exception('Couldn\'t find a repeating sequence in %d iterations' % (y))
uniques.append(x)

이 답변은 몇 년 전에 주어졌습니다 (위에서 선택한 답변 참조). 그것은 역설이 아니라 생일 문제이기 때문에 생일 역설이라고 부르지 않습니다.
orokusaki jul.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.