web-dev-qa-db-ja.com

ランダムで一意の値を生成するC#

私はしばらく検索して、これを見つけるのに苦労していました、私はいくつかのランダムな、ユニークな番号を生成しようとしていますC#です。私は_System.Random_を使用しており、_DateTime.Now.Ticks_シードを使用しています:

_public Random a = new Random(DateTime.Now.Ticks.GetHashCode());
private void NewNumber()
{
    MyNumber = a.Next(0, 10);
}
_

私はNewNumber()を定期的に呼び出していますが、問題は頻繁に数字が繰り返されることです。一部の人々は、私がそれをするたびにランダムを宣言していたので、乱数を生成しないので、提案を関数の外に置いたので提案しました。 _System.Random_を使用するよりも提案や良い方法はありますか?ありがとうございました

27
Christian Peut

私はNewNumber()を定期的に呼び出していますが、問題は頻繁に数字が繰り返されることです。

Random.Nextは、番号が一意であることを保証しません。また、範囲は0〜10であり、重複する値が取得される可能性があります。 intのリストをセットアップし、リストに重複が含まれていないかどうかを確認した後、リストに乱数を挿入できます。何かのようなもの:

public Random a = new Random(); // replace from new Random(DateTime.Now.Ticks.GetHashCode());
                                // Since similar code is done in default constructor internally
public List<int> randomList = new List<int>();
int MyNumber = 0;
private void NewNumber()
{
    MyNumber = a.Next(0, 10);
    if (!randomList.Contains(MyNumber))
        randomList.Add(MyNumber);
}
22
Habib

範囲が0から9のみの場合は、可能なintの配列をシャッフルしてみてください。これにより、数値生成の競合を回避する利点が追加されます。

var nums = Enumerable.Range(0, 10).ToArray();
var rnd = new Random();

// Shuffle the array
for (int i = 0;i < nums.Length;++i)
{
    int randomIndex = rnd.Next(nums.Length);
    int temp = nums[randomIndex];
    nums[randomIndex] = nums[i];
    nums[i] = temp;
}

// Now your array is randomized and you can simply print them in order
for (int i = 0;i < nums.Length;++i)
    Console.WriteLine(nums[i]);
16
itsme86

注、これはお勧めできません:)。「oneliner」も同様です。

//This code generates numbers between 1 - 100 and then takes 10 of them.
var result = Enumerable.Range(1,101).OrderBy(g => Guid.NewGuid()).Take(10).ToArray();
11
JOSEFtw

シャッフルアルゴリズムの正しい実装を投稿しています。ここに投稿された他のアルゴリズムは、均一なシャッフルを生成しないためです。

他の答えが示すように、少数の値をランダム化するには、単純に配列にそれらの値を入れ、配列をシャッフルしてから、必要な値をいくつでも使用できます。

以下は Fisher-Yates Shuffle (別名Knuth Shuffle)の実装です。 (そのリンクの「実装エラー」セクションを読んで(「すべての反復で有効な配列インデックスの範囲全体からjを常に選択する」を検索してください)、ここに掲載されている他の実装の問題点に関する議論を参照してください。)

using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
    static class Program
    {
        static void Main(string[] args)
        {
            Shuffler shuffler = new Shuffler();
            List<int> list = new List<int>{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            shuffler.Shuffle(list);

            foreach (int value in list)
            {
                Console.WriteLine(value);
            }
        }
    }

    /// <summary>Used to shuffle collections.</summary>

    public class Shuffler
    {
        /// <summary>Creates the shuffler with a <see cref="MersenneTwister"/> as the random number generator.</summary>

        public Shuffler()
        {
            _rng = new Random();
        }

        /// <summary>Shuffles the specified array.</summary>
        /// <typeparam name="T">The type of the array elements.</typeparam>
        /// <param name="array">The array to shuffle.</param>

        public void Shuffle<T>(IList<T> array)
        {
            for (int n = array.Count; n > 1; )
            {
                int k = _rng.Next(n);
                --n;
                T temp = array[n];
                array[n] = array[k];
                array[k] = temp;
            }
        }

        private System.Random _rng;
    }
}
9
Matthew Watson

