配列 - wpf c# random




int.MinValueとint.MaxValueの間の乱数(両端を含む) (10)

このメソッドは、整数の制限内でランダムな整数を提供できます。 最大制限がint.MaxValueより小さい場合、通常のRandom.Next(Int32、Int32)を使用しますが、その値を含めるために上限に1を追加します。 そうでない場合、下限がint.MinValueよりも大きい場合、下限を1に下げて、結果に1を加える範囲を1だけ小さくシフトします。 最後に、両方の制限がint.MinValueとint.MaxValueである場合、それぞれ50%の確率で0または1のランダムな整数「a」を生成し、次に他の2つの整数を生成します。最初の整数はint.MinValueと-1、2147483648の値、2番目は0からint.MaxValueの値、2147483648の値までであり、値を「a」で使用すると、完全に等しい確率で整数が選択されます。

private int RandomInclusive(int min, int max)
{
    if (max < int.MaxValue)
        return Random.Next(min, max + 1);
    if (min > int.MinValue)
        return Random.Next(min - 1, max) + 1;
    int a = Random.Next(2);
    return Random.Next(int.MinValue, 0) * a + (Random.Next(-1, int.MaxValue) + 1) * (1 - a);
}

ここにちょっとした謎があります: Random.Next() は、最小値と最大値を受け入れるオーバーロードがあります。 このオーバーロードは、最小値以上(包括的)で最大値未満(排他的)の数値を返します。

最大値を含む範囲全体を含めたいです。 場合によっては、最大値に1を追加するだけでこれを実現できます。 しかし、この場合、最大値は int.MaxValue である可能性が int.MaxValue 、これに1を追加すると、私が望むものを達成できません。

だから誰もが int.MinValue から int.MinValue までの int.MaxValue を包括的に取得する良いトリックを知っていますか?

更新:

下側の範囲は int.MinValue することができますが、他の範囲にすることもできます。 常に int.MinValue ことがわかっている場合、問題はより単純になります。


これがRandom.Next(int、int)の実装ではないことは実際に興味深いです。なぜなら、排他的な振る舞いを包含的な振る舞いから導き出すことができますが、その逆はできないからです。

Number1 = rng.Next(-2.147.483.648, 0);
Number2 = rng.Next(0, 2.147.483.647);
resut = Number1 + Number2;
Number = rng.Next(Int.MinValue, Int.MaxValue);
if(Number > 3)
  Number = Number +1;
skipAbleNumber = rng.Next(Int.MinValue +1, Int.MaxValue);
public static class RandomExtensions
{
    private const long IntegerRange = (long)int.MaxValue - int.MinValue;

    public static int NextInclusive(this Random random, int minValue, int maxValue)
    {
        if (minValue > maxValue)
        {
            throw new ArgumentOutOfRangeException(nameof(minValue));
        }

        var buffer = new byte[4];
        random.NextBytes(buffer);
        var a = BitConverter.ToInt32(buffer, 0);
        var b = a - (long)int.MinValue;
        var c = b * (1.0 / IntegerRange);
        var d = c * ((long)maxValue - minValue + 1);
        var e = (long)d + minValue;
        return (int)e;
    }
}
new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1

これはあなたのために働きますか?

int random(Random rnd, int min, int max) 
{
    return Convert.ToInt32(rnd.NextDouble() * (max - min) + min);
}

これはどうですか?

   using System;

    public class Example
    {
       public static void Main()
       {
          Random rnd = new Random();
          int min_value = max_value;
          int max_value = min_value;

          Console.WriteLine("\n20 random integers from 10 to 20:");
          for (int ctr = 1; ctr <= 20; ctr++) 
          {
             Console.Write("{0,6}", rnd.Next(min_value, max_value));
             if (ctr % 5 == 0) Console.WriteLine();
          }
       }
    }

まあ、私はトリックがあります。 私はそれを「良いトリック」と表現するかどうかはわかりませんが、うまくいくかもしれません。

static class RandomExtension
{
    private static readonly byte[] bytes = new byte[sizeof(int)];

