เราจะสร้างตัวเลขสุ่มในภาษา Swift ของ Apple ได้อย่างไร


443

ฉันรู้ว่าหนังสือ Swift ได้จัดทำเครื่องมือสร้างตัวเลขแบบสุ่ม วิธีที่ดีที่สุดในการคัดลอกและวางการใช้งานนี้ในโปรแกรมของตัวเองหรือไม่ หรือว่ามีห้องสมุดที่เราสามารถใช้ตอนนี้ได้หรือไม่?

คำตอบ:


466

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
ฉันไม่ต้องนำเข้าอย่างชัดเจนดาร์วิน
finneycanhelp

3
ในไฟล์ Playground ของฉันฉันต้องนำเข้าดาร์วินเพราะฉันไม่ได้นำเข้าอย่างอื่น
DonnaLea

SoliQuiD: ยกเว้นละเว้นขีดล่างพิเศษหลังจาก arc4 เช่น arc4random_uniform (5)
Ali Beadle

3
คำเตือน : RC4 หรือ ARC4 ได้รับการแสดงที่จะมีความแตกต่างจากค่าสุ่มบริสุทธิ์ ดังนั้นแม้ว่า ARC4 (ตัวเลขกระแส) เสียงที่เชื่อถือได้เข้ารหัสก็จริงไม่ได้
Maarten Bodewes

2
@MaartenBodewes: ไม่เกี่ยวข้องโดยตรงกับคำตอบนี้อีกต่อไป แต่ arc4random ไม่ได้ใช้รหัส RC4 จริง ๆ ทั้งๆที่ชื่อของมันใน macOS หรือ BSDs ใช้ CSPRNG ที่ระบบจัดเตรียมไว้ให้บน macOS และ ChaCha20 สำหรับ BSD ส่วนใหญ่ RNG เริ่มต้นของ Swift (ดังที่ใช้ในคำตอบนี้) เรียกว่าเป็นรายละเอียดการนำไปใช้งานใน macOS แต่ใช้ตัวสร้างพื้นฐานที่เหมาะสมในแต่ละแพลตฟอร์มที่รองรับ
Stephen Canon

496

ใช้arc4random_uniform(n)สำหรับจำนวนเต็มแบบสุ่มระหว่าง 0 ถึง n-1

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

ส่งผลลัพธ์ไปที่ Int เพื่อที่คุณจะได้ไม่ต้องพิมพ์ vars ของคุณอย่างชัดเจนUInt32(ซึ่งดูเหมือนว่าไม่น่าสนใจ)


7
ง่ายมาก. ฉันชอบมัน. Upvote! 0แต่ลูกเต๋าที่เกิดขึ้นจริงไม่ได้ ในรหัสของคุณอาจจะเป็นdiceRoll 0แค่พูดว่า ...
MK Safi

61
Int(arc4random_uniform(6)+1)ใช่คุณต้องการจริงๆ
Michael Dorst

5
ความน่าจะเป็น = Int (arc4random_uniform (UInt32 (ทั้งหมด))) - ฉันต้องโยนเข้าไปใน UInt32
bshirley

4
ให้ randomElementInArray = Int (arc4random_uniform (array.count))
cmario

อย่าลืมที่จะโยนพารามิเตอร์, n, เข้าสู่arc3random_uniform(n)a UInt32(n)หากคุณใช้ค่าที่ยังไม่ได้เป็นประเภทนั้น
Dave Fontenot

117

แก้ไข: อัปเดตสำหรับ Swift 3.0

arc4randomทำงานได้ดีใน Swift แต่ฟังก์ชั่นพื้นฐานนั้น จำกัด จำนวนเต็ม 32 บิต ( Int64- บิตบน iPhone 5S และ Macs ทันสมัย) นี่คือฟังก์ชั่นทั่วไปสำหรับจำนวนสุ่มชนิดที่แสดงออกโดยตัวอักษรจำนวนเต็ม:

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)

มันไม่ได้รวบรวม: var r = arc4random(UInt64). กรุณาแนะนำคุณหมายถึงอะไรที่นี่?
Ossir

@Ossir compiles ดีสำหรับฉัน ... มันหมายถึงการเรียกใช้ฟังก์ชันarc4random(ที่กำหนดไว้ในการป้องกันรหัสแรก) อาร์กิวเมนต์ซึ่งเป็นUInt64 Type
jstn

arc4random_buf (และส่วนขยาย 64- บิตทั้งหมด) ได้รับผลกระทบจากโมดูโลหรือไม่?
Matthew Seaman

