Giải pháp của Mark (Giải pháp được chấp nhận) gần như hoàn hảo.
int x;
do {
x = rand();
} while (x >= (RAND_MAX - RAND_MAX % n));
x %= n;
chỉnh sửa ngày 25 tháng 3 năm 16 lúc 23:16
Đánh dấu Amery 39k21170211
Tuy nhiên, nó có một cảnh báo loại bỏ 1 tập kết quả hợp lệ trong bất kỳ kịch bản nào trong đó RAND_MAX
( RM
) nhỏ hơn 1 so với bội số của N
(Trong đóN
= Số lượng kết quả hợp lệ có thể có).
tức là, khi 'số lượng giá trị bị loại bỏ' ( D
) bằng N
, thì chúng thực sự là một tập hợp lệ ( V)
không phải là tập hợp không hợp lệ ( I
).
Điều gì gây ra điều này là tại một số điểm Mark đánh mất sự khác biệt giữa N
và Rand_Max
.
N
là một tập hợp những thành viên hợp lệ chỉ bao gồm các số nguyên dương, vì nó chứa một số phản hồi sẽ hợp lệ. (ví dụ: Đặt N
= {1, 2, 3, ... n }
)
Rand_max
Tuy nhiên, một tập hợp (như được xác định cho mục đích của chúng tôi) bao gồm bất kỳ số nguyên không âm nào.
Ở dạng chung nhất, những gì được định nghĩa ở đây là Rand Max
là Tập hợp tất cả các kết quả hợp lệ, về mặt lý thuyết có thể bao gồm số âm hoặc giá trị không phải là số.
Do đó, Rand_Max
được định nghĩa tốt hơn là tập hợp các "Phản hồi có thể".
Tuy nhiên, N
hoạt động dựa trên số lượng giá trị trong tập hợp các phản hồi hợp lệ, do đó, ngay cả khi được xác định trong trường hợp cụ thể của chúng tôi, Rand_Max
sẽ là một giá trị nhỏ hơn tổng số mà nó chứa.
Sử dụng Giải pháp của Mark, các giá trị bị loại bỏ khi: X => RM - RM% N
EG:
Ran Max Value (RM) = 255
Valid Outcome (N) = 4
When X => 252, Discarded values for X are: 252, 253, 254, 255
So, if Random Value Selected (X) = {252, 253, 254, 255}
Number of discarded Values (I) = RM % N + 1 == N
IE:
I = RM % N + 1
I = 255 % 4 + 1
I = 3 + 1
I = 4
X => ( RM - RM % N )
255 => (255 - 255 % 4)
255 => (255 - 3)
255 => (252)
Discard Returns $True
Như bạn có thể thấy trong ví dụ trên, khi giá trị của X (số ngẫu nhiên chúng ta nhận được từ hàm ban đầu) là 252, 253, 254 hoặc 255, chúng ta sẽ loại bỏ nó mặc dù bốn giá trị này bao gồm một tập hợp các giá trị được trả về hợp lệ .
IE: Khi số lượng giá trị bị loại bỏ (I) = N (Số lượng kết quả hợp lệ) thì một bộ giá trị trả về hợp lệ sẽ bị loại bỏ bởi chức năng ban đầu.
Nếu chúng tôi mô tả sự khác biệt giữa các giá trị N và RM là D, nghĩa là:
D = (RM - N)
Sau đó, khi giá trị của D trở nên nhỏ hơn, Tỷ lệ cuộn lại không cần thiết do phương pháp này tăng lên ở mỗi phép nhân tự nhiên. (Khi RAND_MAX KHÔNG bằng Số nguyên tố, đây là mối quan tâm hợp lệ)
VÍ DỤ:
RM=255 , N=2 Then: D = 253, Lost percentage = 0.78125%
RM=255 , N=4 Then: D = 251, Lost percentage = 1.5625%
RM=255 , N=8 Then: D = 247, Lost percentage = 3.125%
RM=255 , N=16 Then: D = 239, Lost percentage = 6.25%
RM=255 , N=32 Then: D = 223, Lost percentage = 12.5%
RM=255 , N=64 Then: D = 191, Lost percentage = 25%
RM=255 , N= 128 Then D = 127, Lost percentage = 50%
Vì tỷ lệ phần trăm Reroll cần thiết tăng N càng gần với RM, nên điều này có thể là mối quan tâm hợp lệ ở nhiều giá trị khác nhau tùy thuộc vào các ràng buộc của hệ thống đang chạy mã và các giá trị được tìm kiếm.
Để phủ nhận điều này, chúng ta có thể thực hiện một sửa đổi đơn giản Như được hiển thị ở đây:
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
Điều này cung cấp một phiên bản tổng quát hơn của công thức tính đến các đặc thù bổ sung của việc sử dụng mô đun để xác định các giá trị tối đa của bạn.
Ví dụ về việc sử dụng một giá trị nhỏ cho RAND_MAX là một số nhân của N.
Phiên bản gốc của Mark'origen:
RAND_MAX = 3, n = 2, Values in RAND_MAX = 0,1,2,3, Valid Sets = 0,1 and 2,3.
When X >= (RAND_MAX - ( RAND_MAX % n ) )
When X >= 2 the value will be discarded, even though the set is valid.
Phiên bản tổng quát 1:
RAND_MAX = 3, n = 2, Values in RAND_MAX = 0,1,2,3, Valid Sets = 0,1 and 2,3.
When X > (RAND_MAX - ( ( RAND_MAX % n ) + 1 ) % n )
When X > 3 the value would be discarded, but this is not a vlue in the set RAND_MAX so there will be no discard.
Ngoài ra, trong trường hợp N phải là số lượng giá trị trong RAND_MAX; trong trường hợp này, bạn có thể đặt N = RAND_MAX +1, trừ khi RAND_MAX = INT_MAX.
Tuy nhiên, bạn có thể sử dụng N = 1 và bất kỳ giá trị nào của X sẽ được chấp nhận và đưa ra câu lệnh IF cho hệ số nhân cuối cùng của bạn. Nhưng có lẽ bạn có mã có thể có lý do hợp lệ để trả về 1 khi hàm được gọi với n = 1 ...
Vì vậy, có thể tốt hơn khi sử dụng 0, thông thường sẽ cung cấp Lỗi Div 0, khi bạn muốn có n = RAND_MAX + 1
Phiên bản tổng quát 2:
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
} else {
x = rand();
}
Cả hai giải pháp này đều giải quyết vấn đề với các kết quả hợp lệ bị loại bỏ một cách không cần thiết sẽ xảy ra khi RM + 1 là sản phẩm của n.
Phiên bản thứ hai cũng bao gồm kịch bản trường hợp cạnh khi bạn cần n bằng tổng số giá trị có thể có trong RAND_MAX.
Cách tiếp cận được sửa đổi trong cả hai đều giống nhau và cho phép giải pháp tổng quát hơn cho nhu cầu cung cấp số ngẫu nhiên hợp lệ và giảm thiểu các giá trị bị loại bỏ.
Để nhắc lại:
Giải pháp chung cơ bản mở rộng ví dụ về nhãn hiệu:
// Assumes:
// RAND_MAX is a globally defined constant, returned from the environment.
// int n; // User input, or externally defined, number of valid choices.
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) ) );
x %= n;
Giải pháp chung mở rộng cho phép thêm một kịch bản RAND_MAX + 1 = n:
// Assumes:
// RAND_MAX is a globally defined constant, returned from the environment.
// int n; // User input, or externally defined, number of valid choices.
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) ) );
x %= n;
} else {
x = rand();
}
Trong một số ngôn ngữ (ngôn ngữ được giải thích đặc biệt) thực hiện các tính toán của hoạt động so sánh bên ngoài điều kiện while có thể dẫn đến kết quả nhanh hơn vì đây là phép tính một lần cho dù có bao nhiêu lần thử lại. YMMV!
// Assumes:
// RAND_MAX is a globally defined constant, returned from the environment.
// int n; // User input, or externally defined, number of valid choices.
int x; // Resulting random number
int y; // One-time calculation of the compare value for x
if n != 0 {
y = RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n)
do {
x = rand();
} while (x > y);
x %= n;
} else {
x = rand();
}
RAND_MAX%n == n - 1
_ là(RAND_MAX + 1) % n == 0
. Khi đọc mã, tôi có xu hướng hiểu% something == 0
là cách chia đều dễ dàng hơn so với các cách tính toán khác. Tất nhiên, nếu stdlib C ++ của bạn cóRAND_MAX
cùng giá trị nhưINT_MAX
,(RAND_MAX + 1)
chắc chắn sẽ không hoạt động; vì vậy tính toán của Mark vẫn là cách thực hiện an toàn nhất.