Обучение C#

Формула программиста

основатель — Волосатов Евгений Витольдович
Поздравляю с 1 сентября! Система начисления Байтов работает.

Генератор случайных чисел

Очень часто в программах возникает необходимость использования случайных чисел - от заполнения массива до криптографии. Для получения последовательности случайных чисел в языке C# имеется класс Random. Этот класс предусматривает два конструктора:
  • Random() - инициализирует экземпляр класса Random с помощью начального значения, зависящего от текущего времени. Как известно, время может быть представлено в тиках - 100-наносекундных импульсах, начиная с 1 января 0001 года. И значение времени в тиках представляет собой 64-битное целое число, которое и будет использоваться для инициализации экземпляра генератора случайных чисел.
  • Random(Int32) - инициализирует экземпляр класса Random с помощью указанного начального значения. Такая инициализация генератора случайных чисел может быть удобна на этапе отладки программы, поскольку в этом случае при каждом запуске программы будут генерироваться одни и те же "случайные" числа.
Основным методом данного класса является метод Next(), позволяющий получить случайное число и имеющий ряд перегрузок:
  • Next() - возвращает случайное целое неотрицательное число формата Int32.
  • Next(Int32) - возвращает случайное целое неотрицательное число, которое меньше указанного значения.
  • Next(Int32 min, Int32 max) - возвращает случайное целое число в указанном диапазоне. При этом должно соблюдаться условие min<max (первый аргумент должен быть меньше второго).
А также методы
  • NextBytes(Byte[]) - заполняет элементы указанного массива байтов случайными числами.
  • NextDouble() - возвращает случайное число с плавающей запятой, в диапазоне [0,0; 1,0).
Пример программы, выводящей 100 случайных чисел в диапазоне от -100 до 100.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;
namespace MyProgram
{
  class Program
  {
    static void Main(string[] args)
    {
      Random rnd = new Random();
      for (int i = 0; i < 100; i++)
        Console.WriteLine("{0,4}", rnd.Next(-100, 101));
      Console.ReadKey();
    }
  }
}
image articles/522/522_1.png
Однако если создавать новый экземпляр класса Random каждый раз, когда требуется сгенерировать случайное число, то это может привести к тому, что генерируемые числа будут одинаковыми.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
namespace MyProgram
{
  class Program
  {
    static void Main(string[] args)
    {
      for (int i = 0; i < 10; i++)
      {
        Random rnd = new Random();
        Console.WriteLine("{0,4}", rnd.Next(-100, 101));
      }
      Console.ReadKey();
    }
  }
}
image articles/522/522_2.png
Поэтому для генерации случайных чисел в программе необходимо создать единственный экземпляр класса Random. Подробнее об этой проблеме смотрите в видео.
 

Генерация неповторяющейся последовательности чисел

