Como se faz um número aleatório entre o intervalo para arc4random_uniform ()?


129

portanto, meu objetivo neste código-bit é rolar aleatoriamente dois dados e, como todos sabemos, seu dado regular possui apenas 6 lados, então importei o Foundation para acessar arc4random_uniform (UInt32). Tentei usar o intervalo de (1..7) para evitar obter aleatoriamente 0, no entanto, retornou um erro do qual não gostei muito. Eu tentei fazer isso:

dice1 = arc4random_uniform(UInt32(1..7))

no entanto que retornou

Não foi possível encontrar uma sobrecarga para 'init' que aceita os argumentos fornecidos

Espero que essa informação seja suficiente para as suas dívidas incríveis por aí para me ajudar :)

Por favor, note que estou apenas fazendo isso em um playground para praticar rapidamente. Não é imperativo que eu aprenda como fazer isso; sou apenas eu mexendo antes de começar a criar aplicativos reais: D

//imports random number function
import Foundation
//creates data storage for dice roll
var dice1: UInt32 = 0
var dice2: UInt32 = 0
//counter variable
var i = 0
//how many times snake eyes happens
var snakeeyes = 0
 //how many times a double is rolled
var `double` = 0
//rolls dice 100 times
while i < 100{
    //from here
    //sets dice roll

Isso retorna um erro de 'Range $ T3' não é conversível em UInt32

   dice1 = arc4random_uniform(1..7)
   dice2 = arc4random_uniform(1..7)
    //checks for snake eyes
    if dice1 == 1 && dice2 == 1 {
        snakeeyes = snakeeyes + 1

    }
    //checks for doubles
    if dice1 == dice2{
        `double` = `double` + 1
    }
    //increases counter
        i = i + 1
    //to here
}
println("You got Snake Eyes \(snakeeyes) times.")
println("You got Doubles, \(`double`) times.")

4
Eu acredito que você deve fazer dice1 = arc4random_uniform(6) + 1para obter o intervalo de 1 a 6. No entanto, eu não faço o objetivo C do iOS, nem tenho conhecimento de linguagem rápida. O método aleatório deve retornar de 0 a 5 e + 1 será de 1 a 6. #
626 Sky

1
Range é um dado de objeto em si, não é um número inteiro; é por isso que você está recebendo o erro quando o argumento apenas recebe (UInt32) - #u_int32_t arc4random_uniform(u_int32_t upper_bound);
Sky

aha! obrigado céu! fiz uma afirmação para testar se estava indo menos que 0 e posso confirmar que era exatamente o que eu precisava colocar como resposta para que eu possa verificá-la como tal!
arcreigh

probabilidade = Int (arc4random_uniform (UInt32 (total))) - se você tiver várias queixas de fundição que são inespecíficos (porque o typeahead / cabeçalhos não são funcionais)
bshirley

Este é construído em começar com Swift 4.2 como apontado abaixo stackoverflow.com/a/50696901/1148030
Peter Lamberg

Respostas:


260

Eu acredito que você deveria fazer

dice1 = arc4random_uniform(6) + 1;

para obter o intervalo de 1 a 6. Não faço o objetivo C do iOS, nem tenho conhecimento de linguagem rápida. O método aleatório deve retornar um valor entre 0 e 5, e + 1 o tornará um valor entre 1 e 6.

Se você precisar de um intervalo entre, digamos 10 - 30, basta fazer

int random = arc4random_uniform(21) + 10;

2
@JoeSmith, você está exatamente certo nisso, deve ser arc4random_uniform (21) +10 para retornar um intervalo entre 10 e 30, pois o limite superior não é inclusivo. A parte "arc4random_uniform (20) +10" se baseia na edição e nos votos da comunidade.
Sky

Sim, eu apenas testei e, para obter uma cor aleatória (ou seja, desejando um valor aleatório entre 0 e 255), usei: "arc4random_uniform (256) + 0")
Chris Allinson

91

Eu fiz uma extensão do tipo Int. testado no playground, espero que seja útil. Também aceita intervalos negativos:

extension Int
{
    static func random(range: Range<Int> ) -> Int
    {
        var offset = 0

        if range.startIndex < 0   // allow negative ranges
        {
            offset = abs(range.startIndex)
        }

        let mini = UInt32(range.startIndex + offset)
        let maxi = UInt32(range.endIndex   + offset)

        return Int(mini + arc4random_uniform(maxi - mini)) - offset
    }
}

use como

var aRandomInt = Int.random(-500...100)  // returns a random number within the given range.

ou defina-a como uma extensão Range como propriedade como esta:

