Jak utworzyć liczbę losową między zakresem dla arc4random_uniform ()?

131

więc moim celem w tym kodebicie jest losowe rzucenie dwoma kośćmi, a jak wszyscy wiemy, twoja zwykła kość ma tylko 6 boków, więc zaimportowałem Foundation, aby uzyskać dostęp do arc4random_uniform (UInt32). Próbowałem użyć zakresu (1..7), aby uniknąć przypadkowego uzyskania 0, ale zwróciło to błąd, który nie podobał mi się zbytnio. Próbowałem to zrobić:

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

jednak to wróciło

Nie można znaleźć przeciążenia dla „init”, które akceptuje podane argumenty

Mam nadzieję, że to wystarczająca ilość informacji, aby mi pomóc niesamowite debi :)

Zwróć uwagę, że robię to na placu zabaw, aby ćwiczyć szybkość. Nie jest konieczne, żebym się tego nauczył; po prostu majstruję, zanim wskoczę do tworzenia rzeczywistych aplikacji: 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

Zwraca to błąd „Zakres $ T3” nie można zamienić na 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.")
arcreigh
źródło
4
Uważam, że powinieneś zrobić, dice1 = arc4random_uniform(6) + 1aby uzyskać zakres od 1 do 6. Nie wykonuję celu C w iOS ani nie mam żadnej wiedzy na temat szybkiego języka. Metoda losowa powinna zwrócić 0 - 5, a +1 będzie wynosić 1 - 6.
Niebo,
1
Zakres jest sam w sobie danymi obiektu, nie jest to liczba całkowita, dlatego otrzymujesz błąd, gdy argument przyjmuje tylko (UInt32) -u_int32_t arc4random_uniform(u_int32_t upper_bound);
Sky
Aha! dziękuję niebo! wykonałem asercję, aby sprawdzić, czy idzie mniej niż 0 i mogę potwierdzić, że jest to dokładnie to, czego potrzebowałem, umieść to jako odpowiedź, abym mógł to zaznaczyć jako taką!
arcreigh
prawdopodobieństwo = Int (arc4random_uniform (UInt32 (total))) - jeśli masz wiele skarg rzutowania, które są niespecyficzne (ponieważ typahead / nagłówki nie działają)
bshirley
Jest to wbudowane począwszy od Swift 4.2, jak wskazano poniżej stackoverflow.com/a/50696901/1148030
Peter Lamberg

Odpowiedzi:

260

Uważam, że powinieneś to zrobić

dice1 = arc4random_uniform(6) + 1;

aby uzyskać zakres 1 - 6. Nie wykonuję celu C w iOS ani nie mam żadnej wiedzy na temat szybkiego języka. Metoda losowa powinna zwracać wartość z przedziału od 0 do 5, a + 1 sprawi, że będzie to wartość z przedziału od 1 do 6.

Jeśli potrzebujesz zakresu między, powiedzmy, 10-30, po prostu zrób

int random = arc4random_uniform(21) + 10;
Niebo
źródło
2
@JoeSmith masz dokładnie rację, powinno być arc4random_uniform (21) +10, aby zwrócić zakres między 10 a 30, ponieważ górna granica nie obejmuje. Część „arc4random_uniform (20) +10” jest oparta na edycji społeczności i głosowaniach.
Sky
Tak, właśnie przetestowałem i aby uzyskać losowy kolor (tj. Chcąc uzyskać losową wartość z zakresu od 0 do 255 włącznie), użyłem: "arc4random_uniform (256) + 0"
Chris Allinson
91

Zrobiłem rozszerzenie typu Int. przetestowałem to na placu zabaw, mam nadzieję, że to się przyda. Akceptuje również zakresy ujemne:

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
    }
}

użyj jak

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

lub zdefiniuj go jako rozszerzenie zakresu jako właściwość w następujący sposób:

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
Ted van Gaalen
źródło
6
Twoje rozszerzenie jest piękne: 3 Prawdziwe wykorzystanie Swift!
Kalzem,
Podoba mi się rozszerzenie Range.
David James,
Dobra odpowiedź. Moim jedynym zastrzeżeniem byłoby stwierdzenie, że randomInt: nie jest naturalnym rozszerzeniem ani Int ani Range. Dodałbym to po prostu jako samodzielną funkcję w pliku narzędzi.
Vince O'Sullivan
Musi zostać zaktualizowany dla Swift 3, zamiast tego zamień range.startIndex na range.lowerBound, a endIndex jest teraz upperBound
Joseph Astrahan
62