このすぐに使用できる方法を確認してください:取得する番号の範囲と数を指定します。

public static int[] getUniqueRandomArray(int min, int max, int count) {
    int[] result = new int[count];
    List<int> numbersInOrder = new List<int>();
    for (var x = min; x < max; x++) {
        numbersInOrder.Add(x);
    }
    for (var x = 0; x < count; x++) {
        var randomIndex = Random.Range(0, numbersInOrder.Count);
        result[x] = numbersInOrder[randomIndex];
        numbersInOrder.RemoveAt(randomIndex);
    }

    return result;
}
2
Evren Ozturk

このようなことができるようになった後、あなたが本当に何であるかに応じて:

using System;
using System.Collections.Generic;
using System.Linq;

namespace SO14473321
{
    class Program
    {
        static void Main()
        {
            UniqueRandom u = new UniqueRandom(Enumerable.Range(1,10));
            for (int i = 0; i < 10; i++)
            {
                Console.Write("{0} ",u.Next());
            }
        }
    }

    class UniqueRandom
    {
        private readonly List<int> _currentList;
        private readonly Random _random = new Random();

        public UniqueRandom(IEnumerable<int> seed)
        {
            _currentList = new List<int>(seed);
        }

        public int Next()
        {
            if (_currentList.Count == 0)
            {
                throw new ApplicationException("No more numbers");
            }

            int i = _random.Next(_currentList.Count);
            int result = _currentList[i];
            _currentList.RemoveAt(i);
            return result;
        }
    }
}
1
Andrew Savinykh

そして、ここではHashSetを使用してN個のランダムな一意の番号を見つける私のバージョンです。 HashSetには異なるアイテムのみを含めることができるため、非常に単純に見えます。おもしろいです-ListやShufflerを使用するよりも速いでしょうか?

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class RnDHash
    {
        static void Main()
        {
            HashSet<int> rndIndexes = new HashSet<int>();
            Random rng = new Random();
            int maxNumber;
            Console.Write("Please input Max number: ");
            maxNumber = int.Parse(Console.ReadLine());
            int iter = 0;
            while (rndIndexes.Count != maxNumber)
            {
                int index = rng.Next(maxNumber);
                rndIndexes.Add(index);
                iter++;
            }
            Console.WriteLine("Random numbers were found in {0} iterations: ", iter);
            foreach (int num in rndIndexes)
            {
                Console.WriteLine(num);
            }
            Console.ReadKey();
        }
    }
}
0
Vasily Novsky

私は、受け入れられた答えがintをリストに追加し続け、if (!randomList.Contains(MyNumber))でチェックし続けることに注意し、特に新しい数字を要求し続ける場合、これはうまくスケーリングしないと思います。

私は反対をします。

  1. 起動時にリストを直線的に生成する
  2. リストからランダムインデックスを取得する
  3. 見つかったintをリストから削除します

これには、起動時にもう少し時間がかかりますが、スケーラビリティははるかに向上します。

public class RandomIntGenerator
{
    public Random a = new Random();
    private List<int> _validNumbers;

    private RandomIntGenerator(int desiredAmount, int start = 0)
    {
        _validNumbers = new List<int>();
        for (int i = 0; i < desiredAmount; i++)
            _validNumbers.Add(i + start);
    }

    private int GetRandomInt()
    {
        if (_validNumbers.Count == 0)
        {
            //you could throw an exception here
            return -1;
        }
        else
        {
            var nextIndex = a.Next(0, _validNumbers.Count - 1);
            var number    = _validNumbers[nextIndex];
            _validNumbers.RemoveAt(nextIndex);
            return number;
        }
    }
}
0
Jack Mariani