Генерация случайных чисел в Swift: от random() до GameplayKit

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Swift и случайные числа: магия, которая работает предсказуемо

Каждый разработчик рано или поздно сталкивается с задачей: «Как сгенерировать случайное число?». Казалось бы, что может быть проще? Но в мире Swift эта тема прошла долгий путь эволюции — от древних C-функций до элегантного встроенного API. В этой статье мы разберем все способы генерации случайных чисел в Swift, от простых до промышленных, с живыми примерами и важными нюансами.

Если вы когда-нибудь задавались вопросом, почему ваш arc4random() возвращает странные числа или как сделать настоящий случайный выбор из массива — вы попали по адресу. Поехали!

Краткая история: как мы дошли до жизни такой

До Swift 4.2 (2018 год) разработчики мучительно выбирали между arc4random(), drand48() и костылями вроде rand() % 100. Последний, кстати, был опасен — смещение (bias) в распределении гарантировано, если диапазон не делится нацело на RAND_MAX. С выходом Swift 4.2 Apple подарила нам RandomNumberGenerator протокол и встроенные методы. Теперь генерация случайных чисел стала безопасной, читаемой и единообразной.

Современный способ: RandomNumberGenerator и методы типов

Начиная с Swift 4.2, самый правильный и простой способ — использовать статические методы на типах:

// Случайное целое число от 1 до 100
let randomInt = Int.random(in: 1...100)

// Случайное число с плавающей точкой от 0.0 до 1.0
let randomDouble = Double.random(in: 0.0...1.0)

// Случайное число Float
let randomFloat = Float.random(in: 0.0...1.0)

// Случайный Bool
let randomBool = Bool.random()

Этот API использует системный генератор SystemRandomNumberGenerator, который является криптостойким (но об этом чуть позже). Важно: диапазон задается через closed range (...) или half-open range (..<).

Почему это круто?

  • Безопасность типов — компилятор не даст вам сгенерировать Double и присвоить Int.
  • Отсутствие смещения — Swift сам заботится о равномерности распределения.
  • Универсальность — работает для всех числовых типов: Int, UInt, Int8, Float, Double, CGFloat и даже Bool.

Генерация случайного числа из диапазона: подводные камни

Казалось бы, что может пойти не так? Рассмотрим пример:

let diceRoll = Int.random(in: 1...6)  // Кубик: 1,2,3,4,5,6
let coinFlip = Int.random(in: 0..<2)  // Монетка: 0 или 1

Все корректно. Но если вы хотите сгенерировать случайное число без верхней границы (например, от 0 до бесконечности) — так не получится. Swift требует явного указания диапазона. Это сделано специально, чтобы избежать ошибок переполнения.

Если вам нужно случайное число от 0 до max (где max — переменная), просто используйте:

let maxValue = 500
let randomUpTo = Int.random(in: 0...maxValue)

Случайный элемент из коллекции (Array, Set, Dictionary)

Генерация случайного числа часто нужна не сама по себе, а для выбора случайного элемента. Swift предоставляет элегантное решение:

let fruits = ["яблоко", "банан", "вишня", "дыня"]
if let randomFruit = fruits.randomElement() {
    print("Сегодня едим \(randomFruit)")
} else {
    print("Корзина пуста!")
}

Метод randomElement() возвращает опциональное значение — это важно, так как коллекция может быть пустой. Для перемешивания всего массива используйте shuffled():

let shuffledFruits = fruits.shuffled()
print(shuffledFruits) // например: ["банан", "дыня", "яблоко", "вишня"]

Криптостойкая генерация: когда безопасность превыше всего

Стандартный Int.random(in:) использует SystemRandomNumberGenerator, который является криптостойким. Это значит, что он подходит для генерации токенов, паролей и ключей шифрования. Но если вам нужно явно указать генератор (например, для тестирования), можно создать свой:

// Свой генератор (НЕ криптостойкий — только для тестов!)
struct LinearCongruentialGenerator: RandomNumberGenerator {
    var state: UInt64 = 1

    mutating func next() -> UInt64 {
        state = state &* 6364136223846793005 &+ 1442695040888963407
        return state
    }
}

var generator = LinearCongruentialGenerator()
let testRandom = Int.random(in: 1...100, using: &generator)

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

Старый способ: arc4random и его друзья (для совместимости)

Если вы поддерживаете старый код или работаете с проектом на Swift 3/4.0, вам может встретиться arc4random. Вот как это выглядело раньше:

// Случайное число от 0 до 99 (старый способ)
let oldRandom = Int(arc4random_uniform(100))

// Случайное число от 10 до 99
let oldRangeRandom = Int(arc4random_uniform(90)) + 10

Недостатки:

  • Работает только с UInt32 — нужно постоянно приводить типы.
  • Неудобно для диапазонов (нужно вычислять смещение).
  • Не работает для Float/Double напрямую.

Используйте этот метод только если вы модернизируете legacy-код. Для нового кода — только Int.random(in:).

Случайные числа в SwiftUI и iOS: практический пример

Давайте соберем все вместе и напишем небольшое приложение — генератор случайных паролей на SwiftUI:

import SwiftUI

struct PasswordGeneratorView: View {
    @State private var passwordLength: Double = 12
    @State private var password: String = ""

    var body: some View {
        VStack(spacing: 20) {
            Text("Генератор паролей")
                .font(.title)

            Slider(value: $passwordLength, in: 6...32, step: 1)
            Text("Длина: \(Int(passwordLength))")

            Button("Сгенерировать") {
                password = generatePassword(length: Int(passwordLength))
            }
            .buttonStyle(.borderedProminent)

            Text(password)
                .font(.system(.body, design: .monospaced))
                .padding()
                .background(Color.gray.opacity(0.2))
                .cornerRadius(8)
        }
        .padding()                

Блоги

Рекомендаций по книгам