Ngẫu nhiên là hoàn toàn ngẫu nhiên?


79

Tôi đã làm điều này để kiểm tra tính ngẫu nhiên của 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

Tôi đã thử thêm khoảng 10 lần nữa và kết quả tốt nhất mà tôi nhận được là 121 lần lặp trước một bộ lặp. Đây có phải là loại kết quả tốt nhất mà bạn có thể nhận được từ thư viện chuẩn không?


56
"Lập trình viên thực dụng", quy tắc 26. "chọn" Không bị hỏng. Rất hiếm khi tìm thấy lỗi trong hệ điều hành hoặc trình biên dịch hoặc thậm chí là sản phẩm hoặc thư viện của bên thứ ba. Rất có thể lỗi nằm trong ứng dụng. Hoặc trong trường hợp này, ứng dụng của lý thuyết xác suất.

10
Chỉ nitpicking: uniques = set () và uniques.add (x) sẽ thích hợp hơn (hiệu quả).
Eric O Lebigot

22
Một trong những đặc tính chính của nghịch lý ngày sinh là nó phản trực giác. Trừ khi bạn biết về nó hoặc có một số nền tảng về lý thuyết xác suất thì bạn sẽ không nhất thiết phải có bất kỳ lý do gì để thực hiện tìm kiếm từ khóa cho nó. Một trong những trang web Hỏi và Đáp của USP là bạn có thể đặt một câu hỏi theo những thuật ngữ không bao giờ thực sự khớp với câu trả lời cho câu hỏi nếu bạn thực hiện một tìm kiếm từ khóa thuần túy mà không biết tìm kiếm gì.
ConcernedOfTunbridgeWells

7
@okoku: (liên quan đến câu trả lời của bạn cho ConcernedOfTunbridge): những gì bạn đang nói là một vấn đề hoàn toàn khác. Một là xác suất nhận được cùng một thẻ hai lần liên tiếp; còn lại là xác suất lấy được BẤT KỲ trong số N-1 thẻ trước đó sau khi N được chọn. Các trung bình số lượng thẻ từ một hoàn hảo RNG cho vấn đề thứ hai nên được khoảng 67; xem xét bạn đã nhận được bất kỳ nơi nào từ 8 đến 121, điều đó nghe có vẻ đúng.
BlueRaja - Danny Pflughoeft

5
bạn đang nhầm lẫn Ngẫu nhiên với Phân phối Đồng đều. Việc một trình tạo ngẫu nhiên trả về cùng một giá trị lặp đi lặp lại là hoàn toàn hợp lệ. Nếu bạn muốn một bộ tạo số Ngẫu nhiên Phân tán Đồng đều là một vấn đề hoàn toàn khác, đó là vấn đề xáo trộn không phải là vấn đề của bộ tạo.

Câu trả lời:


287

Nghịch lý sinh nhật hoặc tại sao PRNG tạo ra các bản sao thường xuyên hơn bạn có thể nghĩ.


Có một số vấn đề đang diễn ra trong vấn đề của OP. Một là nghịch lý ngày sinh như đã đề cập ở trên và thứ hai là bản chất của những gì bạn đang tạo ra, vốn dĩ không đảm bảo rằng một số nhất định sẽ không lặp lại.

Nghịch lý sinh nhật áp dụng khi giá trị đã cho có thể xuất hiện nhiều lần trong khoảng thời gian của trình tạo - và do đó có thể xảy ra trùng lặp trong một mẫu giá trị. Ảnh hưởng của Nghịch lý sinh nhật là khả năng thực sự nhận được các bản sao như vậy là khá đáng kể và khoảng thời gian trung bình giữa chúng nhỏ hơn người ta có thể nghĩ. Sự bất hòa này giữa xác suất nhận thức và xác suất thực tế khiến Nghịch lý Sinh nhật trở thành một ví dụ điển hình về sự thiên lệch nhận thức , trong đó một ước tính trực quan ngây thơ có khả năng sai cực kỳ lớn.

Sơ lược nhanh về Máy tạo số ngẫu nhiên giả (PRNG)

