ฉันมาช้าไปงานปาร์ตี้ แต่ฉันต้องใช้โซลูชันทั่วไปและปรากฎว่าไม่มีวิธีใดที่สามารถตอบสนองความต้องการของฉันได้
ทางออกที่ยอมรับได้ดีสำหรับช่วงขนาดเล็ก อย่างไรก็ตามmaximum - minimum
อาจเป็นระยะอนันต์ ดังนั้นเวอร์ชันที่แก้ไขอาจเป็นเวอร์ชันนี้:
public static double NextDoubleLinear(this Random random, double minValue, double maxValue)
{
// TODO: some validation here...
double sample = random.NextDouble();
return (maxValue * sample) + (minValue * (1d - sample));
}
นี้จะสร้างตัวเลขสุ่มอย่างแม้กระทั่งระหว่างและdouble.MinValue
double.MaxValue
แต่นี่เป็นการแนะนำ "ปัญหา" อื่นซึ่งนำเสนออย่างดีในโพสต์นี้ : ถ้าเราใช้ช่วงใหญ่เช่นนั้นค่าอาจดูเหมือน "ผิดธรรมชาติ" ตัวอย่างเช่นหลังจากสร้าง 10,000 คู่สุ่มระหว่าง 0 และdouble.MaxValue
ค่าทั้งหมดอยู่ระหว่าง 2.9579E + 304 และ 1.7976E + 308
ดังนั้นฉันจึงสร้างเวอร์ชันอื่นซึ่งสร้างตัวเลขในสเกลลอการิทึม:
public static double NextDoubleLogarithmic(this Random random, double minValue, double maxValue)
{
// TODO: some validation here...
bool posAndNeg = minValue < 0d && maxValue > 0d;
double minAbs = Math.Min(Math.Abs(minValue), Math.Abs(maxValue));
double maxAbs = Math.Max(Math.Abs(minValue), Math.Abs(maxValue));
int sign;
if (!posAndNeg)
sign = minValue < 0d ? -1 : 1;
else
{
// if both negative and positive results are expected we select the sign based on the size of the ranges
double sample = random.NextDouble();
var rate = minAbs / maxAbs;
var absMinValue = Math.Abs(minValue);
bool isNeg = absMinValue <= maxValue ? rate / 2d > sample : rate / 2d < sample;
sign = isNeg ? -1 : 1;
// now adjusting the limits for 0..[selected range]
minAbs = 0d;
maxAbs = isNeg ? absMinValue : Math.Abs(maxValue);
}
// Possible double exponents are -1022..1023 but we don't generate too small exponents for big ranges because
// that would cause too many almost zero results, which are much smaller than the original NextDouble values.
double minExponent = minAbs == 0d ? -16d : Math.Log(minAbs, 2d);
double maxExponent = Math.Log(maxAbs, 2d);
if (minExponent == maxExponent)
return minValue;
// We decrease exponents only if the given range is already small. Even lower than -1022 is no problem, the result may be 0
if (maxExponent < minExponent)
minExponent = maxExponent - 4;
double result = sign * Math.Pow(2d, NextDoubleLinear(random, minExponent, maxExponent));
// protecting ourselves against inaccurate calculations; however, in practice result is always in range.
return result < minValue ? minValue : (result > maxValue ? maxValue : result);
}
การทดสอบบางอย่าง:
นี่คือผลการเรียงลำดับของการสร้าง 10,000 เลขสุ่มระหว่าง 0 ถึงDouble.MaxValue
ทั้งสองกลยุทธ์ ผลลัพธ์จะปรากฏขึ้นโดยใช้มาตราส่วนลอการิทึม:
แม้ว่าค่าสุ่มเชิงเส้นดูเหมือนว่าจะผิดในครั้งแรกที่สถิติแสดงให้เห็นว่าไม่มีของพวกเขา "ดีกว่า" อื่น ๆ : แม้กลยุทธ์เชิงเส้นมีการกระจายที่สม่ำเสมอและความแตกต่างเฉลี่ยระหว่างค่าค่อนข้างเหมือนกันมากกับทั้งสองกลยุทธ์ .
การเล่นกับช่วงที่แตกต่างกันแสดงให้ฉันเห็นว่ากลยุทธ์เชิงเส้นต้องเป็น "สติ" ที่มีช่วงระหว่าง 0 และushort.MaxValue
ด้วยค่าต่ำสุด "สมเหตุสมผล" ที่ 10.78294704 (สำหรับulong
ช่วงค่าต่ำสุดคือ 3.03518E + 15 int
;: 353341) สิ่งเหล่านี้เป็นผลลัพธ์เดียวกันของทั้งสองกลยุทธ์ที่แสดงด้วยเครื่องชั่งที่แตกต่างกัน:
แก้ไข:
เมื่อเร็ว ๆ นี้ฉันทำให้ห้องสมุดโอเพ่นซอร์สของฉันรู้สึกอิสระที่จะเห็นRandomExtensions.NextDouble
วิธีการตรวจสอบที่สมบูรณ์