extension Range
{
    var randomInt: Int
    {
        get
        {
            var offset = 0

            if (startIndex as Int) < 0   // allow negative ranges
            {
                offset = abs(startIndex as Int)
            }

            let mini = UInt32(startIndex as Int + offset)
            let maxi = UInt32(endIndex   as Int + offset)

            return Int(mini + arc4random_uniform(maxi - mini)) - offset
        }
    }
}

// usage example: get an Int within the given Range:
let nr = (-1000 ... 1100).randomInt

6
Sua extensão é linda: 3 Um verdadeiro uso do Swift!
Kalzem

Eu gosto da extensão Range.
David James

Boa resposta. Minha única ressalva seria dizer que randomInt: não é uma extensão natural de Int ou Range. Eu simplesmente adicionaria isso como uma função autônoma em um arquivo de utilitários.
Vince O'Sullivan

Precisa ser atualização para rápida 3, substitua range.startIndex com range.lowerBound vez e endIndex é agora upperBound
Joseph Astrahan

62

Muitas respostas boas, mas eu só queria compartilhar minha função pessoal de geração de números aleatórios Swift favorita para números inteiros positivos:

Swift 2

func randomNumber(range: Range<Int> = 1...6) -> Int {
    let min = range.startIndex
    let max = range.endIndex
    return Int(arc4random_uniform(UInt32(max - min))) + min
}

Swift 3

Aqui está uma atualização rápida do Swift 3 e, como bônus, agora funciona para qualquer tipo de valor que esteja em conformidade com o protocolo SignedInteger - muito mais conveniente para aplicativos de dados principais que precisam especificar Int16, Int32 etc. Como uma observação rápida, se você realmente precisa que ele funcione em números inteiros não assinados, apenas copie a função inteira e substitua SignedIntegerpor UnsignedIntegere toIntMax()com toUIntMax().

func randomNumber<T : SignedInteger>(inRange range: ClosedRange<T> = 1...6) -> T {
    let length = (range.upperBound - range.lowerBound + 1).toIntMax()
    let value = arc4random().toIntMax() % length + range.lowerBound.toIntMax()
    return T(value)
}

Swift 4

Graças à remoção do toIntMax () no Swift 4, agora precisamos usar um meio diferente de converter para um tipo inteiro comum. Neste exemplo, estou usando o Int64, que é grande o suficiente para meus propósitos, mas se você estiver usando números inteiros não assinados ou tiver um tipo personalizado Int128 ou Int256, deverá usá-los.

public func randomNumber<T : SignedInteger>(inRange range: ClosedRange<T> = 1...6) -> T {
    let length = Int64(range.upperBound - range.lowerBound + 1)
    let value = Int64(arc4random()) % length + Int64(range.lowerBound)
    return T(value)
}

Mais uma vez, para o total de arquivos aleatórios, aqui está uma extensão que retorna um elemento aleatório de qualquer Collectionobjeto de tipo. Observe que isso usa a função acima para gerar seu índice, assim você precisará de ambos.

extension Collection {
    func randomItem() -> Self.Iterator.Element {
        let count = distance(from: startIndex, to: endIndex)
        let roll = randomNumber(inRange: 0...count-1)
        return self[index(startIndex, offsetBy: roll)]
    }
}

Uso

randomNumber()

retorna um número aleatório entre 1 e 6.

randomNumber(50...100)

retorna um número entre 50 e 100 inclusive. Naturalmente, você pode substituir os valores de 50 e 100 pelo que quiser.

Swift 4.2

Infelizmente, minha melhor resposta StackOverflow ficou obsoleta finalmente. Agora você pode usar simplesmente Int.random(in: 1 ... 6)para gerar um número aleatório em um determinado intervalo. Também funciona para outras formas de número inteiro e número de ponto flutuante. Os tipos de coleção agora também fornecem shuffle()e randomElement()funções. Portanto, não há mais necessidade de funções de randomização sofisticadas, a menos que você queira usar um tipo específico de randomizador.


1
Eu olhei para isso e pensei que deveria estar errado porque (max - min) = 5, produzindo um número inteiro aleatório no intervalo de 0 a 4 (mais 1 fazendo 1 a 5). Mas, ao colocar o código em um playground do Xcode, ficou evidente que funcionava. A razão é que max é realmente igual a 7, pois endIndex retorna "A primeira posição 'após o final' da coleção". (conforme indicado na documentação da Apple). Portanto, uma boa resposta e um exercício de aprendizado útil para mim.
Vince O'Sullivan

Isso funciona com números inteiros negativos também. randomNumber(-3 ... -1)funciona desde que você tenha espaços antes e depois do .... Você também pode random(-3 ..< -1excluir o último número.
Carter Medlin

Use em ClosedIntervalvez de Rangese você quiser que este funcione com não-inteiros.
Carter Medlin

Eu não faria. Os tipos de intervalo foram preteridos no Swift 3. Provavelmente, existe uma maneira de usar os Genéricos para expandir a funcionalidade do código, mas não tive tempo, inclinação ou motivo para investigar.
Ash

1
Lá vamos nós, uma versão inteira genérica do código.
Ash


18

Se você quiser, crie isso para números aleatórios. esta é a extensão do número Int e Double, Float

/**
    Arc Random for Double and Float
*/
public func arc4random <T: IntegerLiteralConvertible> (type: T.Type) -> T {
    var r: T = 0
    arc4random_buf(&r, UInt(sizeof(T)))
    return r
}
public extension Int {
    /**
    Create a random num Int
    :param: lower number Int
    :param: upper number Int
    :return: random number Int
    By DaRkDOG
    */
    public static func random (#lower: Int , upper: Int) -> Int {
        return lower + Int(arc4random_uniform(upper - lower + 1))
    }

}
public extension Double {
    /**
    Create a random num Double
    :param: lower number Double
    :param: upper number Double
    :return: random number Double
    By DaRkDOG
    */
    public static func random(#lower: Double, upper: Double) -> Double {
        let r = Double(arc4random(UInt64)) / Double(UInt64.max)
        return (r * (upper - lower)) + lower
    }
}
public extension Float {
    /**
    Create a random num Float
    :param: lower number Float
    :param: upper number Float
    :return: random number Float
    By DaRkDOG
    */
    public static func random(#lower: Float, upper: Float) -> Float {
        let r = Float(arc4random(UInt32)) / Float(UInt32.max)
        return (r * (upper - lower)) + lower
    }
}

USAR :

let randomNumDouble = Double.random(lower: 0.00, upper: 23.50)
let randomNumInt = Int.random(lower: 56, upper: 992)
let randomNumInt =Float.random(lower: 6.98, upper: 923.09)

operador binário / não pode ser aplicada a dois operandos Duplo
Jason G

13

Swift 3/4:

func randomNumber(range: ClosedRange<Int> = 1...6) -> Int {
    let min = range.lowerBound
    let max = range.upperBound
    return Int(arc4random_uniform(UInt32(1 + max - min))) + min
}

8

Isso ocorre porque arc4random_uniform () é definido da seguinte maneira:

func arc4random_uniform(_: UInt32) -> UInt32

Ele pega um UInt32 como entrada e cospe um UInt32. Você está tentando transmitir um intervalo de valores. arc4random_uniform fornece um número aleatório entre 0 e o número que você passa (exclusivamente); portanto, se, por exemplo, você deseja encontrar um número aleatório entre -50 e 50, como [-50, 50]você pode usararc4random_uniform(101) - 50


Sky respondeu minha pergunta perfeitamente. Acredito que você esteja dizendo a mesma coisa, muito obrigado. Pode confirmar que, definindo dice1,2 = arc4random_uniform (6) +1 realmente definiu o intervalo para 1-6. Testei isso com uma afirmação: D
arcreigh

6

Modifiquei a resposta do @DaRk -_- D0G para trabalhar com o Swift 2.0

/**
Arc Random for Double and Float
*/
public func arc4random <T: IntegerLiteralConvertible> (type: T.Type) -> T {
    var r: T = 0
    arc4random_buf(&r, sizeof(T))
    return r
}
public extension Int {
    /**
    Create a random num Int
    :param: lower number Int
    :param: upper number Int
    :return: random number Int
    By DaRkDOG
    */
    public static func random (lower: Int , upper: Int) -> Int {
        return lower + Int(arc4random_uniform(UInt32(upper - lower + 1)))
    }

}
public extension Double {
    /**
    Create a random num Double
    :param: lower number Double
    :param: upper number Double
    :return: random number Double
    By DaRkDOG
    */
    public static func random(lower: Double, upper: Double) -> Double {
        let r = Double(arc4random(UInt64)) / Double(UInt64.max)
        return (r * (upper - lower)) + lower
    }
}
public extension Float {
    /**
    Create a random num Float
    :param: lower number Float
    :param: upper number Float
    :return: random number Float
    By DaRkDOG
    */
    public static func random(lower: Float, upper: Float) -> Float {
        let r = Float(arc4random(UInt32)) / Float(UInt32.max)
        return (r * (upper - lower)) + lower
    }
}

Solução mais rápida aqui! Muito obrigado!
Andrew


3

Em rápida ...

Isso é inclusivo, a chamada random(1,2)retornará 1 ou 2. Isso também funcionará com números negativos.

    func random(min: Int, _ max: Int) -> Int {
        guard min < max else {return min}
        return Int(arc4random_uniform(UInt32(1 + max - min))) + min
    }

3

A resposta é apenas 1 código de linha:

let randomNumber = arc4random_uniform(8999) + 1000 //for 4 digit random number
let randomNumber = arc4random_uniform(899999999) + 100000000 //for 9 digit random number
let randomNumber = arc4random_uniform(89) + 10    //for 2 digit random number
let randomNumber = arc4random_uniform(899) + 100  //for 3 digit random number

A solução alternativa é:

    func generateRandomNumber(numDigits: Int) -> Int{
    var place = 1
    var finalNumber = 0;
    var finanum = 0;
    for var i in 0 ..< numDigits {
        place *= 10
        let randomNumber = arc4random_uniform(10)         
        finalNumber += Int(randomNumber) * place
        finanum = finalNumber / 10
           i += 1
    }
    return finanum
}

Embora a desvantagem seja que o número não pode começar de 0.


2

Desde o Swift 4.2:

Int {    
    public static func random(in range: ClosedRange<Int>) -> Int
    public static func random(in range: Range<Int>) -> Int
}

Usado como:

Int.random(in: 2...10)

2

Edit: Swift 4.2+ fornece isso agora:

(100...200).randomElement()

É idiomático para mim estender Range:

public extension Range where Bound == Int {
    var random: Int {
        return lowerBound + Int(arc4random_uniform(UInt32(upperBound - lowerBound)))
    }
}

public extension ClosedRange where Bound == Int {
    var random: Int {
        return lowerBound + Int(arc4random_uniform(UInt32(upperBound - lowerBound + 1)))
    }
}

Em uso:

let foo = (100..<600).random

Provavelmente apenas uma coisa estilística. Não há vantagem inerente a nenhum dos métodos, é apenas o que você se sentir mais confortável.
Ash

1
Para as pessoas que consideram esta “estilística” Eu tenho uma recomendação idioma para eles: C. Diverta-se!
Mxcl 27/03

Estou certo de que alguém já tinha feito isso há 3 anos :) stackoverflow.com/questions/34712453/…
Leo Dabus