Phần đầu tiên của vấn đề là bạn đang lấy giá trị hiển thị của bộ tạo số ngẫu nhiên và chuyển đổi nó thành một số nhỏ hơn nhiều, vì vậy không gian của các giá trị có thể bị giảm đi. Mặc dù một số trình tạo số giả ngẫu nhiên không lặp lại các giá trị trong khoảng thời gian của chúng, phép biến đổi này sẽ thay đổi miền thành một miền nhỏ hơn nhiều. Miền nhỏ hơn sẽ làm mất hiệu lực của điều kiện 'không lặp lại', vì vậy bạn có thể mong đợi khả năng lặp lại đáng kể.

Một số thuật toán, chẳng hạn như PRNG congruential tuyến tính ( A'=AX|M) làm độc đáo đảm bảo cho toàn bộ thời gian. Trong LCG, giá trị được tạo chứa toàn bộ trạng thái của bộ tích lũy và không có trạng thái bổ sung nào được giữ lại. Bộ tạo là xác định và không thể lặp lại một số trong khoảng thời gian - bất kỳ giá trị tích lũy đã cho nào chỉ có thể ngụ ý một giá trị liên tiếp có thể có. Do đó, mỗi giá trị chỉ có thể xuất hiện một lần trong khoảng thời gian của bộ tạo. Tuy nhiên, khoảng thời gian của một PRNG như vậy là tương đối nhỏ - khoảng 2 ^ 30 đối với các triển khai điển hình của thuật toán LCG - và không thể lớn hơn số lượng các giá trị riêng biệt.

Không phải tất cả các thuật toán PRNG đều có chung đặc điểm này; một số có thể lặp lại một giá trị nhất định trong khoảng thời gian. Trong bài toán OP, thuật toán Mersenne Twister (được sử dụng trong mô-đun ngẫu nhiên của Python ) có khoảng thời gian rất dài - lớn hơn nhiều so với 2 ^ 32. Không giống như PRNG tuyến tính công suất, kết quả không hoàn toàn là một hàm của giá trị đầu ra trước đó vì bộ tích lũy chứa trạng thái bổ sung. Với đầu ra số nguyên 32 bit và khoảng thời gian ~ 2 ^ 19937, nó không thể cung cấp một bảo đảm như vậy.

Mersenne Twister là một thuật toán phổ biến cho PRNG vì nó có các đặc tính thống kê và hình học tốt và một khoảng thời gian rất dài - các đặc điểm mong muốn cho một PRNG được sử dụng trên các mô hình mô phỏng.

  • Các thuộc tính thống kê tốt có nghĩa là các số được tạo bởi thuật toán được phân phối đồng đều và không có số nào có xác suất xuất hiện cao hơn đáng kể so với các số khác. Các thuộc tính thống kê kém có thể tạo ra sai lệch không mong muốn trong kết quả.

  • Các properies hình học tốt có nghĩa là tập hợp N số không nằm trên một siêu phẳng trong không gian N chiều. Các đặc tính hình học kém có thể tạo ra các tương quan giả trong mô hình mô phỏng và làm sai lệch kết quả.

  • Một khoảng thời gian dài có nghĩa là bạn có thể tạo ra rất nhiều số trước khi chuỗi kết thúc ở đầu. Nếu một mô hình cần một số lượng lớn các lần lặp lại hoặc phải chạy từ một số hạt giống thì 2 ^ 30 hoặc các số rời rạc có sẵn từ một triển khai LCG điển hình có thể không đủ. Thuật toán MT19337 có khoảng thời gian rất dài - 2 ^ 19337-1, hoặc khoảng 10 ^ 5821. Để so sánh, tổng số nguyên tử trong vũ trụ được ước tính vào khoảng 10 ^ 80.

Số nguyên 32 bit được tạo bởi MT19337 PRNG không thể đại diện cho đủ các giá trị riêng biệt để tránh lặp lại trong một khoảng thời gian lớn như vậy. Trong trường hợp này, các giá trị trùng lặp có khả năng xảy ra và không thể tránh khỏi với một mẫu đủ lớn.

Tóm lại, Nghịch lý sinh nhật

Bài toán này ban đầu được định nghĩa là xác suất để hai người bất kỳ trong phòng có cùng ngày sinh. Điểm mấu chốt là bất kỳ hai người nào trong phòng đều có thể sinh nhật chung. Mọi người có xu hướng hiểu sai vấn đề một cách ngây thơ như xác suất một người nào đó trong phòng chia sẻ sinh nhật với một cá nhân cụ thể, đó là nguồn gốc của sự thiên vị nhận thức thường khiến mọi người đánh giá thấp xác suất. Đây là giả định không chính xác - không có yêu cầu đối với trận đấu phải dành cho một cá nhân cụ thể và bất kỳ hai cá nhân nào cũng có thể trùng khớp.

Biểu đồ này cho thấy xác suất sinh nhật chung khi số người trong phòng tăng lên.  Đối với 23 người, xác suất sinh nhật của hai người chỉ là hơn 50%.

Xác suất trận đấu xảy ra giữa hai cá nhân bất kỳ cao hơn nhiều so với xác suất trận đấu với một cá nhân cụ thể vì trận đấu không nhất thiết phải đến một ngày cụ thể. Thay vào đó, bạn chỉ phải tìm hai cá nhân có cùng ngày sinh. Từ biểu đồ này (có thể được tìm thấy trên trang Wikipedia về chủ đề này), chúng ta có thể thấy rằng chúng ta chỉ cần 23 người trong phòng để có 50% cơ hội tìm thấy hai người trùng khớp theo cách này.

Từ mục nhập Wikipedia về chủ đề này, chúng ta có thể có được một bản tóm tắt tốt đẹp. Trong bài toán OP, chúng ta có 4.500 'sinh nhật' có thể xảy ra, thay vì 365. Đối với một số giá trị ngẫu nhiên nhất định được tạo (tương đương với 'người'), chúng ta muốn biết xác suất của bất kỳ hai giá trị giống nhau nào xuất hiện trong dãy.

Tính toán ảnh hưởng có thể xảy ra của Nghịch lý sinh nhật đối với vấn đề của OP

Đối với một chuỗi 100 số, chúng ta có (100 * 99) / 2 = 4950 các cặp (xem phần Tìm hiểu vấn đề ) có khả năng khớp (tức là cặp đầu tiên có thể khớp với số thứ hai, thứ ba, v.v., cặp thứ hai có thể khớp với số thứ ba, thứ tư, v.v.), vì vậy số lượng kết hợp có thể phù hợp hơn là chỉ 100.

Từ Tính toán xác suất, chúng tôi nhận được một biểu thức của 1 - (4500! / (4500 ** 100 * (4500 - 100)!) . Đoạn mã Python sau đây thực hiện một đánh giá ngây thơ về xác suất xảy ra một cặp phù hợp.

# === 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)

Điều này tạo ra kết quả hợp lý là p = 0,669 cho một trận đấu xảy ra trong 100 số được lấy mẫu từ tập hợp 4500 giá trị có thể. (Có thể ai đó có thể xác minh điều này và đăng nhận xét nếu nó sai). Từ đó, chúng ta có thể thấy rằng độ dài của các lần chạy giữa các số phù hợp mà OP quan sát được có vẻ khá hợp lý.

Chú thích cuối trang: sử dụng xáo trộn để nhận một chuỗi các số giả ngẫu nhiên duy nhất

Hãy xem câu trả lời này dưới đây của S. Mark để biết phương tiện nhận được một bộ số ngẫu nhiên duy nhất được đảm bảo. Kỹ thuật mà người đăng đề cập đến lấy một mảng số (do bạn cung cấp, để bạn có thể biến chúng thành duy nhất) và xáo trộn chúng thành một thứ tự ngẫu nhiên. Việc vẽ các số theo thứ tự từ mảng xáo trộn sẽ cung cấp cho bạn một chuỗi các số giả ngẫu nhiên được đảm bảo không lặp lại.

Chú thích: PRNG bảo mật về mặt mật mã

Thuật toán MT không an toàn về mặt mật mã vì nó tương đối dễ dàng để suy ra trạng thái bên trong của bộ tạo bằng cách quan sát một chuỗi số. Các thuật toán khác như Blum Blum Shub được sử dụng cho các ứng dụng mật mã nhưng có thể không phù hợp với các ứng dụng mô phỏng hoặc số ngẫu nhiên chung. Các PRNG an toàn về mặt mật mã có thể đắt tiền (có thể yêu cầu tính toán bignum) hoặc có thể không có các đặc tính hình học tốt. Trong trường hợp của loại thuật toán này, yêu cầu chính là nó phải không khả thi về mặt tính toán để suy ra trạng thái bên trong của bộ tạo bằng cách quan sát một chuỗi giá trị.


Một điều chỉnh: PRNG dựa trên LCG, được sử dụng đúng cách, không đảm bảo đầu ra duy nhất cho chu trình hoàn chỉnh. Ví dụ, Turbo Pascal LCG truyền thống có (IIRC) 31 bit trạng thái bên trong, nhưng nó chỉ tạo ra các số 15 bit có thể lặp lại trong một chu kỳ đơn.
Porculus

46

Trước khi đổ lỗi cho Python, bạn thực sự nên xem lại một số lý thuyết xác suất & thống kê. Bắt đầu bằng cách đọc về nghịch lý ngày sinh

Nhân tiện, randommô-đun trong Python sử dụng Mersenne twister PRNG, được coi là rất tốt, có một thời kỳ rất lớn và đã được thử nghiệm rộng rãi. Vì vậy, hãy yên tâm rằng bạn đang ở trong tay tốt.


42

Nếu bạn không muốn thiết lập lại, hãy tạo mảng tuần tự và sử dụng random.shuffle


3
Chúa tôi yêu random.shuffle. Đó là một trong những lõi của dự án của tôi :)
PizzAzzra


15

Tính ngẫu nhiên thực sự chắc chắn bao gồm việc lặp lại các giá trị trước khi toàn bộ tập hợp các giá trị có thể bị cạn kiệt. Ngược lại, nó sẽ không phải là ngẫu nhiên, vì bạn có thể dự đoán trong bao lâu một giá trị sẽ không được lặp lại.

Nếu bạn đã từng tung xúc xắc, bạn chắc chắn thường có 3 con sáu liên tiếp ...



4

Đó không phải là một bộ lặp. Bộ lặp là khi bạn lặp lại cùng một trình tự . Không chỉ một con số.


4

Bạn đang tạo 4500các số ngẫu nhiên từ một phạm vi 500 <= x <= 5000. Sau đó, bạn kiểm tra xem từng số đã được tạo trước đó chưa. Bài toán ngày sinh cho chúng ta biết xác suất là bao nhiêu để hai trong số đó khớp với những nlần thử nằm ngoài một phạm vi d.

Bạn cũng có thể đảo ngược công thức để tính xem bạn phải tạo ra bao nhiêu số cho đến khi cơ hội tạo ra một bản sao nhiều hơn 50%. Trong trường hợp này, bạn có >50%cơ hội tìm thấy một số trùng lặp sau các 79lần lặp.


1

Bạn đã xác định một không gian ngẫu nhiên gồm 4501 giá trị (500-5000) và bạn đang lặp lại 4500 lần. Về cơ bản, bạn được đảm bảo sẽ gặp va chạm trong bài kiểm tra mà bạn đã viết.

Để nghĩ về nó theo cách khác:

  • Khi mảng kết quả trống P (dupe) = 0
  • 1 giá trị trong Mảng P (dupe) = 1/4500
  • 2 giá trị trong Mảng P (dupe) = 2/4500
  • Vân vân.

Vì vậy, khi bạn đạt đến 45/4500, phần chèn đó có 1% khả năng là bản sao và xác suất đó tiếp tục tăng lên với mỗi lần chèn tiếp theo.

Để tạo một bài kiểm tra thực sự kiểm tra khả năng của hàm ngẫu nhiên, hãy tăng vũ trụ các giá trị ngẫu nhiên có thể có (ví dụ: 500-500000) Bạn có thể nhận được hoặc không thể nhận được một bản dupe. Nhưng trung bình bạn sẽ nhận được nhiều lần lặp hơn.


3
Phép toán của bạn không chính xác vì vấn đề ngày sinh. Xem các câu trả lời khác. Sau 45 lần chèn, bạn có 1% cơ hội lặp lại lần chèn đầu tiên, nhưng bạn cũng có 44 lần chèn khác biệt mà bạn có thể đã lặp lại.
jcdyer

0

Đối với bất kỳ ai khác gặp vấn đề này, tôi đã sử dụng uuid.uuid4 () và nó hoạt động như một sự quyến rũ.


3
Câu hỏi sau đó có thể đã được diễn giải tốt hơn là "Tôi muốn tạo một chuỗi các số không lặp lại, hàm randint () của Python dường như không làm được điều đó - thì sao?" chứ không phải là "Trình tạo số ngẫu nhiên của Python không tốt" :-) Giả sử uuid4 () thực sự là ngẫu nhiên, nó vẫn có thể lặp lại - chỉ thực sự khó xảy ra. Các thuộc tính thực tế bạn muốn từ những con số là gì? Không lặp lại? Ngẫu nhiên? (Chọn một.) Không-lặp lại-thường xuyên? (Sử dụng một phạm vi int lớn hơn, dường như tất cả uuid4 đều như vậy.) Chính xác thì bạn muốn sử dụng các con số để làm gì là câu hỏi thực sự.
agnoster