อคติแบบโมดูโลจะเกิดขึ้นเมื่อคุณเพิ่มขอบเขตบนเท่านั้นดังนั้นจึงไม่มีผลกับarc4random_bufมัน วัตถุประสงค์ของส่วนขยายเหล่านี้คือการทำสิ่งที่arc4random_uniformจะทำ (บรรเทาความอคติโมดูโล) ยกเว้นประเภท 64 บิต
jstn

เมื่อใช้ฟังก์ชั่นลอยฉันจะรวมค่าสูงสุดในช่วงของความเป็นไปได้อย่างไร สมมุติว่าผมทำ 0.0 ต่ำกว่าและ 1.0 สูงกว่า ด้วยตรรกะนี้มันจะให้ฉัน 0.0 ถึง 0.99999999 แต่ฉันต้องการรวม 1.0 เป็นไปได้แทน ฉันจะบรรลุสิ่งนี้ได้อย่างไร
MobileMon

80

แก้ไขสำหรับ Swift 4.2

เริ่มต้นใน Swift 4.2 แทนที่จะใช้ฟังก์ชั่น C ที่นำเข้า arc4random_uniform () ตอนนี้คุณสามารถใช้ฟังก์ชั่นดั้งเดิมของ Swift ได้แล้ว

// Generates integers starting with 0 up to, and including, 10
Int.random(in: 0 ... 10)

คุณสามารถใช้random(in:)เพื่อรับค่าสุ่มสำหรับค่าดั้งเดิมอื่น ๆ เช่นกัน; เช่น Int, Double, Float และแม้แต่ Bool

Swift เวอร์ชั่น <4.2

วิธีนี้จะสร้างIntค่าสุ่มระหว่างขั้นต่ำและสูงสุดที่กำหนด

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

61

ฉันใช้รหัสนี้:

var k: Int = random() % 10;

20
คุณต้องโทรหา srandom (UInt32 (เวลา (ไม่มี))) ก่อนมิฉะนั้นจะส่งคืนหมายเลขลำดับเดิมเสมอ
Hola Soy Edu Feliz Navidad

3
ฉันอ่าน apple doc โดยการสุ่ม () สองครั้ง แต่ไม่สามารถรวบรวมการใช้งานได้ ... ฉันหวังว่าพวกเขาจะรวมตัวอย่างโค้ดง่ายๆแบบนี้ไว้ด้านบน "ฟังก์ชั่นสุ่ม () ใช้ตัวป้อนกลับแบบไม่เป็นเชิงเส้นเติมแต่งแบบสุ่มสร้างตารางเริ่มต้นที่มีขนาดยาว 31 จำนวนเต็มมันส่งกลับตัวเลขสุ่มหลอกต่อเนื่องในช่วงตั้งแต่ 0 ถึง (2 31) -1 ช่วงเวลาของตัวสร้างตัวเลขสุ่มนี้มีขนาดใหญ่มากประมาณ 16 * ((2 31) -1) " ... ขอบคุณแอปเปิ้ลมากมาย ... ฉันจะต้องอ้างอิงในวิทยานิพนธ์ต่อไปของฉัน
nocarrier

1
Random () บางครั้งนำไปสู่ความผิดพลาดอย่างฉับพลันบนไอแพด หากสิ่งนี้เกิดขึ้นใช้ arc4random_uniform (6) จากด้านบน หากคุณใช้การสุ่ม () คุณสามารถสร้างค่าการสุ่มได้มากขึ้นโดยการเติม srandomdev ()
JackPearse

1
ฉันได้รับข้อความแสดงข้อผิดพลาดของคอมไพเลอร์:random is unavailable in Swift: Use arc4random instead.
Mike Keskinov

1
วิธีนี้มีอคติแบบโมดูโล: zuttobenkyou.wordpress.com/2012/10/18/…
rgov

33

ในฐานะของ iOS 9 คุณสามารถใช้คลาส GameplayKit ใหม่เพื่อสร้างตัวเลขสุ่มได้หลายวิธี

คุณมีแหล่งที่มาสี่ประเภทให้เลือก: แหล่งสุ่มทั่วไป (ไม่มีชื่อลงไปที่ระบบเพื่อเลือกว่ามันทำอะไร), เชิงเส้นเชิงเส้นตรง, ARC4 และ Mersenne Twister สิ่งเหล่านี้สามารถสร้าง ints แบบสุ่มลอยและบูลส์

ในระดับที่ง่ายที่สุดคุณสามารถสร้างตัวเลขสุ่มจากแหล่งสุ่มในตัวของระบบดังนี้:

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

7
ชุดนี้จะนำเข้าหนักแค่ไหน ฉันไม่ต้องการที่จะขยายรหัสของฉัน
μολὼν.λαβέ

25

คุณสามารถทำได้เช่นเดียวกับใน C:

let randomNumber = arc4random()