1

Consegui criar um número aleatório usando o seguinte código:

var coin = arc4random_uniform(2) + 1

Espero que isso possa ajudá-lo.


0

Solução Swift 3 Xcode Beta 5. Baseado na resposta de Ted van Gaalen.

extension Int
  {
     static func random(range: Range<Int> ) -> Int
    {
        var offset = 0

        if range.lowerBound < 0   // allow negative ranges
        {
            offset = Swift.abs(range.lowerBound)
        }

        let mini = UInt32(range.lowerBound + offset)
        let maxi = UInt32(range.upperBound   + offset)

        return Int(mini + arc4random_uniform(maxi - mini)) - offset
    }
}

0

var rangeFromLimits = arc4random_uniform ((UPPerBound - LOWerBound) + 1)) + LOWerBound;


0

espero que isso esteja funcionando. fazer número aleatório entre o intervalo para arc4random_uniform ()?

var randomNumber = Int(arc4random_uniform(6))
print(randomNumber)

0

Provavelmente alguém achará útil esta versão um pouco atualizada da Rangeextensão da resposta de Ted van Gaalen usando o Swift 4 / Xcode 9+ :

extension CountableClosedRange where Bound == Int {
    var randomFromRange: Bound {
        get {
            var offset = 0
            if lowerBound < 0 {
                offset = abs(lowerBound)
            }
            let mini = UInt32(lowerBound + offset)
            let maxi = UInt32(upperBound + offset)
            return Int(mini + arc4random_uniform(maxi - mini)) - offset
        }
    }
}

let n = (-1000 ... 1000).randomFromRange
print(n)

Ou essa é uma solução um pouco "hacky" para oferecer suporte a intervalos abertos e fechados:

extension CountableRange where Bound == Int {
    var randomFromRange: Bound {
        return uniformRandom(from: lowerBound, to: upperBound)
    }
}

extension CountableClosedRange where Bound == Int {
    var randomFromRange: Bound {
        return uniformRandom(from: lowerBound, to: upperBound - 1)
    }
}

func uniformRandom(from: Int, to: Int) -> Int {
    var offset = 0
    if from < 0 {
        offset = abs(from)
    }
    let mini = UInt32(from + offset)
    let maxi = UInt32(to + offset)
    return Int(mini + arc4random_uniform(maxi - mini)) - offset
}

Não tenho certeza se existe uma maneira de adicionar propriedades aos dois tipos de intervalos simultaneamente.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.