@agnoster Tôi thực sự không có ý định xúc phạm Python, nhưng Ngẫu nhiên: Thiếu khả năng dự đoán, không có bất kỳ mẫu hệ thống nào và Mẫu lặp lại: Mẫu gồm một nhóm các mục lặp đi lặp lại. Hãy xem, trình tạo ngẫu nhiên không phải là ngẫu nhiên nếu nó lặp lại vì sau đó nó có một mẫu.
orokusaki

9
Định nghĩa của bạn về "ngẫu nhiên" là sai. Nghiêm túc mà nói, hãy quay lại và đọc lại những điều về nghịch lý ngày sinh. Một trình tạo số thực sự ngẫu nhiên sẽ vẫn lặp lại thường xuyên hơn nhiều so với trực giác bạn mong đợi. Như @ConcernedOfTunbridgeW đã chỉ ra, xác suất lặp lại trong phạm vi 500-5000 trong 100 số đầu tiên là ~ 66%, hoàn toàn không mâu thuẫn với những gì bạn đã quan sát, tôi tin. Tính ngẫu nhiên không có nghĩa là "không có lặp lại", nó chỉ có nghĩa là ... tốt, ngẫu nhiên. Trên thực tế, nếu bạn đảm bảo thiếu số lần lặp lại, bộ tạo phải ít ngẫu nhiên hơn để thực thi điều đó.
agnoster