Sporo dobrych odpowiedzi, ale chciałem tylko podzielić się moją ulubioną funkcją generowania liczb losowych Swift dla dodatnich liczb całkowitych:

Szybki 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
}

Szybki 3

Oto szybka aktualizacja dla Swift 3 i jako bonus, teraz działa dla każdego typu wartości, który jest zgodny z protokołem SignedInteger - znacznie wygodniejszy dla podstawowych aplikacji danych, które muszą określić Int16, Int32 itp. naprawdę potrzebne do pracy na liczby całkowite bez znaku, a także, po prostu skopiować całą funkcję następnie zastąpić SignedIntegerz UnsignedIntegeri toIntMax()z 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)
}

Szybki 4

Dzięki usunięciu toIntMax () w Swift 4, musimy teraz użyć innych sposobów konwersji na wspólny typ liczb całkowitych. W tym przykładzie używam Int64, który jest wystarczająco duży do moich celów, ale jeśli używasz liczb całkowitych bez znaku lub masz typ niestandardowy Int128 lub Int256, powinieneś ich użyć.

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)
}

Jeszcze jedno, dla całkowitego losowego pliku, oto rozszerzenie, które zwraca losowy element z dowolnego Collectionobiektu typu. Zauważ, że używa powyższej funkcji do wygenerowania indeksu, więc będziesz potrzebować obu.

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)]
    }
}

Stosowanie

randomNumber()

zwraca liczbę losową od 1 do 6.

randomNumber(50...100)

zwraca liczbę od 50 do 100 włącznie. Oczywiście wartości 50 i 100 można zastąpić dowolnymi wartościami.

Swift 4.2

Niestety, moja najlepsza odpowiedź StackOverflow stała się w końcu przestarzała. Możesz teraz użyć po prostu Int.random(in: 1 ... 6)do wygenerowania liczby losowej w podanym zakresie. Działa również dla innych postaci liczb całkowitych i zmiennoprzecinkowych. Typy kolekcji również teraz udostępniają shuffle()i randomElement()działają. Dlatego nie ma już potrzeby wymyślnych funkcji randomizacji, chyba że chcesz użyć określonego typu randomizera.

Popiół
źródło
1
Spojrzałem na to i pomyślałem, że musi być źle, ponieważ (max - min) = 5, dając losową liczbę całkowitą z zakresu od 0 do 4 (plus 1, co daje 1 do 5). Ale po umieszczeniu kodu na placu zabaw Xcode było oczywiste, że działa. Powodem jest to, że max jest w rzeczywistości równe 7, ponieważ endIndex zwraca „Pierwsza pozycja kolekcji za końcową”. (zgodnie z dokumentacją Apple). A więc dobra odpowiedź i przydatne ćwiczenie do nauki.
Vince O'Sullivan
Działa to również z ujemnymi liczbami całkowitymi. randomNumber(-3 ... -1)działa tak długo, jak masz spacje przed i po .... Możesz użyć, random(-3 ..< -1aby wykluczyć również ostatnią liczbę.
Carter Medlin,
Użyj ClosedIntervalzamiast, Rangejeśli chcesz, aby ta praca działała z liczbami niecałkowitymi.
Carter Medlin,
Nie chciałbym. Typy interwałów zostały wycofane w Swift 3. Prawdopodobnie istnieje sposób na użycie Generics do rozszerzenia funkcjonalności kodu, ale nie miałem czasu, chęci ani powodu, by to zbadać.
Ash,
1
Zaczynamy, uogólniona wersja kodu będąca liczbą całkowitą.
Ash
23

Według Swift 4.2 teraz łatwo jest uzyskać taką liczbę losową

let randomDouble = Double.random(in: -7.9...12.8)

let randomIntFrom0To10 = Int.random(in: 0 ..< 10)

aby uzyskać więcej informacji, sprawdź to

Nosov Pavel
źródło
18

Jeśli chcesz, tworzę to dla liczb losowych. jest to rozszerzenie liczby Int i 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
    }
}

