Генерировать различные случайные числа в C#

просто интересно, можете ли вы подтвердить, что следующий код действителен и посоветовать, есть ли лучшие альтернативы ему?

Я пытаюсь создать коллекцию различных случайных чисел от 1 до 100000.

Random rand = new Random();
List<Int32> result = new List<Int32>();
for (Int32 i = 0; i < 300; i++)
{
    Int32 curValue = rand.Next(1, 100000);
    while (result.Exists(value => value == curValue))
    {
        curValue = rand.Next(1, 100000);
    }
    result.Add(curValue);
} 

9 ответов


Да, насколько я могу судить, этот код делает именно то, что вы хотите.

цикл по списку для проверки каждого значения не очень эффективен. Вы можете поместить значения в HashSet<int> для ускорения проверки.

поскольку HashSet не сохраняет порядок элементов, вам все равно нужно List но:

Random rand = new Random();
List<int> result = new List<int>();
HashSet<int> check = new HashSet<int>();
for (Int32 i = 0; i < 300; i++) {
    int curValue = rand.Next(1, 100000);
    while (check.Contains(curValue)) {
        curValue = rand.Next(1, 100000);
    }
    result.Add(curValue);
    check.Add(curValue);
}

Не то, чтобы это имело большое значение, если вам просто иногда нужны 300 номеров, но было бы более эффективно использовать HashSet, который выполняет O(1) поиск, а не o(n), как в списке

Random rand = new Random();
HashSet<int> result = new HashSet<int>();
while (result.Count < 300)
{
    result.Add(rand.Next(1, 1000000));
}

повторно использовать man. Когда-то, что вы хотите сделать-это коллекция numebers (Перечислимым.Диапазон будет делать), второе-перетасовка (случайным образом) этих чисел. Вы можете найти ответ на эту проблему на SO, и благодаря LINQ решение красиво, но, возможно, не самый быстрый, в любом случае я использую его, потому что мне нравится его красота.

LINQ в перетасовка: http://www.code56.com/2009/02/lets-do-some-shuffling.html (мертвая ссылка). Очень похожая реализация: http://csharpsimple.blogspot.com/2012/01/shuffle-in-linq-part-2.html

короче:

var rand = new Random();
        return source.Select(t => new {
                Index = rand.Next(),
                Value = t })
            .OrderBy(p => p.Index)
            .Select(p => p.Value);

Это будет делать то, что вы хотите, за исключением того, что выбрасывание числа, потому что вы видели его раньше, уменьшает энтропию (случайность) последовательности.


ваша версия будет работать, так что я предлагаю просто забавная альтернатива:

List<Int32> result = new List<Int32>();
Queue<int> working = new Queue<int>(new int[2] {1, 100000});
Random rand = new Random();

while (result.Count < 300) {
    int lower = working.Dequeue();
    int upper = working.Dequeue();
    int pivot = rand.Next(lower,upper);
    result.Add(pivot);
    if(lower < pivot) { working.Enqueue(lower); working.Enqueue(pivot); }
    if(pivot+1 < upper) { working.Enqueue(pivot + 1); working.Enqueue(upper); }
}

в этих случаях я всегда думаю, что вам действительно не нужно случайные цифры. Вместо этого вы больше собираетесь перетасовать некоторые существующие числа.

Так как насчет того, чтобы взять диапазон, который вам нравится, с чем-то вроде var range = Enumerable.Range(1, 100000), перетасуйте их и возьмите необходимое количество элементов, что-то вроде range.ToArray().Shuffle().Take(300)?

одна реализация алгоритма shuffle может быть найти здесь. Я знаю, что это не метод расширения, который можно использовать так, как описано выше, но изменить подпись довольно легко.


Если вы готовы принять передний удар, чтобы предварительно создать список из 100 000 ints, этот метод очень быстрый:

static IList<int> GetRandoms(int[] sample)
{
    var rand = new Random();
    var result = new int[300];
    var count = sample.Length;

    for (int i = 0; i < 300; i++)
    {
        var index = rand.Next(count);
        result[i] = sample[index];
        sample[index] = sample[--count];
    }
    return result;
}

Так, вы бы назвали это так:

var sample = Enumerable.Range(1, 100000).ToArray();
var data = GetRandoms(sample);

изменить мой небрежный ответ!

var result = new HashSet<int>();
var random = new Random();
var seq = Enumerable.Range(1, 30).GetEnumerator();
while(seq.MoveNext()) {
  while(!result.Add(random.Next(1, 100000)));
}

Я понимаю, что это действительно старый пост, но добавить в моем недавнем опыте с делать что-то в том же духе (мне пришлось генерировать 100k случайных идентификаторов документов с иностранными идентификаторами, в которых оба должны были быть уникальными списками#).

чтобы ускорить его, вы можете захотеть многопоточность с параллелью.если это так, вы не можете использовать hashset. Вместо этого используйте where byte будет просто ConcurrentDictionary где байт будет " new byte ()"

время выполнения от ~1,5 часа до 20 минут для меня. Если вы должны, я бы предложил использовать linkedList для хранения, а не очередь или список, если у вас нет конкретной потребности в случайном доступе.