Как генерировать случайное число на языке Apple Swift?

356

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

Теги:
random

25 ответов

327
Лучший ответ

Используйте стандартные библиотечные функции для высококачественных случайных чисел: arc4random() или arc4random_uniform(), как и в Objective-C.

Они находятся в модуле Darwin, поэтому, если вы не импортировали AppKit, UIKit или Foundation (которые импортируют его для вас), вам нужно будет import Darwin.

Swift 4.2

Swift 4.2, поставляемый с Xcode 10, представляет новые простые в использовании случайные функции для многих типов данных. Вы можете вызвать метод random() для числовых типов.

let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
  • 7
    Я не должен был явно импортировать Дарвин
  • 3
    В моем файле Playground мне нужно было импортировать Darwin, потому что я больше ничего не импортировал.
Показать ещё 4 комментария
461

Используйте arc4random_uniform(n) для случайного целого числа от 0 до n-1.

let diceRoll = Int(arc4random_uniform(6) + 1)

Передайте результат Int, чтобы вам не приходилось явно вводить ваши vars как UInt32 (что кажется un-Swifty).

  • 6
    Очень просто. Мне это нравится. Upvote! Но у реальных кубиков нет 0 . В вашем коде diceRoll может быть 0 . Просто говорю...
  • 60
    Да, вы действительно хотите Int(arc4random_uniform(6)+1) .
Показать ещё 7 комментариев
113

Изменить: Обновлено для Swift 3.0

arc4random хорошо работает в Swift, но базовые функции ограничены 32-разрядными целыми типами (Int - это 64-разрядная версия на iPhone 5S и современные Mac). Здесь общая функция для случайного числа типа, выражаемого целочисленным литералом:

public func arc4random<T: ExpressibleByIntegerLiteral>(_ type: T.Type) -> T {
    var r: T = 0
    arc4random_buf(&r, MemoryLayout<T>.size)
    return r
}

Мы можем использовать эту новую общую функцию для расширения UInt64, добавления граничных аргументов и смягчения модульного смещения. (Это поднимается прямо из arc4random.c)

public extension UInt64 {
    public static func random(lower: UInt64 = min, upper: UInt64 = max) -> UInt64 {
        var m: UInt64
        let u = upper - lower
        var r = arc4random(UInt64.self)

        if u > UInt64(Int64.max) {
            m = 1 + ~u
        } else {
            m = ((max - (u * 2)) + 1) % u
        }

        while r < m {
            r = arc4random(UInt64.self)
        }

        return (r % u) + lower
    }
}

С этим мы можем расширить Int64 для тех же аргументов, касающихся переполнения:

public extension Int64 {
    public static func random(lower: Int64 = min, upper: Int64 = max) -> Int64 {
        let (s, overflow) = Int64.subtractWithOverflow(upper, lower)
        let u = overflow ? UInt64.max - UInt64(~s) : UInt64(s)
        let r = UInt64.random(upper: u)

        if r > UInt64(Int64.max)  {
            return Int64(r - (UInt64(~lower) + 1))
        } else {
            return Int64(r) + lower
        }
    }
}

Чтобы завершить семейство...

private let _wordSize = __WORDSIZE

public extension UInt32 {
    public static func random(lower: UInt32 = min, upper: UInt32 = max) -> UInt32 {
        return arc4random_uniform(upper - lower) + lower
    }
}

public extension Int32 {
    public static func random(lower: Int32 = min, upper: Int32 = max) -> Int32 {
        let r = arc4random_uniform(UInt32(Int64(upper) - Int64(lower)))
        return Int32(Int64(r) + Int64(lower))
    }
}

public extension UInt {
    public static func random(lower: UInt = min, upper: UInt = max) -> UInt {
        switch (_wordSize) {
            case 32: return UInt(UInt32.random(UInt32(lower), upper: UInt32(upper)))
            case 64: return UInt(UInt64.random(UInt64(lower), upper: UInt64(upper)))
            default: return lower
        }
    }
}

public extension Int {
    public static func random(lower: Int = min, upper: Int = max) -> Int {
        switch (_wordSize) {
            case 32: return Int(Int32.random(Int32(lower), upper: Int32(upper)))
            case 64: return Int(Int64.random(Int64(lower), upper: Int64(upper)))
            default: return lower
        }
    }
}

После всего этого мы можем, наконец, сделать что-то вроде этого:

let diceRoll = UInt64.random(lower: 1, upper: 7)
  • 0
    Он не компилируется: var r = arc4random(UInt64) . Пожалуйста, посоветуйте, что вы здесь имели в виду?
  • 0
    @Ossir компилируется нормально для меня ... это означает вызов функции arc4random (определенной в первом блоке кода) с аргументом UInt64 который является Type .
Показать ещё 7 комментариев
70

Этот метод генерирует случайное значение Int между заданным минимальным и максимальным

func randomInt(min: Int, max:Int) -> Int {
    return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
56

Я использовал этот код:

var k: Int = random() % 10;
  • 20
    сначала вы должны вызвать srandom (UInt32 (time (nil))), иначе он всегда будет возвращать одну и ту же последовательность чисел
  • 0
    первое значит в didFinishLaunchingWithOptions ?
Показать ещё 4 комментария
31

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

У вас есть четыре типа источников на выбор: общий случайный источник (без названия, вплоть до системы, чтобы выбрать, что он делает), линейный конгруэнтный, ARC4 и Mersenne Twister. Они могут генерировать случайные ints, floats и bools.

На простейшем уровне вы можете создать случайное число из встроенного в систему случайного источника:

GKRandomSource.sharedRandom().nextInt()

Это генерирует число от -2,147,483,648 до 2,147,483,647. Если вам нужно число от 0 до верхней границы (исключая), вы должны использовать это:

GKRandomSource.sharedRandom().nextIntWithUpperBound(6)

GameplayKit имеет встроенные конструкторы удобства для работы с игральными костями. Например, вы можете катить шестигранную матрицу следующим образом:

let d6 = GKRandomDistribution.d6()
d6.nextInt()

Кроме того, вы можете сформировать случайное распределение, используя такие вещи, как GKShuffledDistribution. Это требует немного больше объяснений, но если вам интересно, вы можете прочитать мой учебник по случайным номерам GameplayKit.

  • 1
    Спасибо за этот совет, это один из лучших ответов. Для использования этих функций необходимо добавить import GameplayKit . Swift 3 изменил синтаксис на GKRandomSource.sharedRandom().nextInt(upperBound: 6)
  • 6
    насколько тяжелым этот комплект для импорта? я не хочу раздувать мой код
23

Вы можете сделать это так же, как в C:

let randomNumber = arc4random()

randomNumber предполагается, что он имеет тип UInt32 (32-разрядное целое без знака)

  • 12
    Приложение: rand , arc4random , drand48 и друзья - все в модуле Darwin . Он уже импортирован для вас, если вы создаете приложение Cocoa, UIKit или Foundation, но вам нужно будет import Darwin на игровых площадках.
  • 5
    И не пытайтесь преобразовать результат arc4random () в Int - это будет хорошо работать на 64-битной платформе, но на 32-битной платформе, 32-битные подписи Ints, так что вы получите неожиданный отрицательный номера. Это уже сбило с толку нескольких человек, поэтому я решил упомянуть об этом здесь.
21

Использовать arc4random_uniform()

Применение:

arc4random_uniform(someNumber: UInt32) → UInt32

Это дает случайные целые числа в диапазоне от 0 до someNumber - 1.

Максимальное значение для UInt32 составляет 4 294 967 295 (т. UInt32 2^32 - 1).

Примеры:

  • Монетный флип

    let flip = arc4random_uniform(2) // 0 or 1
    
  • Бросок в кости

    let roll = arc4random_uniform(6) + 1 // 1...6
    
  • Случайный день в октябре

    let day = arc4random_uniform(31) + 1 // 1...31
    
  • Случайный год в 1990-х

    let year = 1990 + arc4random_uniform(10)
    

Общая форма:

let number = min + arc4random_uniform(max - min + 1)

где number, max и min - UInt32.

Как насчет...

arc4random()

Вы также можете получить случайное число, используя arc4random(), который создает UInt32 между 0 и 2 ^ 32-1. Таким образом, чтобы получить случайное число между 0 и x-1, вы можете разделить его на x и взять остаток. Или, другими словами, используйте Оператор Remainder (%):

let number = arc4random() % 5 // 0...4

Однако это вызывает небольшое смещение по модулю (см. Также здесь и здесь), поэтому рекомендуется использовать arc4random_uniform().

Преобразование в и из Int

Обычно было бы хорошо сделать что-то подобное, чтобы конвертировать назад и вперед между Int и UInt32:

let number: Int = 10
let random = Int(arc4random_uniform(UInt32(number)))

Проблема состоит в том, что Int имеет диапазон -2,147,483,648...2,147,483,647 на 32-битных системах и диапазон -9,223,372,036,854,775,808...9,223,372,036,854,775,807 на 64-битных системах. Сравните это с диапазоном UInt32 0...4,294,967,295. U UInt32 означает unsigned.

Рассмотрим следующие ошибки:

UInt32(-1) // negative numbers cause integer overflow error
UInt32(4294967296) // numbers greater than 4,294,967,295 cause integer overflow error

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

17

Пример случайного числа между 10 (0-9);

import UIKit

let randomNumber = Int(arc4random_uniform(10))

Очень простой код - простой и короткий.

16

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

public extension ExpressibleByIntegerLiteral {
    public static func arc4random() -> Self {
        var r: Self = 0
        arc4random_buf(&r, MemoryLayout<Self>.size)
        return r
    }
}

Теперь мы можем сделать:

let i = Int.arc4random()
let j = UInt32.arc4random()

и все остальные целочисленные классы в порядке.

16

Я смог использовать rand() для получения случайного CInt. Вы можете сделать это Int, используя что-то вроде этого:

let myVar: Int = Int(rand())

Вы можете использовать свою любимую случайную функцию C и просто конвертировать в значение в Int, если необходимо.

  • 0
    Да, в противном случае преобразование типов может быть непростым делом, и позволить конструктору Int справиться с ним - это реально спасает от боли.
  • 4
    Обратите внимание, что если вы не вызываете srand (...) (вызываете его только один раз) перед использованием rand (), последовательность чисел всегда будет одинаковой при каждом выполнении вашей программы. Если вы не хотите этого, используйте arc4random ()
Показать ещё 2 комментария
10

В Swift 4.2 вы можете генерировать случайные числа, вызывая метод random() для любого нужного числового типа, предоставляя диапазон, с которым вы хотите работать. Например, это порождает случайное число в диапазоне от 1 до 9, включительно с обеих сторон

let randInt = Int.random(in: 1..<10)

Также с другими типами

let randFloat = Float.random(in: 1..<20)
let randDouble = Double.random(in: 1...30)
let randCGFloat = CGFloat.random(in: 1...40)
  • 0
    Очень круто, но Swift 4.2 еще не вышел.
  • 0
    проверьте в Xcode 10, вам может потребоваться обновление
Показать ещё 1 комментарий
9

Вот библиотека, которая хорошо работает https://github.com/thellimist/SwiftRandom

public extension Int {
    /// SwiftRandom extension
    public static func random(lower: Int = 0, _ upper: Int = 100) -> Int {
        return lower + Int(arc4random_uniform(UInt32(upper - lower + 1)))
    }
}

public extension Double {
    /// SwiftRandom extension
    public static func random(lower: Double = 0, _ upper: Double = 100) -> Double {
        return (Double(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
    }
}

public extension Float {
    /// SwiftRandom extension
    public static func random(lower: Float = 0, _ upper: Float = 100) -> Float {
        return (Float(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
    }
}

public extension CGFloat {
    /// SwiftRandom extension
    public static func random(lower: CGFloat = 0, _ upper: CGFloat = 1) -> CGFloat {
        return CGFloat(Float(arc4random()) / Float(UINT32_MAX)) * (upper - lower) + lower
    }
}
8
 let MAX : UInt32 = 9
 let MIN : UInt32 = 1

    func randomNumber()
{
    var random_number = Int(arc4random_uniform(MAX) + MIN)
    print ("random = ", random_number);
}
6

Я хотел бы добавить к существующим ответам, что пример генератора случайных чисел в книге Swift представляет собой генератор линейных конгруэнций (LCG), он сильно ограничен и не должен быть исключающим тривиальные примеры, где качество случайности вообще не имеет значения. И LCG никогда не должен использоваться для криптографических целей.

arc4random() намного лучше и может использоваться для большинства целей, но снова не следует использовать для криптографических целей.

Если вы хотите что-то, что гарантировано для криптографической защиты, используйте SecCopyRandomBytes(). Обратите внимание: если вы создадите генератор случайных чисел во что-то, кто-то другой может оказаться (ошибочно), используя его для криптографических целей (например, создание пароля, генерации ключей или солей), тогда вам следует использовать SecCopyRandomBytes() в любом случае, даже если ваш необходимость не требует этого.

5

Без arc4Random_uniform() в некоторых версиях Xcode (в 7.1 он запускается, но не автозаполняется для меня). Вы можете сделать это вместо этого.

Чтобы создать случайное число из 0-5. Первая

import GameplayKit

Тогда

let diceRoll = GKRandomSource.sharedRandom().nextIntWithUpperBound(6)
5
var randomNumber = Int(arc4random_uniform(UInt32(**5**)))

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

  • 1
    Если вы пройдете 5, он вернет 5 возможных результатов от нуля до четырех. 0 ... 4
4

Начиная с Swift 4.2

Существует новый набор API:

let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomDouble = Double.random(in: 1 ... 10)
  • Все числовые типы теперь имеют random(in:) метод, который принимает range.

  • Он возвращает число, равномерно распределенное в этом диапазоне.


TL; DR

Что ж, что случилось с "хорошим" старым способом?

  1. Вы должны использовать импортированные C API (они разные между платформами).

  2. И более того...

Что, если я скажу, что случайность не такая случайная?

Если вы используете arc4random() (для вычисления остатка), как arc4random() % aNumber, результат не равномерно распределены между 0 и aNumber. Существует проблема, называемая смещением Модуло.

Modulo bias

Обычно функция генерирует случайное число от 0 до MAX (зависит от типа и т.д.). Чтобы сделать быстрый, простой пример, скажем, максимальное число равно 7 и вы заботитесь о случайном числе в диапазоне 0..< 2 (или интервал [0, 3), если вы предпочитаете это).

Вероятности для отдельных чисел:

  • 0: 3/8 = 37,5%
  • 1: 3/8 = 37,5%
  • 2: 2/8 = 25%

Другими словами, вы, скорее всего, получите 0 или 1, чем 2. Конечно, не забывайте, что это чрезвычайно упрощено, а число MAX намного выше, что делает его более "справедливым".

Эта проблема решается SE-0202 - Случайная унификация в Swift 4.2

2

Swift 4.2

Swift 4.2 включил в стандартную библиотеку собственный и довольно полнофункциональный API случайных чисел. (Предложение Swift Evolution SE-0202)

let intBetween0to9 = Int.random(in: 0...9) 
let doubleBetween0to1 = Double.random(in: 0...1)

Все типы номеров имеют статическую случайную (в :), которая принимает диапазон и возвращает случайное число в заданном диапазоне

2

В следующем коде будет создано безопасное случайное число от 0 до 255:

extension UInt8 {
  public static var random: UInt8 {
    var number: UInt8 = 0
    _ = SecRandomCopyBytes(kSecRandomDefault, 1, &number)
    return number
  }
}

Вы называете это следующим образом:

print(UInt8.random)

Для больших чисел это становится более сложным.
Это лучшее, что я мог придумать:

extension UInt16 {
  public static var random: UInt16 {
    let count = Int(UInt8.random % 2) + 1
    var numbers = [UInt8](repeating: 0, count: 2)
    _ = SecRandomCopyBytes(kSecRandomDefault, count, &numbers)
    return numbers.reversed().reduce(0) { $0 << 8 + UInt16($1) }
  }
}

extension UInt32 {
  public static var random: UInt32 {
    let count = Int(UInt8.random % 4) + 1
    var numbers = [UInt8](repeating: 0, count: 4)
    _ = SecRandomCopyBytes(kSecRandomDefault, count, &numbers)
    return numbers.reversed().reduce(0) { $0 << 8 + UInt32($1) }
  }
}

В этих методах используется дополнительное случайное число, чтобы определить, сколько UInt8 будут использоваться для создания случайного числа. Последняя строка преобразует [UInt8] в UInt16 или UInt32.

Я не знаю, считаются ли последние два по-настоящему случайными, но вы можете настроить их по своему вкусу:)

  • 0
    Вы ловко избежали предвзятости, введенной по модулю +1 для этого. Вы можете предупредить читателей, почему вы это сделали.
  • 0
    Это интересно, я не особо задумывался о том, что здесь может быть смещение по модулю. Возможно, шансы получить небольшое число не совпадают с шансом получить большое количество.
1

Подробнее

xCode 9.1, Swift 4

Математическое ориентированное решение (1)

import Foundation

class Random {

    subscript<T>(_ min: T, _ max: T) -> T where T : BinaryInteger {
        get {
            return rand(min-1, max+1)
        }
    }
}

let rand = Random()

func rand<T>(_ min: T, _ max: T) -> T where T : BinaryInteger {
    let _min = min + 1
    let difference = max - _min
    return T(arc4random_uniform(UInt32(difference))) + _min
}

Использование решения (1)

let x = rand(-5, 5)       // x = [-4, -3, -2, -1, 0, 1, 2, 3, 4]
let x = rand[0, 10]       // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Программируемое решение (2)

Не забудьте добавить Math-ориентированное решение (1) код здесь

import Foundation

extension CountableRange where Bound : BinaryInteger {

    var random: Bound {
        return rand(lowerBound-1, upperBound)
    }
}

extension CountableClosedRange where Bound : BinaryInteger {

    var random: Bound {
        return rand[lowerBound, upperBound]
    }
}

Использование решения (2)

let x = (-8..<2).random           // x = [-8, -7, -6, -5, -4, -3, -2, -1, 0, 1]
let x = (0..<10).random           // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let x = (-10 ... -2).random       // x = [-10, -9, -8, -7, -6, -5, -4, -3, -2]

Полный образец

Не забудьте добавить решение (1) и решение (2) коды здесь

private func generateRandNums(closure:()->(Int)) {

    var allNums = Set<Int>()
    for _ in 0..<100 {
        allNums.insert(closure())
    }
    print(allNums.sorted{ $0 < $1 })
}

generateRandNums {
    (-8..<2).random
}

generateRandNums {
    (0..<10).random
}

generateRandNums {
    (-10 ... -2).random
}

generateRandNums {
    rand(-5, 5)
}
generateRandNums {
    rand[0, 10]
}

Пример результата

Изображение 1637

  • 1
    Этот ответ не по теме. Вопрос был в том, как сгенерировать случайное число. Не как сделать библиотеку случайных чисел. Sheesh.
0

Swift 4.2

До свидания, чтобы импортировать Foundation C lib arc4random_uniform()

// 1  
let digit = Int.random(in: 0..<10)

// 2
if let anotherDigit = (0..<10).randomElement() {
  print(anotherDigit)
} else {
  print("Empty range.")
}

// 3
let double = Double.random(in: 0..<1)
let float = Float.random(in: 0..<1)
let cgFloat = CGFloat.random(in: 0..<1)
let bool = Bool.random()
  1. Вы произвольно используете (в :) для генерации случайных цифр из диапазонов.
  2. randomElement() возвращает nil, если диапазон пуст, поэтому вы разворачиваете возвращенный Int? с если пусть.
  3. Вы используете случайные (в :) для генерации случайных двойных, плавающих или CGFloat и random() для возврата случайного Bool.

Подробнее @Официальный

0

Для iOS, macOS и tvOS вы можете использовать общесистемный случайный источник в Xcode framework GameKit. Здесь вы можете найти класс GKRandomSource с помощью GKRandomSource класса sharedRandom():

import GameKit

let number: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

func randomGenerator() -> Int {
    let random = GKRandomSource.sharedRandom().nextInt(upperBound: number.count)
    return number[random]
}

Это написано в Swift 4.2.

0

Я использую этот код для генерации случайного числа:

//
//  FactModel.swift
//  Collection
//
//  Created by Ahmadreza Shamimi on 6/11/16.
//  Copyright  2016 Ahmadreza Shamimi. All rights reserved.
//

import GameKit

struct FactModel {

    let fun  = ["I love swift","My name is Ahmadreza","I love coding" ,"I love PHP","My name is ALireza","I love Coding too"]


    func getRandomNumber() -> String {

        let randomNumber  = GKRandomSource.sharedRandom().nextIntWithUpperBound(fun.count)

        return fun[randomNumber]
    }
}
  • 2
    Добро пожаловать в ТАК. Не рекомендуется отвечать только на коды. Пожалуйста, отредактируйте свой ответ, чтобы объяснить, почему этот код отвечает на вопрос и как он работает. См. Stackoverflow.com/help/how-to-answer для получения дополнительной информации.
  • 2
    Пожалуйста, предоставьте некоторый контекст вокруг вашего ответа и добро пожаловать в stackoverflow. :)
0

Вы можете использовать GeneratorOf следующим образом:

var fibs = ArraySlice([1, 1])
var fibGenerator = GeneratorOf{
    _ -> Int? in
    fibs.append(fibs.reduce(0, combine:+))
    return fibs.removeAtIndex(0)
}

println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
println(fibGenerator.next())
  • 3
    Как Фибоначчи случайно?
  • 0
    Привет Николай, Этот блок кода является старой версией Swift 1.2. Если вы попробуете новый Swift 2.0. Это не было бы работой.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню