В данной статье мы рассмотрим ответ на вопрос: как сформировать массив случайных чисел в программе на языке Zig? Этот вопрос последнее время часто мелькает на разных ресурсах в сети интернет, хотя, казалось бы, это простая задача.

В стандартной библиотеке Zig есть пространство имён для генерации случайных чисел std.Random. В нем есть различные типы генераторов псевдослучайных чисел. В данной статье мы рассмотрим генератор типа std.Random.DefaultPrng.

Этот генератор требует инициализации, как и любой другой из стандартной библиотеки Zig. Инициализация происходит, как не трудно догадаться, с помощью функции init.

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

var rand = std.Random.DefaultPrng.init(0);
std.debug.print("Случайное число: {}\n", .{rand.random().int(u8)});

Вы получите вывод:

Случайное число: 223

Понятно, что если сразу же еще раз вызвать rand.random().int(u8), вы получите следующее число из псевдослучайной последовательности. Но между запусками эти последовательности будут одинаковыми, поскольку точка отсчета для алгоритма остается неизменной константой.

Вывод: нужно внести энтропию (неопределенность) в инициализацию генератора. Самое простое решение — использовать текущее время в миллисекундах.

var rand = std.Random.DefaultPrng.init(@intCast(std.Io.Timestamp.now(init.io, .awake).toMilliseconds()));
std.debug.print("Случайное число: {}\n", .{rand.random().int(u8)});

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

var rand = std.Random.DefaultPrng.init(blk: {
    var seed: [8]u8 = undefined;
    _ = std.os.linux.getrandom(&seed, 8, 0);
    break :blk std.mem.bytesToValue(u64, &seed);
});
std.debug.print("Случайное число: {}\n", .{rand.random().int(u8)});

Как результат, разброс полученных чисел будет больше запуск от запуска, чем в случае с использованием текущего времени.

Первый вариант

Теперь вернемся к основной теме — формированию массива случайных чисел. В данном случае мы будем формировать массив из 10 случайных чисел типа u8. Например, код может быть таким:

const std = @import("std");

pub fn main() !void {
    var prng: std.Random.DefaultPrng = .init(blk: {
        var seed: [8]u8 = undefined;
        _ = std.os.linux.getrandom(&seed, 8, 0);
        break :blk std.mem.bytesToValue(u64, &seed);
    });
    const rand = prng.random();

    var randList: [10]u8 = undefined;
    for (0..randList.len) |i| {
        // Генерация случайного числа типа u8
        randList[i] = rand.int(u8);
    }

    std.debug.print("Случайные числа: {any}\n", .{randList});
}

Второй вариант

Второй вариант формирования массива случайных чисел — использование криптографически безопасного генератора, который доступен в стандартной библиотеке Zig с версии 0.16.0. В данном случае мы будем формировать массив из 10 случайных чисел типа u8. Например, код может быть таким:

const std = @import("std");

pub fn main(init: std.process.Init) !void {
    const io = init.io;

    var randList: [10]u16 = undefined;
    for (&randList) |*randItem| {
        std.Io.random(io, std.mem.asBytes(randItem));
    }

    std.debug.print("Случайные числа: {any}\n", .{randList});
}

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

Обсудить можно в наших чатах, адреса которых вы можете найти в разделе «Контакты».