    public static int InclusiveNext(this Random random, int min, int max)
    {
        if (max < int.MaxValue)
            // can safely increase 'max'
            return random.Next(min, max + 1);

        // now 'max' is definitely 'int.MaxValue'
        if (min > int.MinValue)
            // can safely decrease 'min'
            // so get ['min' - 1, 'max' - 1]
            // and move it to ['min', 'max']
            return random.Next(min - 1, max) + 1;

        // now 'max' is definitely 'int.MaxValue'
        // and 'min' is definitely 'int.MinValue'
        // so the only option is
        random.NextBytes(bytes);
        return BitConverter.ToInt32(bytes, 0);
    }
}

したがって、基本的には、上限が int.MaxValue 場合に4バイトを生成し、 int に変換するだけの拡張メソッドです。そうでない場合は、標準の Next(int, int) オーバーロードを使用します。

maxValueint.MaxValue 場合、 int.MaxValue は無視されることに注意してください。 私はそれを説明しなかったと思います...


キャストなし、 long ない、すべての境界ケースを考慮に入れた、最高のパフォーマンス。

public static class RandomExtensions
{
    public static int NextInclusive(this Random rng, int minValue, int maxValue)
    {
        if (maxValue == int.MaxValue)
        {
            var bytes = new byte[4];
            rng.NextBytes(bytes);
            return BitConverter.ToInt32(bytes, 0);
        }
        return rng.Next(minValue, maxValue + 1);
    }
}

私が理解しているように、ランダムに-2.147.483.648と+2.147.483.647の間の値を出力する必要があります。 しかし問題は、これらの値を与えられたRandomが-2.147.483.648から+2.147.483.64 6 までの値しか与えないことです。最大値は排他的です。

オプション0:物を持ち帰り、それなしでやることを学ぶ

ダグラス・アダムスはプログラマーの知る限りではありませんでしたが、彼にはいくつかの良いアドバイスがあります:「目に見えないものを作る技術は非常に複雑であるため、99億9千9億9千9百9 -1兆回のうち9万回、99回、それを取り去り、それなしでやる方がはるかに簡単で効果的です。」

これはそのような場合かもしれません。

オプション1:より大きなランダムが必要です!

Random.Next()は、引数としてInt32を使用します。 私が考えることができる1つのオプションは、入力として次に高いレベルの整数(Int64)を取ることができる別のランダム関数を使用することです。 Int32は暗黙的にInt64にキャストされます。 Int64 Number = Int64(Int32.MaxValue)+1;

ただし、これを行うには、.NETライブラリの外部に移動する必要があります。 その時点で、Maxを含むRandomを探すこともできます。

しかし、1つの値を除外しなければならなかった数学的な理由があると思います。

オプション2:より多くのロール

別の方法は、Randomの2つの呼び出し(それぞれ範囲の半分)を使用し、それらを追加することです。

new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue

ただし、ランダム分布を台無しにすることは90%確信しています。 私のP&P RPGの経験は、サイコロのチャンスの経験を与えてくれました。事実、2つのサイコロを振る(または同じ2回)ことで、特定の1つのダイとは非常に異なる結果の分布が得られます。 このランダム分布が必要ない場合は、オプションです。 しかし、配布についてあまり気にしないのであれば、チェックする価値があります。

オプション3:全範囲が必要ですか? それとも、最小値と最大値が含まれているだけですか?

何らかの形式のテストを実行しており、Int.MaxValueとInt.MinValueの両方が範囲内にある必要があると思います。 しかし 、その間の すべての値も必要ですか、それともどちらも必要ありませんか? 値を失う必要がある場合、Int.MaxValueではなく4を失うことを好むでしょうか?

new Random(-451732719).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue

このようなコードは、 4を除く MinValueとMaxValueの間のすべての数値を取得します。 しかし、ほとんどの場合、3と5を処理できるコードは4も処理できます。4を明示的にテストする必要はありません。

もちろん、4は実行する必要のある重要なテスト番号ではないと想定しています(これらの理由から1と0は避けました)。 番号をランダムに「スキップ」することもできます。

new Random(-394328071).NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue

そして、 > 4 ではなく > skipAbleNumber を使用します。


範囲を2つに分割し、 MaxValue を補正します。

r.Next(2) == 0 ? r.Next(int.MinValue, 0) : (1 + r.Next(-1, int.MaxValue))

範囲を同じサイズにすると、異なる数学で同じ結果を得ることができます。 ここでは、 int.MinValue = -1 - int.MaxValue という事実に依存してい int.MinValue = -1 - int.MaxValue

