Tại sao C ++ rand () dường như chỉ tạo ra các số có cùng độ lớn?


146

Trong một ứng dụng nhỏ được viết bằng C / C ++, tôi gặp vấn đề với randchức năng và có thể là hạt giống:

Tôi muốn tạo ra một chuỗi các số ngẫu nhiên có thứ tự khác nhau, tức là với các giá trị logarit khác nhau (cơ sở 2). Nhưng dường như tất cả các số được tạo ra đều có cùng thứ tự, dao động trong khoảng từ 2 ^ 25 đến 2 ^ 30.

Có phải bởi vì rand()được gieo mầm với thời gian Unix mà bây giờ là một con số tương đối lớn? Tôi đang quên cái gì? Tôi rand()chỉ gieo hạt một lần vào đầu main().


7
FWIW vậy, nó là C hay C ++? Nếu bằng C / C ++, bạn có nghĩa là bạn thực sự có thể sử dụng C ++ và việc đề cập đến C chỉ là ngẫu nhiên, có lẽ điều này en.cppreference.com/w/cpp/numeric/random/binomial_distribution có thể giúp ích.
R. Martinho Fernandes

9
Thật không may, bạn đã đặt cược vào con ngựa sai. Hạt giống không phải là vấn đề của bạn. Vấn đề của bạn là phân phối dự kiến ​​sai. Vì lập trình viên không thiên vị sẽ mong đợi rand()trả về các số được phân phối đồng đều (tài liệu có thứ hạng cao của Google nói rõ ràng như vậy) Tôi không nghĩ câu hỏi này hữu ích cho các độc giả tương lai. Đó là lý do tại sao bỏ phiếu nhưng đừng để nó ngăn cản bạn sử dụng SO.
Hoàng đế Orionii

12
@ doug65536 "... trong đó không có số nào được lặp lại" - đó không phải là ngẫu nhiên! Tôi có thể tài trợ cho quỹ hưu trí của mình tại bảng craps nếu xúc xắc rand () của tôi không bao giờ trả lại cùng một số cho đến khi mọi số có thể được trả về.
Chris Gregg

6
@GalacticCowboy Đừng nhầm lẫn định kỳ với việc lặp lại các số riêng lẻ. Từ bài viết Wikipedia bạn đã trích dẫn: "một kết quả lặp đi lặp lại không ngụ ý rằng đã kết thúc giai đoạn này, vì trạng thái bên trong của nó có thể lớn hơn đầu ra của nó." Sẽ rất, rất tệ nếu PRNG tạo ra một giá trị và sau đó được đảm bảo không tạo lại giá trị đó cho đến khi tất cả các giá trị được trả về.
Chris Gregg

12
Doug65536, không ai được chọn chiến đấu. Họ chỉ nói chính xác rằng bạn sai. Một PRNG hoàn toàn có thể vui vẻ đưa ra những điều sau nếu tôi muốn RAND trong khoảng từ 1 đến 10: 2 4 7 2 8 1 5 9 7 3 Điều đó hoàn toàn hợp lệ, mặc dù có nhiều 2 và 7 giây. Tôi nghĩ rằng bạn đang khiến PRNG nhầm lẫn với thiết bị xáo trộn trên iPhone của bạn.
Thư giãn tại Síp

Câu trả lời:


479

Chỉ có 3% số trong khoảng từ 1 đến 2 30 mà KHÔNG nằm trong khoảng từ 2 25 đến 2 30 . Vì vậy, điều này nghe có vẻ khá bình thường :)

Bởi vì 2 25 /2 30 = 2 -5 = 1/32 = 0,03125 = 3,125%


36
Vâng, điểm tốt! Có số lượng nhiều hơn 31 lần trong khoảng từ 2 ^ 25 đến 2 ^ 30 so với giữa 1 và 2 ^ 25 :) cảm ơn vì đã trả lời nhanh. Tôi cần suy nghĩ lại về chương trình sau đó. Câu hỏi đã được trả lời.
Tallaron Mathias

1
@TallaronMathias Hãy xem xét cắt bớt số thông qua >>bẻ khóa - điều này sẽ cung cấp cho bạn các số nhỏ hơn. (Hoặc dùng mô-đun với %.)
Sean Allred

13
Tôi hy vọng điều này là hiển nhiên đối với hầu hết các lập trình viên: Bất kỳ số nguyên không dấu nào dưới 2 ^ 25 phải có 7 bit đầu tiên bằng 0- và nếu mỗi bit là ngẫu nhiên ...
BlueRaja - Danny Pflughoeft

