2つのdoubleの間の乱数を生成することは可能ですか?
例:
public double GetRandomeNumber(double minimum, double maximum)
{
return Random.NextDouble(minimum, maximum)
}
それから私はそれを次のように呼びます:
double result = GetRandomNumber(1.23, 5.34);
どんな考えでもいただければ幸いです。
2つのdoubleの間の乱数を生成することは可能ですか?
例:
public double GetRandomeNumber(double minimum, double maximum)
{
return Random.NextDouble(minimum, maximum)
}
それから私はそれを次のように呼びます:
double result = GetRandomNumber(1.23, 5.34);
どんな考えでもいただければ幸いです。
回答:
はい。
Random.NextDoubleは、0と1の間のdoubleを返します。次に、これに入る必要がある範囲(最大と最小の差)を掛け、それをベースに追加します(最小)。
public double GetRandomNumber(double minimum, double maximum)
{
Random random = new Random();
return random.NextDouble() * (maximum - minimum) + minimum;
}
実際のコードはランダムな静的メンバーでなければなりません。これにより、乱数ジェネレータを作成するコストが節約され、GetRandomNumberを頻繁に呼び出すことができるようになります。すべての呼び出しで新しいRNGを初期化しているため、呼び出し間でシステム時刻が変わらないほど速く呼び出した場合、RNGはまったく同じタイムスタンプでシードされ、同じ乱数ストリームを生成します。
public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5は、拡張メソッドの作成を提案しました。これを行う方法を示すより完全なコード例を次に示します。
public static class RandomExtensions
{
public static double NextDouble(
this Random random,
double minValue,
double maxValue)
{
return random.NextDouble() * (maxValue - minValue) + minValue;
}
}
これで、Random
クラスのメソッドであるかのように呼び出すことができます。
Random random = new Random();
double value = random.NextDouble(1.23, 5.34);
Random
ループ内に多数の新しいオブジェクトを作成しないでください。これにより、同じ値が連続して何度も取得される可能性があります。たくさんの乱数が必要な場合はRandom
、のインスタンスを1つ作成して再利用してください。
気をつけてください。random
たとえばfor(int i = 0; i < 10; i++)
、ループの内部を生成する場合は、new Random()
宣言をループの中に入れないでください。
MSDNから:
乱数の生成は、シード値から始まります。同じシードを繰り返し使用すると、同じ一連の数値が生成されます。異なるシーケンスを生成する1つの方法は、シード値を時間依存にすることで、ランダムの新しいインスタンスごとに異なるシリーズを生成します。デフォルトでは、Randomクラスのパラメーターなしのコンストラクターは、システムクロックを使用してシード値を生成します...
この事実に基づいて、次のようにします。
var random = new Random();
for(int d = 0; d < 7; d++)
{
// Actual BOE
boes.Add(new LogBOEViewModel()
{
LogDate = criteriaDate,
BOEActual = GetRandomDouble(random, 100, 1000),
BOEForecast = GetRandomDouble(random, 100, 1000)
});
}
double GetRandomDouble(Random random, double min, double max)
{
return min + (random.NextDouble() * (max - min));
}
このようにすると、さまざまなdouble値が得られることが保証されます。
あなたはこれを行うことができます:
public class RandomNumbers : Random
{
public RandomNumbers(int seed) : base(seed) { }
public double NextDouble(double minimum, double maximum)
{
return base.NextDouble() * (maximum - minimum) + minimum;
}
}
私はパーティーに少し遅れましたが、一般的なソリューションを実装する必要があり、どのソリューションも自分のニーズを満たすことができないことがわかりました。
受け入れられているソリューションは、狭い範囲に適しています。ただし、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
ます。しかし、これは別の「問題」をもたらします。それはこの投稿でうまく提示されています。そのような大きな範囲を使用すると、値が「不自然」に見えるかもしれません。たとえば、0から10,000のランダムなdoubleを生成した後、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);
}
いくつかのテスト:
以下は、0からDouble.MaxValue
両方の戦略で10,000のランダムなdouble数を生成した結果のソートです。結果は対数目盛を使用して表示されます。
線形ランダム値は一見間違っているように見えますが、統計はそれらのどれも他より「優れている」ことはないことを示しています:線形戦略でさえ均一な分布を持ち、値間の平均差は両方の戦略でほとんど同じです。
さまざまな範囲で遊んだところ、線形戦略は範囲が0でushort.MaxValue
「妥当な」最小値10.78294704(ulong
範囲の最小値は3.03518E + 15; int
:353341)で「正気」であることがわかりました。これらは、異なるスケールで表示された両方の戦略の同じ結果です。
編集:
最近、ライブラリをオープンソースにRandomExtensions.NextDouble
しました。完全に検証されたメソッドを自由に見てください。
値の1つが負の場合はどうなりますか?より良いアイデアはないでしょう:
double NextDouble(double min, double max)
{
if (min >= max)
throw new ArgumentOutOfRangeException();
return random.NextDouble() * (Math.Abs(max-min)) + min;
}
Math.Abs()
は冗長だと思います。あなたはそれを保証したのでmin >= max
、max - min
とにかく負でない数でなければなりません。
[ double.MinValue
; の範囲の乱数が必要な場合 double.MaxValue
]
// Because of:
double.MaxValue - double.MinValue == double.PositiveInfinity
// This will be equals to NaN or PositiveInfinity
random.NextDouble() * (double.MaxValue - double.MinValue)
代わりに使用:
public static class RandomExtensions
{
public static double NextDoubleInMinMaxRange(this Random random)
{
var bytes = new byte[sizeof(double)];
var value = default(double);
while (true)
{
random.NextBytes(bytes);
value = BitConverter.ToDouble(bytes, 0);
if (!double.IsNaN(value) && !double.IsInfinity(value))
return value;
}
}
}
ループで呼び出す場合に同じ乱数を生成する方法については、ループの外側にある新しいRandom()オブジェクトをグローバル変数として宣言するのが便利です。
これをループで実行する場合は、GetRandomInt関数の外部でRandomクラスのインスタンスを宣言する必要があることに注意してください。
"どうしてこれなの?" あなたが尋ねる。
まあ、ランダムクラスは実際には擬似乱数を生成し、ランダマイザーの「シード」はシステム時間です。ループが十分に速い場合、システムクロック時間はランダマイザーと異なって表示されず、Randomクラスの新しい各インスタンスは同じシードで開始し、同じ擬似乱数を与えます。
ソースはここにあります:http : //www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/
スタティックランダムを使用するか、システムクロックがシードするため、数値がタイト/高速ループで繰り返される傾向があります。
public static class RandomNumbers
{
private static Random random = new Random();
//=-------------------------------------------------------------------
// double between min and the max number
public static double RandomDouble(int min, int max)
{
return (random.NextDouble() * (max - min)) + min;
}
//=----------------------------------
// double between 0 and the max number
public static double RandomDouble(int max)
{
return (random.NextDouble() * max);
}
//=-------------------------------------------------------------------
// int between the min and the max number
public static int RandomInt(int min, int max)
{
return random.Next(min, max + 1);
}
//=----------------------------------
// int between 0 and the max number
public static int RandomInt(int max)
{
return random.Next(max + 1);
}
//=-------------------------------------------------------------------
}
参照:https : //docs.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.8