หนึ่งจะสร้างตัวเลขสุ่มระหว่างช่วงสำหรับ arc4random_uniform () ได้อย่างไร


129

ดังนั้นเป้าหมายของฉันในโค้ดบิตนี้คือการสุ่มทอยลูกเต๋าสองลูกและอย่างที่เราทุกคนรู้ว่าการตายปกติของคุณมีเพียง 6 ด้านดังนั้นฉันจึงนำเข้า Foundation เพื่อเข้าถึง arc4random_uniform (UInt32) ฉันพยายามใช้ช่วงของ (1..7) เพื่อหลีกเลี่ยงการสุ่มรับ 0 อย่างไรก็ตามมันส่งคืนข้อผิดพลาดที่ฉันไม่ชอบมากเกินไป ฉันพยายามทำสิ่งนี้:

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

อย่างไรก็ตามที่กลับมา

ไม่พบโอเวอร์โหลดสำหรับ "init" ที่ยอมรับอาร์กิวเมนต์ที่ให้มา

ฉันหวังว่านี่จะเป็นข้อมูลที่เพียงพอสำหรับคุณที่น่าทึ่งที่จะช่วยฉัน :)

โปรดทราบว่าฉันกำลังทำสิ่งนี้ในสนามเด็กเล่นเพื่อฝึกความรวดเร็ว ฉันไม่จำเป็นต้องเรียนรู้วิธีการทำสิ่งนี้ เป็นเพียงแค่ฉันซ่อมแซมก่อนที่จะเริ่มสร้างแอพจริง: 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

สิ่งนี้ส่งคืนข้อผิดพลาดของ 'Range $ T3' ไม่สามารถแปลงเป็น 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
ฉันเชื่อว่าคุณควรทำdice1 = arc4random_uniform(6) + 1เพื่อให้ได้ช่วง 1 - 6 ฉันไม่ได้ใช้ iOS วัตถุประสงค์ C และไม่มีความรู้ใด ๆ เกี่ยวกับภาษาที่รวดเร็ว วิธีการสุ่มควรส่งคืนคุณเป็น 0 - 5 และ + 1 จะเป็น 1 - 6
ท้องฟ้า

1
Range เป็นข้อมูลออบเจ็กต์เองไม่ใช่ข้อมูลจำนวนเต็มนั่นคือสาเหตุที่คุณได้รับข้อผิดพลาดเมื่ออาร์กิวเมนต์ใช้เวลาใน (UInt32) -u_int32_t arc4random_uniform(u_int32_t upper_bound);
Sky

AHA! ขอบคุณฟ้า! ทำการยืนยันเพื่อทดสอบว่ามันไปน้อยกว่า 0 หรือไม่และสามารถยืนยันได้ว่านี่คือสิ่งที่ฉันต้องการวางไว้เป็นคำตอบเพื่อให้ฉันสามารถตรวจสอบได้!
arcreigh

ความน่าจะเป็น = Int (arc4random_uniform (UInt32 (ทั้งหมด))) - หากคุณมีข้อร้องเรียนเกี่ยวกับการแคสต์หลายรายการที่ไม่เฉพาะเจาะจง (เนื่องจาก typeahead / headers ไม่ทำงาน)
bshirley

สิ่งนี้ถูกสร้างขึ้นโดยเริ่มต้นด้วย Swift 4.2 ตามที่ระบุไว้ด้านล่างstackoverflow.com/a/50696901/1148030
Peter Lamberg

คำตอบ:


260

ฉันเชื่อว่าคุณควรทำ

dice1 = arc4random_uniform(6) + 1;

เพื่อให้ได้ช่วง 1 - 6 ฉันไม่ได้ใช้ iOS วัตถุประสงค์ C และฉันไม่มีความรู้เกี่ยวกับภาษาที่รวดเร็ว วิธีการสุ่มควรส่งคืนค่าระหว่าง 0 ถึง 5 และ + 1 จะทำให้เป็นค่าระหว่าง 1 ถึง 6

หากคุณต้องการช่วงระหว่างให้พูดว่า 10 - 30 ก็แค่ทำ

int random = arc4random_uniform(21) + 10;

2
@JoeSmith คุณตรงกับสิ่งนี้มันควรจะเป็น arc4random_uniform (21) +10 เพื่อส่งคืนช่วงระหว่าง 10 - 30 เนื่องจากขอบเขตบนไม่รวม ส่วน "arc4random_uniform (20) +10" ขึ้นอยู่กับการแก้ไขและการโหวตของชุมชน
ท้องฟ้า

ใช่ฉันเพิ่งทดสอบและเพื่อให้ได้สีแบบสุ่ม (เช่นต้องการค่าสุ่มระหว่างและรวมถึง 0-255) ฉันใช้: "arc4random_uniform (256) + 0"
Chris Allinson

91

ฉันได้สร้างส่วนขยายประเภท Int ทดสอบในสนามเด็กเล่นหวังว่าจะเป็นประโยชน์ นอกจากนี้ยังยอมรับช่วงค่าลบ:

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

ใช้เช่น

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