118
@ BlueRaja-DannyPflughoeft - nếu xác suất rõ ràng, sòng bạc sẽ ngừng hoạt động.
Brett Hale

26
@BrettHale - Mặc dù vậy, tôi không nghĩ lập trình viên là nhân khẩu học mục tiêu của sòng bạc.
EkoostikMartin

272

Màu xanh nhạt hơn là vùng giữa 0 và 2 25 ; màu xanh đậm hơn là vùng giữa 2 25 và 2 30 . Bọ ve là sức mạnh của 2.

phân phối


42

Bạn cần phải chính xác hơn: bạn muốn các giá trị logarit cơ sở 2 khác nhau nhưng bạn muốn phân phối gì cho việc này? Các hàm rand () tiêu chuẩn tạo phân phối đồng đều, bạn sẽ cần chuyển đổi đầu ra này bằng cách sử dụng hàm lượng tử liên quan đến phân phối mà bạn muốn.

Nếu bạn cho chúng tôi biết phân phối thì chúng tôi có thể cho bạn biết quantilechức năng bạn cần.


13
+1, phân phối là thuật ngữ quan trọng. Thật không có ý nghĩa gì khi nói về các số ngẫu nhiên khi không có thông tin gì về phân phối. Đồng phục chỉ là một trường hợp đặc biệt, mặc dù là một trường hợp quan trọng. Có thể là một nơi tốt để chỉ ra các bản phân phối khác nhau từ thư viện chuẩn C ++ 11.
leftaroundabout

18

Nếu bạn muốn các đơn đặt hàng lớn khác nhau, tại sao không thử pow(2, rand())? Hoặc có lẽ chọn thứ tự trực tiếp là rand (), như Harold đề xuất?


3
ý tưởng tốt, nhưng bạn nên sửa câu trả lời của mình bằng pow thay vì ^ (đó là toán tử xor logic, không phải là sức mạnh, bằng ngôn ngữ C).
kriss

6
rand()có thể tăng lên RAND_MAX, bạn thực sự cần phải chia tỷ lệ số ngẫu nhiên của mình để kết quả không bị tràn ra ...
Floris

@Floris: nhưng nếu bạn mở rộng phạm vi có thể đếm được trên một phạm vi rất lớn, bạn sẽ có RẤT NHIỀU lỗ hổng, đó có lẽ không phải là điều OP đang mong đợi.
André Caron

13

@ C4stor đã làm cho một điểm tuyệt vời. Tuy nhiên, đối với trường hợp tổng quát hơn và dễ hiểu hơn đối với con người (cơ sở 10): đối với phạm vi từ 1 đến 10 ^ n, ~ 90% số là từ 10 ^ (n-1) đến 10 ^ n, do đó, ~ 99% số lượng đi từ 10 ^ (n-2) đến 10 ^ n. Tiếp tục thêm nhiều số thập phân như bạn muốn.

Toán học vui, nếu bạn tiếp tục làm điều này cho n, bạn có thể thấy rằng từ 1 đến 10 ^ n, 99.9999 ...% = 100% số là từ 10 ^ 0 đến 10 ^ n với phương pháp này.

Bây giờ về mã, nếu bạn muốn một số ngẫu nhiên có thứ tự độ lớn ngẫu nhiên, từ 0 đến 10 ^ n, bạn có thể làm:

  1. Tạo một số ngẫu nhiên nhỏ từ 0 đến n

  2. Nếu bạn biết phạm vi mà n có, hãy tạo một số lượng lớn thứ tự ngẫu nhiên 10 ^ k trong đó k> max {n}.

  3. Cắt số ngẫu nhiên dài hơn để có được n chữ số của số ngẫu nhiên lớn này.


46
Bạn hoàn toàn chính xác, nhưng để có câu trả lời THỰC SỰ dễ hiểu, OP nên tự hỏi tại sao 90% số ngẫu nhiên trong khoảng từ 1 đến 100 là hai chữ số.
Hỏi về Monica

13

Câu trả lời cơ bản (và chính xác) đã được đưa ra và chấp nhận ở trên: có 10 số từ 0 đến 9, 90 số trong khoảng từ 10 đến 99, 900 trong khoảng từ 100 đến 999, v.v.

Đối với một cách tính toán hiệu quả để có được một phân phối với phân phối logarit xấp xỉ , bạn muốn dịch chuyển sang phải số ngẫu nhiên của mình bằng một số ngẫu nhiên:

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

