Генерировать различные случайные числа в С#
Просто интересно, можете ли вы подтвердить, что приведен следующий код, и сообщить, есть ли у него лучшие альтернативы?
Я пытаюсь создать набор различных случайных чисел между 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 для хранения, а также вместо очереди или списка, если у вас нет необходимости в произвольном доступе.