Для генерации последовательности неповторяющихся чисел требуется массив, в котором будут сохраняться уже сгенерированные числа. При этом каждое вновь сгенерированное число сравнивается со всеми уже сгенерированными элементами массива и, в случае несовпадения, добавляется в массив. В случае совпадения числа с одним из элементов массива генерируется новое значение для указанного элемента массива и сравнение повторяется.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System;
namespace MyProgram
{
  class Program
  {
    static void Main(string[] args)
    {
      Random rnd = new Random();
      int[] a = new int[100];
      a[0] = rnd.Next(0, 101);
      for (int i = 1; i < 100;)
      {
        int num = rnd.Next(0, 101); // генерируем элемент
        int j;
        // поиск совпадения среди заполненных элементов
        for (j = 0; j < i; j++)
        {
          if (num == a[j])
            break; // совпадение найдено, элемент не подходит
        }
        if (j == i)
        { // совпадение не найдено
          a[i] = num; // сохраняем элемент
          i++; // переходим к следующему элементу
        }
      }
      for (int i = 0; i < 100; i++)
      {
        Console.Write("{0,4} ", a[i]);
        if (i % 10 == 9)
          Console.WriteLine();
      }
      Console.ReadKey();
    }
  }
}
image articles/522/522_3.png
Однако чем ближе к концу массива, тем больше генераций необходимо производить для получения неповторяющегося значения.
Следующий пример отображает количество вызовов метода Next() для получения каждого элемента, а также общее количество сгенерированных случайных чисел для заполнения массива из 100 элементов неповторяющимися значениями.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;
namespace MyProgram
{
  class Program
  {
    static void Main(string[] args)
    {
      Random rnd = new Random();
      int[] a = new int[100]; // массив элементов
      int[] count = new int[100]; // массив количества генераций
      a[0] = rnd.Next(0, 101);
      int c = 0; // счетчик количества генераций
      count[0] = 1; // a[0] генерируется только 1 раз
      for (int i = 1; i < 100;)
      {
        int num = rnd.Next(0, 101);
        c++; // сгенерировали элемент еще один раз
        int j;
        for (j = 0; j < i; j++)
        {
          if (num == a[j])
            break;
        }
        if (j == i)
        {
          a[i] = num; i++;
          count[i] = c; c = 0; // сохраняем количество генераций
        }
      }
      // Вывод значений элементов
      Console.WriteLine("Значения элементов");
      for (int i = 0; i < 100; i++)
      {
        Console.Write("{0,4} ", a[i]);
        if (i % 10 == 9)
          Console.WriteLine();
      }
      Console.WriteLine();
      // Вывод количества генераций
      Console.WriteLine("Количество генераций элементов");
      int sum = 0;
      for (int i = 0; i < 100; i++)
      {
        sum += count[i];
        Console.Write("{0,4} ", count[i]);
        if (i % 10 == 9)
          Console.WriteLine();
      }
      Console.WriteLine("Общее количество генераций - {0}", sum);
      Console.ReadKey();
    }
  }
}
image articles/522/522_4.png
 

Перемешивание значений

Часто возникает задача расставить уже имеющийся набор значений в произвольном порядке. С этой целью также используется генератор случайных чисел. Для решения этой задачи создается массив и заполняется значениями, которые необходимо расставить в произвольном порядке.
После этого генерируется два значения индексов массива случайным образом, и значения элементов с полученными индексами меняются местами. Процедура повторяется не менее N/2 раз, где N - количество элементов массива.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
namespace MyProgram
{
  class Program
  {
    static void Main(string[] args)
    {
      Random rnd = new Random();
      int[] a = new int[100];
      for (int i = 0; i < 100; i++)
        a[i] = i;
      for (int i = 0; i < 50; i++)
      {
        int i1 = rnd.Next(0, 100); // первый индекс
        int i2 = rnd.Next(0, 100); // второй индекс
        // обмен значений элементов с индексами i1 и i2
        int temp = a[i1];
        a[i1] = a[i2];
        a[i2] = temp;
      }
      Console.WriteLine("Значения элементов");
      for (int i = 0; i < 100; i++)
      {
        Console.Write("{0,4} ", a[i]);
        if (i % 10 == 9)
          Console.WriteLine();
      }
      Console.ReadKey();
    }
  }
}
image articles/522/522_5.png
Перемешивание значений является более эффективным если диапазон значений совпадает с их количеством (или близок к нему), поскольку в этом случае значительно сокращается количество генераций случайных элементов.

Автор: Вставская Елена Владимировна

 
Написать комментарий:




Начинаем практику по языку C#





Если вы пришли без приглашения -
введите тысяча двадцать четыре (цифрами).
Чтобы стать хорошим программистом — нужно писать программы. На нашем сайте очень много практических упражнений.

После заполнения формы ты будешь подписан на рассылку «C# Вебинары и Видеоуроки», у тебя появится доступ к видеоурокам и консольным задачам.

Несколько раз в неделю тебе будут приходить письма — приглашения на вебинары, информация об акциях и скидках, полезная информация по C#.

Ты в любой момент сможешь отписаться от рассылки.