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

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

Я пытаюсь создать набор различных случайных чисел между 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);
} 

Ответы

Ответ 1

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

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

Поскольку "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);
}

Ответ 2

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

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

Ответ 3

Повторно используйте человека. Как только вы захотите сделать, это набор чисел (Enumerable.Range будет делать), во-вторых, это случайное перемещение этих чисел. Вы можете найти ответ на эту проблему на 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);

Ответ 4

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

Ответ 5

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

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); }
}

Ответ 6

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

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

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

Ответ 7

Изменен мой неряшливый ответ!

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)));
}

Ответ 8

Если вы хотите сделать предварительный сбой для предварительного создания списка из 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);

Ответ 9

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

Чтобы ускорить его, вы можете многопоточно его использовать с помощью parallel.for - если это так, вы не можете использовать хешсет. Вместо этого используйте где байт будет просто ConcurrentDictionary of < int, byte > где байт будет "новым байтом()"

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