randomNumberถูกอนุมานว่าเป็นชนิดUInt32(จำนวนเต็มที่ไม่ได้ลงชื่อ 32 บิต)


12
ภาคผนวก: rand, arc4random, drand48และเพื่อน ๆ ทุกคนในDarwinโมดูล มีการนำเข้าแล้วสำหรับคุณหากคุณกำลังสร้างแอป Cocoa, UIKit หรือมูลนิธิ แต่คุณจะต้องimport Darwinเล่นในสนามเด็กเล่น
rickster

5
และอย่าพยายามโยนผลลัพธ์ของ arc4random () ไปยัง Int - สิ่งนี้จะทำงานได้ดีบนแพลตฟอร์ม 64 บิต แต่บนแพลตฟอร์ม 32 บิต Ints มีการลงนามแบบ 32 บิตดังนั้นคุณจะได้รับค่าลบที่ไม่คาดคิด หมายเลข นี่ทำให้คนขึ้นสองสามคนแล้วดังนั้นฉันคิดว่าฉันจะพูดถึงที่นี่
Matt Gibson

23

ใช้ arc4random_uniform()

การใช้งาน:

arc4random_uniform(someNumber: UInt32) -> UInt32

นี้จะช่วยให้คุณจำนวนเต็มแบบสุ่มในช่วงที่จะ0someNumber - 1

ค่าสูงสุดสำหรับUInt324,294,967,295 (นั่นคือ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,295UUInt32ที่ไม่มีการลงชื่อ

พิจารณาข้อผิดพลาดต่อไปนี้:

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

ดังนั้นคุณเพียงแค่ต้องแน่ใจว่าพารามิเตอร์อินพุตของคุณอยู่ในUInt32ช่วงและคุณไม่ต้องการเอาต์พุตที่อยู่นอกช่วงนั้น


19

ตัวอย่างสำหรับหมายเลขสุ่มในระหว่าง 10 (0-9);

import UIKit

let randomNumber = Int(arc4random_uniform(10))

รหัสง่ายมาก - ง่ายและสั้น


16

ฉันสามารถใช้rand()เพื่อรับ CInt แบบสุ่มได้ คุณสามารถทำให้มันเป็น Int ได้โดยใช้สิ่งนี้:

let myVar: Int = Int(rand())

คุณสามารถใช้ฟังก์ชันสุ่ม C ที่คุณชื่นชอบและเพียงแปลงเป็นค่าเป็น Int หากต้องการ


อ๋อการแปลงแบบเป็นธุรกิจที่ยุ่งยากหากปล่อยให้ตัวสร้าง Int จัดการกับมันเป็นการประหยัดความเจ็บปวดที่แท้จริง
Kzrbill

4
โปรดทราบว่าหากคุณไม่ได้เรียก srand (... ) (เรียกใช้เพียงครั้งเดียวเท่านั้น) ก่อนใช้ rand () ลำดับของตัวเลขจะเหมือนกันทุกครั้งระหว่างการทำงานของโปรแกรมของคุณ หากคุณไม่ต้องการสิ่งนี้ให้ใช้ arc4random ()
SomeGuy

2
คุณสามารถใช้random()ซึ่งจะส่งกลับIntมากกว่าUInt32- และเช่น @SomeGuy ที่กล่าวถึงเพียงโทรsrandom(arc4random())หนึ่งครั้งก่อนที่จะใช้เพื่อให้แน่ใจว่ามีเมล็ดที่แตกต่างกันและสุ่มสำหรับการดำเนินการของแต่ละโปรแกรมของคุณ
brittlewis12

1
ทุกคนสามารถแสดงความคิดเห็นกับ rand () vs arc4random_uniform () ได้ไหม
รูเบนมาร์ติเนซจูเนียร์

16

คำตอบของ @jstnเป็นสิ่งที่ดี แต่ verbose เล็กน้อย 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()

และคลาสจำนวนเต็มอื่น ๆ ทั้งหมดก็โอเค


11

ใน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)

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


6

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

arc4random()ดีกว่ามากและสามารถใช้เพื่อวัตถุประสงค์ส่วนใหญ่ได้ แต่ไม่ควรใช้อีกครั้งเพื่อจุดประสงค์การเข้ารหัส

SecCopyRandomBytes()ถ้าคุณต้องการสิ่งที่รับประกันว่าจะมีความปลอดภัยเข้ารหัสการใช้งาน โปรดทราบว่าหากคุณสร้างตัวสร้างตัวเลขสุ่มเป็นบางสิ่งบางอย่างคนอื่นอาจลงเอยด้วยการใช้งานเพื่อจุดประสงค์ในการเข้ารหัส (เช่นรหัสผ่านการสร้างรหัสหรือการสร้างเกลือ) คุณควรพิจารณาใช้SecCopyRandomBytes()ต่อไปแม้ว่าความต้องการของคุณจะไม่ได้ ไม่ต้องการสิ่งนั้น


