Глава 2. Классы Math и Random


Математические вычисления и случайные числа

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

Математические функции класса Math

Класс Math содержит готовые методы для математических вычислений. Имя класса и имя метода разделяются точкой, а значения для вычисления записываются в круглых скобках.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
double result = Math.Pow(2, 3); // Возведение в степень: результат 8
Console.WriteLine(result);

result = Math.Sqrt(16); // Квадратный корень: результат 4
Console.WriteLine(result);

result = Math.Abs(-10); // Модуль числа: результат 10
Console.WriteLine(result);

result = Math.Round(3.565, 2); // Округление до двух знаков после запятой
Console.WriteLine(result);

result = Math.Floor(3.9); // Округление вниз: результат 3
Console.WriteLine(result);

result = Math.Ceiling(3.9); // Округление вверх: результат 4
Console.WriteLine(result);

Запятая между аргументами отделяет одно значение от другого: в Math.Pow(2, 3) число 2 — основание, а 3 — показатель степени. Десятичные дроби в коде записываются с точкой, например 3.9.

Math.Floor округляет в сторону меньшего числа. Это не то же самое, что отбросить дробную часть: для отрицательных чисел результаты различаются.

static void Main(string[] args)
{
    double r = -1.2;
    Console.WriteLine($"После приведения к int: {(int)r}"); // -1
    Console.WriteLine($"После Math.Floor: {Math.Floor(r)}"); // -2
}

Math.Min(a, b) возвращает меньшее из двух значений, а Math.Max(a, b) — большее. Эти методы пригодятся в упражнениях.

Случайные числа: класс Random

Для получения случайных чисел создадим объект Random. В примерах поле rnd находится внутри класса Program, но за пределами метода Main. Создайте его один раз и используйте для следующих розыгрышей.

Полная программа, которая получает случайное целое число от 1 до 10:

1
2
3
4
5
6
7
8
9
10
11
12
using System;

class Program
{
    public static Random rnd = new Random();

    public static void Main()
    {
        int number = rnd.Next(1, 11); // Возможны числа от 1 до 10
        Console.WriteLine(number);
    }
}

У rnd.Next(min, max) нижняя граница входит в диапазон, а верхняя не входит. Поэтому для чисел от 1 до 10 нужны аргументы 1 и 11.

Для случайного вещественного числа от 0 включительно до 1 не включительно замените содержимое Main:

1
2
double number = rnd.NextDouble();
Console.WriteLine(number);

Пример: бросок игрального кубика

У кубика шесть граней, поэтому возможны результаты от 1 до 6. Используем уже созданное поле rnd:

1
2
3
4
5
public static void Main()
{
    int dice = rnd.Next(1, 7); // От 1 до 6 включительно
    Console.WriteLine("Результат броска: " + dice);
}

Запустите программу несколько раз. Числа могут повторяться: случайный выбор не означает, что каждый следующий результат обязан отличаться от предыдущего.

Таблица трассировки случайного выбора

Предположим, при одном запуске выпало число 4. Тогда таблица трассировки выглядит так:

Выполненное действие dice Вывод
Объявление переменной и случайный выбор 4  
Вывод на экран 4 Результат броска: 4

При другом запуске таблица может содержать другое число. Для трассировки сначала зафиксируйте, какое значение вернул случайный выбор.

Зачем создавать один объект Random?

Один объект Random может последовательно выдавать много чисел. Не нужно создавать новый объект перед каждым вызовом Next.

На будущее: повторное создание Random в старом .NET Framework

Этот пример использует массивы, циклы и методы. Вернитесь к нему после знакомства с этими темами. Для текущих упражнений достаточно создавать одно поле static Random rnd = new Random(); и использовать его повторно.

Объект Random выдаёт последовательность псевдослучайных чисел. Последовательность зависит от начального значения — seed. В старом .NET Framework несколько объектов, созданных почти одновременно, могли получать одинаковые начальные значения и выдавать одинаковые последовательности. Поведение зависит от версии .NET: в современной среде пример ниже может не воспроизвести проблему.

Представьте автомат, который выдаёт числа. Чтобы получить тысячу чисел, достаточно одного автомата. Если вместо этого создать тысячу одинаково настроенных автоматов и взять у каждого только первое число, результаты могут совпасть.

Следующий код заполняет массив из 1000 целых чисел нулями и единицами, затем подсчитывает единицы. При независимых равновероятных выборах ожидаем число, близкое к 500, хотя точного совпадения не требуется. В старом .NET Framework 4.7.2 повторное создание Random могло приводить к сильно смещённому результату, вплоть до 0 или 1000.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static int[] arr = new int[1000];
static int current = 0;
static void Main(string[] args)
{
    for (int i = 0; i < 1000; i++)
        Fill(); // Заполняем массив

    int count = 0;
    for (int i = 0; i < 1000; i++)
    {
        if (arr[i] > 0) // Считаем единицы
            count++;
    }
    // В старой среде результат может оказаться равным 0 или 1000
    Console.WriteLine(count);
}
public static void Fill()
{
    // Неудачное место для создания генератора: при каждом вызове
    Random rnd = new Random();
    arr[current] = rnd.Next(0, 2); // Записываем 0 или 1
    current = current + 1;
}

Исправление: перенесите Random rnd = new Random(); из Fill в поле класса и добавьте static. Тогда все вызовы Fill будут использовать один генератор.

Отладка с помощью точек останова

Отладка помогает понять, как выполняется программа, и найти ошибку. Вы можете печатать промежуточные значения или приостановить программу на точке останова (breakpoint) и проверить переменные.

Попробуйте на программе с кубиком:

  1. В Visual Studio нажмите в левом поле рядом со строкой int dice = rnd.Next(1, 7);. Появится красная точка.
  2. Запустите отладку клавишей F5. Программа остановится до выполнения отмеченной строки.
  3. Нажмите F10, чтобы выполнить строку. Наведите курсор на dice и посмотрите его значение.
  4. Нажмите F10 ещё раз и сравните вывод с найденным значением.
  5. Нажмите F5, чтобы продолжить выполнение. Точку останова можно убрать повторным нажатием на красную точку.
Дополнительно: видео об отладке на иврите

Необязательное видео на иврите показывает работу с точками останова:

Дополнительно: видео о Math на иврите

Подборка видео о классе Math на иврите

Следующие шаги

Практика 2.1: Math и Random — задания и решения

Следующая глава: условия и логические выражения → · Содержание курса