1
Câu hỏi về những gì bạn muốn những con số này vẫn còn. Nếu bạn đặc biệt muốn các số không lặp lại, tại sao? uuid4 () là (nếu nó thực sự ngẫu nhiên) không khác gì randint () với phạm vi rất lớn. Nếu bạn muốn trình tự khó đoán, việc loại bỏ các lần lặp lại thực sự khiến bạn đau đớn, bởi vì một khi tôi nhìn thấy con số, chẳng hạn, 33, tôi biết rằng bất cứ điều gì xảy ra tiếp theo không có 33 trong đó. Vì vậy, việc thực thi không lặp lại thực sự làm cho trình tự của bạn dễ dự đoán hơn - bạn thấy không?
agnoster

0

Có một nghịch lý ngày sinh. Tính đến điều này, bạn nhận ra rằng những gì bạn đang nói là việc tìm kiếm "764, 3875, 4290, 4378, 764" hoặc một cái gì đó tương tự không phải là rất ngẫu nhiên vì một số trong dãy đó lặp lại. Cách thực sự để làm điều đó là so sánh các chuỗi với nhau. Tôi đã viết một tập lệnh python để thực hiện việc này.

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)

Câu trả lời này đã được đưa ra cách đây nhiều năm (xem câu trả lời đã chọn ở trên). Nó không được gọi là nghịch lý ngày sinh, vì nó không phải là một nghịch lý, mà chỉ là vấn đề ngày sinh.
orokusaki,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.