r.Next(int.MinValue, 0) - (r.Next(2) == 0 ? 0 : int.MinValue)

Random.Next(int minValue, int maxValue) 内部実装 は、 Int32.MinValueInt32.MaxValue 間の範囲など、大きな範囲の2つのサンプルを生成します。 NextInclusive メソッドでは、合計4つのサンプルを含む別の大きな範囲 Next を使用する必要がありました。 そのため、パフォーマンスは、バッファーを4バイト(バイトごとに1サンプル)で埋めるバージョンと同等になります。

public static class RandomExtensions
{
    public static int NextInclusive(this Random random, int minValue, int maxValue)
    {
        if (maxValue == Int32.MaxValue)
        {
            if (minValue == Int32.MinValue)
            {
                var value1 = random.Next(Int32.MinValue, Int32.MaxValue);
                var value2 = random.Next(Int32.MinValue, Int32.MaxValue);
                return value1 < value2 ? value1 : value1 + 1;
            }
            return random.Next(minValue - 1, Int32.MaxValue) + 1;
        }
        return random.Next(minValue, maxValue + 1);
    }

}

いくつかの結果:

new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue
new Random(24917099).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue
var random = new Random(784288084);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue

更新: 私の実装では、可能な最大範囲( Int32.MinValue - Int32.MaxValue )で平凡なパフォーマンスを実現しているため、4倍高速な新しいものを思い付きました。 私のマシンでは毎秒約22,000,000の乱数を生成します。 それより速くなるとは思わない。

public static int NextInclusive(this Random random, int minValue, int maxValue)
{
    if (maxValue == Int32.MaxValue)
    {
        if (minValue == Int32.MinValue)
        {
            var value1 = random.Next() % 0x10000;
            var value2 = random.Next() % 0x10000;
            return (value1 << 16) | value2;
        }
        return random.Next(minValue - 1, Int32.MaxValue) + 1;
    }
    return random.Next(minValue, maxValue + 1);
}

いくつかの結果:

new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue
new Random(1655705829).NextInclusive(int.MinValue, int.MaxValue); // = int.MaxValue
var random = new Random(1704364573);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // = int.MinValue

System.Numerics.BigInteger ように使用することをお勧めします。

class InclusiveRandom
{
    private readonly Random rnd = new Random();

    public byte Next(byte min, byte max) => (byte)NextHelper(min, max);
    public sbyte Next(sbyte min, sbyte max) => (sbyte)NextHelper(min, max);
    public short Next(short min, short max) => (short)NextHelper(min, max);
    public ushort Next(ushort min, ushort max) => (ushort)NextHelper(min, max);
    public int Next(int min, int max) => (int)NextHelper(min, max);
    public uint Next(uint min, uint max) => (uint)NextHelper(min, max);
    public long Next(long min, long max) => (long)NextHelper(min, max);
    public ulong Next(ulong min, ulong max) => (ulong)NextHelper(min, max);

    private BigInteger NextHelper(BigInteger min, BigInteger max)
    {
        if (max <= min)
            throw new ArgumentException($"max {max} should be greater than min {min}");

        return min + RandomHelper(max - min);
    }

    private BigInteger RandomHelper(BigInteger bigInteger)
    {
        byte[] bytes = bigInteger.ToByteArray();
        BigInteger random;

        do
        {
            rnd.NextBytes(bytes);
            bytes[bytes.Length - 1] &= 0x7F;
            random = new BigInteger(bytes);
        } while (random > bigInteger);

        return random;
    }
}

sbyte テストし sbyte

var rnd = new InclusiveRandom();
var frequency = Enumerable.Range(sbyte.MinValue, sbyte.MaxValue - sbyte.MinValue + 1).ToDictionary(i => (sbyte)i, i => 0ul);
var count = 100000000;
for (var i = 0; i < count; i++)
    frequency[rnd.Next(sbyte.MinValue, sbyte.MaxValue)]++;
foreach (var i in frequency)
    chart1.Series[0].Points.AddXY(i.Key, (double)i.Value / count);

chart1.ChartAreas[0].AxisY.StripLines
    .Add(new StripLine { Interval = 0, IntervalOffset = 1d / 256, StripWidth = 0.0003, BackColor = Color.Red });

配布はOKです。





random