หรือกำหนดเป็นส่วนขยายช่วงเป็นคุณสมบัติดังนี้:

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
ส่วนขยายของคุณสวยงาม: 3 การใช้งาน Swift อย่างแท้จริง!
Kalzem

ฉันชอบส่วนขยายช่วง
David James

คำตอบที่ดี. ข้อแม้เดียวของฉันคือการบอกว่า randomInt: ไม่ใช่ส่วนขยายตามธรรมชาติของ Int หรือ Range ฉันจะเพิ่มสิ่งนี้เป็นฟังก์ชั่นสแตนด์อะโลนในไฟล์ยูทิลิตี้
Vince O'Sullivan

จำเป็นต้องมีการอัปเดตสำหรับ swift 3 แทนที่ range.startIndex ด้วย range.lowerBound แทนและ endIndex เป็น upperBound
Joseph Astrahan

62

คำตอบที่ดีค่อนข้างน้อย แต่ฉันแค่อยากจะแบ่งปันฟังก์ชันการสร้างตัวเลขสุ่ม 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
}

สวิฟต์ 3

นี่คือการอัปเดตด่วนสำหรับ Swift 3 และเป็นโบนัสตอนนี้ใช้งานได้กับประเภทค่าใด ๆ ที่สอดคล้องกับโปรโตคอล SignedInteger - สะดวกกว่ามากสำหรับแอปพลิเคชันข้อมูลหลักที่ต้องระบุ Int16, Int32 เป็นต้นโปรดทราบอย่างรวดเร็วหากคุณ จริงๆต้องไปทำงานในจำนวนเต็มไม่ได้ลงนามเป็นอย่างดีเพียงคัดลอกฟังก์ชั่นทั้งหมดแล้วแทนที่SignedIntegerด้วยUnsignedIntegerและมีtoIntMax()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)
}

สวิฟต์ 4

ด้วยการลบ toIntMax () ใน Swift 4 ตอนนี้เราต้องใช้วิธีอื่นในการแปลงเป็นประเภทจำนวนเต็มทั่วไป ในตัวอย่างนี้ฉันใช้ Int64 ซึ่งมีขนาดใหญ่พอสำหรับวัตถุประสงค์ของฉัน แต่ถ้าคุณใช้จำนวนเต็มที่ไม่ได้ลงชื่อหรือมีประเภทที่กำหนดเอง Int128 หรือ Int256 คุณควรใช้สิ่งเหล่านี้

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

อีกประการหนึ่งสำหรับการสุ่ม phile ทั้งหมดนี่คือส่วนขยายที่ส่งคืนองค์ประกอบแบบสุ่มจากCollectionวัตถุประเภทใด ๆ โปรดทราบว่าสิ่งนี้ใช้ฟังก์ชันด้านบนเพื่อสร้างดัชนีดังนั้นคุณจะต้องใช้ทั้งสองอย่าง

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

การใช้

randomNumber()

ส่งกลับตัวเลขสุ่มระหว่าง 1 ถึง 6

randomNumber(50...100)

ส่งคืนตัวเลขระหว่าง 50 ถึง 100 รวม โดยปกติคุณสามารถแทนที่ค่า 50 และ 100 ด้วยสิ่งที่คุณต้องการ

Swift 4.2

อนิจจาคำตอบ StackOverflow ที่ดีที่สุดของฉันถูกแสดงผลล้าสมัยในที่สุด ตอนนี้คุณสามารถใช้เพียงInt.random(in: 1 ... 6)เพื่อสร้างตัวเลขสุ่มในช่วงที่กำหนด ใช้ได้กับรูปแบบอื่น ๆ ของจำนวนเต็มและเลขทศนิยม ขณะนี้ยังมีประเภทคอลเลกชันshuffle()และrandomElement()ฟังก์ชันต่างๆ ดังนั้นจึงไม่จำเป็นต้องใช้ฟังก์ชันการสุ่มแบบแฟนซีอีกต่อไปเว้นแต่คุณจะต้องการใช้ประเภทสุ่มเฉพาะ


1
ฉันดูสิ่งนี้และคิดว่ามันต้องผิดแน่ ๆ เพราะ (max - min) = 5 โดยให้จำนวนเต็มสุ่มในช่วง 0 ถึง 4 (บวก 1 ทำให้ 1 ถึง 5) แต่ด้วยการใส่รหัสลงใน Xcode playground เห็นได้ชัดว่ามันใช้งานได้ เหตุผลที่ว่า max มีค่าเท่ากับ 7 จริง ๆ แล้วเนื่องจาก endIndex ส่งกลับ "ตำแหน่งแรกของคอลเลคชัน 'เลยจุดสิ้นสุด'" (ตามที่ระบุไว้ในเอกสารของ Apple) ดังนั้นคำตอบที่ดีและแบบฝึกหัดการเรียนรู้ที่มีประโยชน์สำหรับฉัน
Vince O'Sullivan