Nó không hoàn hảo, nhưng nó nhanh hơn nhiều so với máy tính pow(2, rand()*scalefactor). Nó sẽ "vón cục" theo nghĩa là phân phối sẽ thống nhất cho các số trong một yếu tố 2 (thống nhất cho 128 đến 255, một nửa mật độ cho 256 đến 1023, v.v.).

Dưới đây là biểu đồ tần số của các số từ 0 đến 31 (trong các mẫu 1M):

nhập mô tả hình ảnh ở đây


nitpick: điều này khuyến khích số lượng rất nhỏ hơn người ta có thể mong đợi. Xác suất nhận được số 0 cao hơn đáng kể so với 10.
Vịt Mooing

Chà - toàn bộ vấn đề này là để khuyến khích những con số nhỏ, vì vậy tôi rất vui vì nó hoạt động! Tôi đã chạy một mô phỏng Monte Carlo và điều này mang lại cho tôi yếu tố giảm 2 lần khi số lượng nhân đôi - không giống như phân phối nhật ký. Cập nhật câu trả lời với một hình ảnh.
Floris

không, ý tôi là, với rand()>>(rand()&31);người ta sẽ trực giác mong đợi 1/32 số có 32 bit và 1/32 số có 31 bit và 1/32 số có 30 bit, v.v. Nhưng đó là không phải là kết quả mà bạn nhận được, chỉ khoảng 1/64 số sẽ có kết quả là 32 bit, trong khi gần một nửa là 0. Vì toán tinh thần của tôi không đồng ý với các phép đo của bạn, tôi sẽ phải tự đo. cái này ra
Vịt Mooing

2
Tôi không có ý nói mã của bạn là sai. Có lẽ đó là những gì tôi sẽ làm. Nó chỉ đáng được cảnh báo rằng kết quả không được phân phối hoàn toàn như người ta mong đợi.
Vịt Mooing

1
Tôi nghĩ vấn đề xuất phát từ việc nghĩ 0 là số 1 bit ... đó là loại câu hỏi hóc búa mà bạn gặp phải khi trộn lẫn các số nguyên và logarit. Đó là một bài tập tốt mặc dù và bạn đã cho tôi một cái gì đó để suy nghĩ. "Kiểm tra giới hạn của thuật toán của bạn" - nó không bao giờ cũ.
Floris

5

Có số lượng chính xác bằng nhau giữa 0 và 2 ^ 29 và 2 ^ 29 và 2 ^ 30.

Một cách khác để xem xét vấn đề: xem xét biểu diễn nhị phân của số ngẫu nhiên bạn tạo ra, xác suất bit cao nhất là 1 bằng 1/2, và do đó, bạn nhận được lệnh 29 trong một nửa trường hợp. Những gì bạn muốn là nhìn thấy một số sẽ dưới 2 ^ 25, nhưng điều đó có nghĩa là 5 bit cao nhất đều bằng 0, xảy ra với xác suất thấp là 1/32. Rất có thể là ngay cả khi bạn chạy nó trong một thời gian dài, bạn sẽ không bao giờ thấy thứ tự dưới 15 nào cả (xác suất là một cái gì đó giống như lăn 6 6 lần liên tiếp).

Bây giờ, một phần câu hỏi của bạn về hạt giống. Không, hạt giống không thể xác định phạm vi các số được tạo ra, nó chỉ xác định phần tử đầu tiên, ban đầu. Hãy nghĩ về rand () như một chuỗi tất cả các số có thể có trong phạm vi (hoán vị được xác định trước). Hạt giống xác định nơi bạn bắt đầu vẽ số từ chuỗi. Đây là lý do tại sao nếu bạn muốn (giả) ngẫu nhiên, bạn sử dụng thời gian hiện tại để khởi tạo trình tự: bạn không quan tâm rằng vị trí bạn bắt đầu không được phân phối đồng đều, tất cả vấn đề là bạn không bao giờ bắt đầu từ cùng một vị trí.


2

sử dụng pow(2,rand()) nó sẽ cho câu trả lời theo thứ tự độ lớn mong muốn !!


2

Nếu bạn muốn sử dụng các số ngẫu nhiên từ một dịch vụ trực tuyến, bạn có thể sử dụng wget cho điều đó, bạn có thể muốn thấy bạn cũng có thể sử dụng các dịch vụ như Random.org để tạo số ngẫu nhiên, bạn có thể bắt chúng bằng cách sử dụng wget và sau đó đọc các số từ tập tin tải về

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html


Chào mừng đến với SO. xin vui lòng không đăng liên kết như là câu trả lời. Bạn có thể cung cấp một bản phác thảo chi tiết của một câu trả lời để lại các chi tiết được đọc qua các liên kết.
Shai
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.