Avatar ·

Как сгенерировать случайное число в Swift? (Int, Double, Float, Bool)

Всем привет! Я начинающий iOS-разработчик, пишу своё первое приложение на Swift. Столкнулся с проблемой: мне нужно сгенерировать случайное число, но я не понимаю, как это правильно сделать в современном Swift. В старых учебниках я видел использование `arc4random()`, `arc4random_uniform()` и даже `rand()`, но Xcode ругается на эти функции, говорит, что они нерекомендуемые или вообще не работают в некоторых контекстах. Например, я пытался сделать так: ```swift let randomNumber = arc4random_uniform(10) print(randomNumber) ``` Это компилируется, но возвращает `UInt32`, а мне нужно `Int`. Когда я пытаюсь использовать это в вычислениях, приходится делать приведение типов, что неудобно. А если нужно сгенерировать случайное число с плавающей точкой, например Double от 0.0 до 1.0, я вообще не знаю, как подступиться. Подскажите, пожалуйста, как правильно генерировать случайные числа в Swift? Хотелось бы увидеть примеры для разных типов: Int, Double, Float, Bool. И чтобы код был современным, без устаревших функций. Заранее спасибо! P.S. Пишу на Swift 5, Xcode 15. <<>>

Современный способ генерации случайных чисел в Swift (Swift 4.2+)

Начиная с Swift 4.2 (вышла в 2018 году), в стандартную библиотеку была добавлена встроенная поддержка генерации случайных чисел. Вам больше не нужно использовать arc4random(), drand48() или другие C-функции. Всё делается через статические методы на самих типах.

1. Случайное целое число (Int)

Для генерации случайного Int в заданном диапазоне используйте метод random(in:):

// Случайное число от 1 до 10 включительно
let randomInt = Int.random(in: 1...10)
print(randomInt) // Например: 7

// Случайное число от 0 до 100 (не включая 100)
let randomIntHalfOpen = Int.random(in: 0..<100)
print(randomIntHalfOpen) // Например: 42

2. Случайное число с плавающей точкой (Double, Float)

Работает аналогично, но диапазон может быть дробным:

// Случайный Double от 0.0 до 1.0
let randomDouble = Double.random(in: 0...1)
print(randomDouble) // Например: 0.54321

// Случайный Float от -10.0 до 10.0
let randomFloat = Float.random(in: -10.0...10.0)
print(randomFloat) // Например: 3.14159

// Случайный CGFloat (часто используется в UIKit)
import CoreGraphics
let randomCGFloat = CGFloat.random(in: 0...1)

3. Случайное булево значение (Bool)

Для Bool есть специальный метод random():

let randomBool = Bool.random()
print(randomBool) // true или false с вероятностью 50%

4. Случайный элемент из коллекции

Это очень удобно для массивов и других коллекций:

let colors = ["red", "green", "blue", "yellow"]
if let randomColor = colors.randomElement() {
    print(randomColor) // Например: "green"
}

// Для массивов с опциональными элементами
let numbers = [1, 2, 3, 4, 5]
let randomNumber = numbers.randomElement()! // Опционально, но если массив не пуст — безопасно

5. Перемешивание массива (shuffle)

Если нужно случайно перемешать элементы:

var deck = [1, 2, 3, 4, 5, 6]
deck.shuffle()
print(deck) // Например: [3, 6, 1, 5, 2, 4]

// Или получить новую перемешанную копию:
let shuffledDeck = deck.shuffled()

Важные замечания

  • Безопасность типов: random(in:) возвращает значение того же типа, к которому вы его применяете. Никаких приведений UInt32 к Int.
  • Криптостойкость: Стандартный генератор SystemRandomNumberGenerator (используется по умолчанию) является криптографически безопасным. Для обычных задач этого более чем достаточно.
  • Воспроизводимость: Если вам нужны повторяемые последовательности (например, для тестов), можно создать свой генератор, реализующий протокол RandomNumberGenerator.

Пример: создание генератора для тестов

struct SeededGenerator: RandomNumberGenerator {
    var state: UInt64
    
    mutating func next() -> UInt64 {
        state = state &* 6364136223846793005 &+ 1442695040888963407
        return state
    }
}

var generator = SeededGenerator(state: 42)
let testInt = Int.random(in: 1...100, using: &generator)
let testDouble = Double.random(in: 0...1, using: &generator)

Таким образом, забудьте про arc4random — используйте .random(in:) и .randomElement(). Это современно, безопасно и удобно.

<<>>

Альтернативный подход: использование GameplayKit для более сложной рандомизации

Если вам нужно больше контроля над распределением случайных чисел (например, нормальное распределение, гауссово шумоподавление или генерация случайных чисел с определённой вероятностью), обратите внимание на фреймворк GameplayKit (импортируется как import GameplayKit).

Пример с равномерным распределением:

import GameplayKit

let randomSource = GKRandomSource()
let randomInt = randomSource.nextInt(upperBound: 10) // 0..<10
let randomIntInRange = randomSource.nextInt(in: 1...10)

Для гауссова распределения:

let gaussianSource = GKGaussianDistribution(randomSource: GKRandomSource(), mean: 0.0, deviation: 1.0)
let sample = gaussianSource.nextUniform() // -1...1, с пиком около 0

Также можно создать детерминированный источник для повторяемых последовательностей:

let fixedSeed = GKLinearCongruentialRandomSource(seed: 12345)
let value1 = fixedSeed.nextInt(upperBound: 100) // Всегда будет одинаково при старте с seed 12345

Когда это полезно? В играх, симуляциях, генерации уровней или тестовых данных, где нужно предсказуемое "случайное" поведение. Для 90% задач стандартного .random(in:) более чем достаточно.