ซึ่งใช้ได้กับจำนวนเต็มลบเช่นกัน randomNumber(-3 ... -1)...ทำงานตราบเท่าที่คุณมีช่องว่างก่อนและหลัง คุณสามารถใช้random(-3 ..< -1เพื่อยกเว้นหมายเลขสุดท้ายได้เช่นกัน
Carter Medlin

ใช้ClosedIntervalแทนRangeถ้าคุณต้องการให้ทำงานนี้กับไม่ใช่จำนวนเต็ม
Carter Medlin

ฉันจะไม่ ประเภทช่วงเวลาเลิกใช้แล้วใน Swift 3 อาจมีวิธีใช้ Generics เพื่อขยายฟังก์ชันการทำงานของโค้ด แต่ฉันไม่มีเวลาความโน้มเอียงหรือเหตุผลในการตรวจสอบ
เถ้า

1
เอาล่ะเป็นรหัสรุ่นจำนวนเต็มทั่วไป
Ash


18

ถ้าคุณต้องการฉันสร้างมันสำหรับตัวเลขสุ่ม นี่คือส่วนขยายของจำนวน Int และ 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
    }
}

ใช้:

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)

ตัวดำเนินการไบนารี / ไม่สามารถใช้กับตัวถูกดำเนินการสองตัว
Jason G


8

นั่นเป็นเพราะ arc4random_uniform () ถูกกำหนดไว้ดังนี้:

func arc4random_uniform(_: UInt32) -> UInt32

ใช้ UInt32 เป็นอินพุตและคาย UInt32 ออกมา คุณกำลังพยายามส่งผ่านค่าต่างๆ arc4random_uniform ให้หมายเลขสุ่มระหว่าง 0 ถึงและหมายเลขที่คุณส่งผ่าน (โดยเฉพาะ) ตัวอย่างเช่นหากคุณต้องการหาตัวเลขสุ่มระหว่าง -50 ถึง 50 เช่นเดียวกับที่[-50, 50]คุณสามารถใช้arc4random_uniform(101) - 50


Sky ตอบคำถามของฉันอย่างสมบูรณ์ฉันเชื่อว่าคุณกำลังพูดในสิ่งเดียวกันเช่นกันขอบคุณมากสามารถยืนยันได้ว่าการตั้งค่า dice1,2 = arc4random_uniform (6) +1 ได้ตั้งค่าช่วงเป็น 1-6 ฉันทดสอบสิ่งนี้ด้วยการยืนยัน: D
arcreigh

6

ฉันแก้ไขคำตอบของ @DaRk -_- D0G เพื่อทำงานกับ 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
    }
}

ทางออกที่เร็วที่สุดที่นี่! ขอบคุณมาก!
Andrew

5

สวิฟท์:

var index = 1 + random() % 6

2
คุณต้องเริ่มต้นสิ่งนี้มิฉะนั้นคุณจะได้รับหมายเลขสุ่มเหมือนกันทุกครั้ง
William T.

3

อย่างรวดเร็ว ...

รวมแล้วการโทรrandom(1,2)จะส่งกลับ 1 หรือ 2 ซึ่งจะใช้ได้กับตัวเลขที่เป็นค่าลบ

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

3

คำตอบคือโค้ด 1 บรรทัด:

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

ทางเลือกอื่นคือ:

    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
}

แม้ว่าข้อเสียเปรียบคือตัวเลขนั้นไม่สามารถเริ่มจาก 0 ได้


2

ตั้งแต่ Swift 4.2:

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

ใช้เช่น:

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

2

แก้ไข: Swift 4.2+ ให้สิ่งนี้ทันที:

(100...200).randomElement()

เป็นสำนวนสำหรับฉันที่จะขยาย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)))
    }
}

ใช้งานอยู่:

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

น่าจะเป็นแค่โวหาร ไม่มีข้อได้เปรียบโดยธรรมชาติสำหรับทั้งสองวิธีเป็นเพียงสิ่งที่คุณรู้สึกสบายใจกว่า
Ash

1
สำหรับคนที่พิจารณานี้“โวหาร” Cผมมีคำแนะนำภาษาสำหรับพวกเขา: มีความสุข!
mxcl

ฉันแน่ใจว่ามีคนทำไปแล้วเมื่อ 3 ปีก่อน :) stackoverflow.com/questions/34712453/…
Leo Dabus

1

ฉันสร้างหมายเลขสุ่มสำเร็จโดยใช้รหัสต่อไปนี้:

var coin = arc4random_uniform(2) + 1

หวังว่านี่จะช่วยคุณได้


0

โซลูชัน Swift 3 Xcode Beta 5 จากคำตอบของ 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

อาจมีคนพบว่ามีประโยชน์เวอร์ชันRangeส่วนขยายที่อัปเดตเล็กน้อยจากคำตอบของ Ted van Gaalen โดยใช้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)

หรือนี่เป็นโซลูชัน "แฮ็ก" เล็กน้อยเพื่อรองรับช่วงเวลาเปิดและปิด:

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
}

ไม่แน่ใจว่ามีวิธีเพิ่มคุณสมบัติให้กับช่วงเวลาทั้งสองประเภทพร้อมกันหรือไม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.