Mark's Solution (Kabul edilen çözüm) Neredeyse Mükemmel.
int x;
do {
x = rand();
} while (x >= (RAND_MAX - RAND_MAX % n));
x %= n;
25 Mart'ta saat 23: 16'da düzenlendi
Mark Amery 39k21170211
Bununla birlikte, herhangi bir senaryoda 1 geçerli sonuç kümesini atayan bir uyarı vardır; burada RAND_MAX
( RM
), ( ) sayısının katından 1 daha azdır N
(Where N
= olası geçerli sonuçların sayısı).
yani, 'atılan değer sayısı' ( D
) eşit olduğunda N
, bunlar aslında geçerli V)
bir kümedir (geçersiz bir küme değil I
).
Bunun nedenleri Mark arasındaki fark gözden kaybeder noktada olduğunu N
ve Rand_Max
.
N
geçerli üyeleri bir dizi yanıt içerdiğinden, geçerli üyeleri yalnızca Pozitif Tamsayılardan oluşan bir kümedir. (örneğin: Set N
= {1, 2, 3, ... n }
)
Rand_max
Bununla birlikte (bizim amaçlarımız için tanımlandığı gibi) herhangi bir sayıda negatif olmayan tam sayı içeren bir kümedir.
En genel biçiminde, burada Rand Max
teorik olarak negatif sayılar veya sayısal olmayan değerler içerebilen tüm geçerli sonuçların Kümesi olarak tanımlanan şeydir .
Bu nedenle Rand_Max
"Olası Yanıtlar" kümesi olarak daha iyi tanımlanır.
Bununla birlikte N
, geçerli yanıtlar kümesindeki değerlerin sayımına karşı çalışır, bu nedenle özel durumumuzda tanımlandığı gibi, Rand_Max
içerdiği toplam sayıdan daha az bir değer olacaktır.
Mark'ın Çözümü kullanılarak, Değerler şu durumlarda Atılır: 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
Yukarıdaki örnekte de görebileceğiniz gibi, X değeri (başlangıç işlevinden aldığımız rastgele sayı) 252, 253, 254 veya 255 olduğunda, bu dört değer geçerli bir döndürülen değerler kümesi içermesine rağmen onu atacağız .
IE: Atılan değerlerin sayısı (I) = N (Geçerli sonuçların sayısı) geçerli bir dönüş değeri kümesi orijinal işlev tarafından atılır.
N ve RM değerleri arasındaki farkı D olarak tanımlarsak, yani:
D = (RM - N)
Daha sonra D değeri küçüldükçe, bu yönteme bağlı olarak gereksiz yeniden silindirlerin yüzdesi her bir doğal çarpımda artar. (RAND_MAX bir Prime Numarasına DEĞİL DEĞİLSE, bu geçerli bir husustur)
ÖRNEĞİN:
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%
İhtiyaç duyulan Reroll'lerin yüzdesi N'nin RM'ye yaklaştıkça artması nedeniyle, bu kodu çalıştıran sistemin kısıtlamalarına ve aranan değerlere bağlı olarak birçok farklı değer için geçerli olabilir.
Bunu reddetmek için burada gösterildiği gibi basit bir değişiklik yapabiliriz:
int x;
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
Bu, formülün, maksimum değerlerinizi tanımlamak için modül kullanmanın ek özelliklerini açıklayan daha genel bir sürümünü sağlar.
N'nin çarpımı olan RAND_MAX için küçük bir değer kullanma örnekleri.
Mark'original Versiyon:
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.
Genelleştirilmiş Sürüm 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.
Ayrıca, N'nin RAND_MAX içindeki değerlerin sayısı olması durumunda; bu durumda, RAND_MAX = INT_MAX olmadığı sürece N = RAND_MAX +1 ayarlayabilirsiniz.
Ancak, sadece N = 1 kullanabilirsiniz ve X'in herhangi bir değeri kabul edilir ve son çarpanınız için bir IF ifadesi ekler. Ancak, işlev n = 1 ile çağrıldığında 1 döndürmek için geçerli bir nedeni olabilecek bir kodunuz olabilir ...
Bu nedenle, n = RAND_MAX + 1 olmasını istediğinizde, normalde bir Div 0 Hatası sağlayacak olan 0 kullanmak daha iyi olabilir.
Genelleştirilmiş Sürüm 2:
int x;
if n != 0 {
do {
x = rand();
} while (x > (RAND_MAX - ( ( ( RAND_MAX % n ) + 1 ) % n) );
x %= n;
} else {
x = rand();
}
Bu çözümlerin her ikisi de, RM + 1 bir n ürünü olduğunda ortaya çıkan gereksiz yere atılmış geçerli sonuçlarla sorunu çözmektedir.
İkinci sürüm ayrıca, RAND_MAX'ta bulunan toplam olası değer kümesine eşit olması gerektiğinde uç senaryo senaryosunu da kapsar.
Her ikisinde de değiştirilmiş yaklaşım aynıdır ve geçerli rastgele sayılar sağlama ve atılan değerleri en aza indirme ihtiyacına daha genel bir çözüm sağlar.
Tekrarlamak için:
Markanın örneğini genişleten Temel Genel Çözüm:
// 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;
Bir ek RAND_MAX + 1 = n senaryosuna izin veren Genişletilmiş Genel Çözüm:
// 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();
}
Bazı dillerde (özellikle yorumlanan diller), karşılaştırma işleminin while koşulu dışında hesaplamaları yapmak, daha fazla yeniden deneme yapılması gerekmeksizin tek seferlik bir hesaplama olduğundan daha hızlı sonuçlara yol açabilir. 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
_ _(RAND_MAX + 1) % n == 0
. Kodu okurken,% something == 0
diğer eşit hesaplama yöntemlerinden daha kolay “eşit bölünebilir” olarak anlamaya meyilliyim . Tabii ki, C ++ stdlibRAND_MAX
ile aynı değere sahipseINT_MAX
,(RAND_MAX + 1)
kesinlikle işe yaramaz; Mark'ın hesaplaması en güvenli uygulama olmaya devam ediyor.