6

ตั้งแต่ 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 APIs (พวกเขามีความแตกต่างกันระหว่างแพลตฟอร์ม)

  2. และยิ่งกว่านั้น ...

ถ้าฉันบอกคุณว่าการสุ่มไม่ใช่การสุ่ม

ถ้าคุณใช้arc4random() (ในการคำนวณส่วนที่เหลือ)เช่นarc4random() % aNumberผลที่ไม่ได้กระจายอย่างสม่ำเสมอระหว่างและ0 aNumberมีปัญหาที่เรียกว่าเป็นอคติ Modulo

อคติโมดูโล

โดยปกติการทำงานสร้างเลขสุ่มระหว่าง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


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
Leo Dabus

3

สวิฟท์ 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 () จะคืนค่าเป็นศูนย์ถ้าช่วงว่างดังนั้นคุณจึงแกะ Int ที่คืนกลับมาหรือไม่ ด้วยถ้าปล่อยให้
  3. คุณใช้ random (ใน :) เพื่อสร้าง Double, Float หรือ CGFloat และ Random () เพื่อสุ่ม Bool แบบสุ่ม

เพิ่มเติม @ เป็นทางการ


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

วิธีการเหล่านี้ใช้หมายเลขสุ่มพิเศษเพื่อพิจารณาจำนวนUInt8s ที่จะใช้ในการสร้างหมายเลขสุ่ม บรรทัดสุดท้ายแปลง[UInt8]ไปUInt16หรือUInt32หรือ

ฉันไม่รู้ว่าสองคนสุดท้ายยังนับว่าสุ่มอย่างแท้จริงหรือไม่ แต่คุณสามารถปรับแต่งมันตามความชอบของคุณ :)


คุณหลีกเลี่ยงอคติอย่างชาญฉลาดโดยโมดูโล่, +1 สำหรับสิ่งนั้น คุณอาจเตือนผู้อ่านว่าทำไมคุณถึงทำ
jww

นั่นเป็นเรื่องที่น่าสนใจฉันไม่ได้คิดว่าอคติแบบโมดูโลอาจจะอยู่ที่นี่ บางทีโอกาสที่จะได้จำนวนน้อยอาจไม่เหมือนกับการได้จำนวนมาก
Zyphrax

2

สวิฟท์ 4.2

Swift 4.2 ได้รวม API สุ่มหมายเลขแบบดั้งเดิมและมีคุณสมบัติครบถ้วนในไลบรารีมาตรฐาน ( ข้อเสนอ Swift Evolution SE-0202 )

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

ประเภทตัวเลขทั้งหมดมีสแตติกสุ่ม (เป็น :)ซึ่งใช้ช่วงและส่งกลับหมายเลขสุ่มในช่วงที่กำหนด


1

สวิฟท์ 4.2, 10.1

สำหรับ iOS, MacOS และ tvOS คุณสามารถใช้ทั้งระบบแหล่งข้อมูลที่สุ่มGameKitในกรอบของ Xcode ที่นี่คุณสามารถค้นหา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]
}
randomGenerator()

หรือเพียงแค่ใช้randomElement()วิธีการที่ส่งกลับองค์ประกอบแบบสุ่มของคอลเลกชัน:

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

let randomNumber = number.randomElement()!
print(randomNumber)

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
Fibonacci เป็นอย่างไร
Nikolai Ruhe

สวัสดี Nikolai บล็อกโค้ดนี้เป็นเวอร์ชั่นเก่า Swift 1.2 หากคุณลอง Swift 2.0 ใหม่ มันจะไม่ทำงาน
Durul Dalkanat

2
ฉันเข้าใจ แต่ก็ดูเหมือนว่าเครื่องกำเนิดฟีโบนักชีสำหรับฉันไม่ใช่ตัวเลขสุ่มอย่างที่ถามในคำถาม
Nikolai Ruhe

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
ยินดีต้อนรับสู่ SO ไม่มีคำแนะนำในรหัสเท่านั้นโปรดแก้ไขคำตอบของคุณเพื่ออธิบายสาเหตุที่รหัสนี้ตอบคำถามและวิธีการทำงาน ดู stackoverflow.com/help/how-to-answer สำหรับข้อมูลเพิ่มเติม
ทิมมาโลน

2
โปรดระบุบริบทรอบคำตอบของคุณและยินดีต้อนรับสู่ stackoverflow :)
Jonathan Eustace

0

รายละเอียด

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)

อย่าลืมเพิ่มรหัสคณิตศาสตร์ (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]
}

ผลตัวอย่าง

ป้อนคำอธิบายรูปภาพที่นี่


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