POSŁUGIWAĆ SIĘ :

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)
YannSteph
źródło
operator binarny / nie można zastosować do dwóch podwójnych operandów
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
}
Alessandro Ornano
źródło
8

Dzieje się tak, ponieważ arc4random_uniform () jest zdefiniowana w następujący sposób:

func arc4random_uniform(_: UInt32) -> UInt32

Pobiera UInt32 jako dane wejściowe i wypluwa UInt32. Próbujesz przekazać mu zakres wartości. arc4random_uniform daje losową liczbę z przedziału od 0 do liczby, którą przekazujesz (wyłącznie), więc jeśli na przykład chcesz znaleźć losową liczbę między -50 a 50, tak jak w [-50, 50]przypadkuarc4random_uniform(101) - 50

Mick MacCallum
źródło
Sky odpowiedział na moje pytanie doskonale. Myślę, że mówisz to samo, dziękuję bardzo, mogę potwierdzić, że ustawiając kostki1,2 = arc4random_uniform (6) +1 rzeczywiście ustawił zakres na 1-6 Przetestowałem to z asercją: D
arcreigh,
6

Zmodyfikowałem odpowiedź @DaRk -_- D0G na pracę ze 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
    }
}
Skakałem
źródło
Najszybsze rozwiązanie tutaj! Wielkie dzięki!
Andrew
5

Szybki:

var index = 1 + random() % 6
Alexander Volkov
źródło
2
Musisz to zasiać, bo inaczej za każdym razem otrzymasz tę samą liczbę losową.
William T.
3

W szybkim ...

To jest włącznie, dzwonienie random(1,2)zwróci 1 lub 2, Działa to również z liczbami ujemnymi.

    func random(min: Int, _ max: Int) -> Int {
        guard min < max else {return min}
        return Int(arc4random_uniform(UInt32(1 + max - min))) + min
    }
Carter Medlin
źródło
3

Odpowiedź to tylko jeden kod liniowy:

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

Alternatywnym rozwiązaniem jest:

    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
}

Chociaż wadą jest to, że liczba nie może zaczynać się od 0.

Nupur Sharma
źródło
2

Od wersji Swift 4.2:

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

Używane jak:

Int.random(in: 2...10)
Camsoft
źródło
2

Edycja: Swift 4.2+ zapewnia to teraz:

(100...200).randomElement()

To dla mnie idiomatyczne rozszerzenie 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)))
    }
}

W użyciu:

let foo = (100..<600).random
mxcl
źródło
Prawdopodobnie tylko stylistyczna sprawa. Żadna z tych metod nie ma nieodłącznych zalet, jest to po prostu cokolwiek, z czym czujesz się bardziej komfortowo.
Ash
1
Dla ludzi, którzy uważają ten „stylistykę” Mam zalecenie językowy dla nich C. Baw się dobrze!
mxcl
Jestem pewien, że ktoś już to zrobił 3 lata temu :) stackoverflow.com/questions/34712453/…
Leo Dabus
1

Pomyślnie utworzyłem liczbę losową za pomocą następującego kodu:

var coin = arc4random_uniform(2) + 1

Mam nadzieję, że to może ci pomóc.

Bigfoot11
źródło
0

Rozwiązanie Swift 3 Xcode Beta 5. Na podstawie odpowiedzi Teda van Gaalena.

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
    }
}
Statik
źródło
0

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

dev_shanghai
źródło
0

mam nadzieję, że to działa. zrobić liczbę losową między zakresem dla arc4random_uniform ()?

var randomNumber = Int(arc4random_uniform(6))
print(randomNumber)
Vidhyapathi Kandhasamy
źródło
0

Prawdopodobnie przydaje się ta nieco zaktualizowana wersja Rangerozszerzenia z odpowiedzi Teda van Gaalena przy użyciu 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)

Lub to trochę „hacky” rozwiązanie do obsługi otwartych i zamkniętych okresów:

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
}

Nie jestem pewien, czy istnieje sposób na jednoczesne dodanie właściwości do obu typów interwałów.

devforfu
źródło