ฉันจะสลับอาร์เรย์ใน Swift ได้อย่างไร


305

ฉันจะสุ่มหรือสุ่มองค์ประกอบภายในอาร์เรย์ใน Swift ได้อย่างไร ตัวอย่างเช่นหากอาร์เรย์ของฉันประกอบด้วยไพ่ 52 ใบฉันต้องการสลับแถวเพื่อสับไพ่


2
นี่ไม่ใช่เฉพาะภาษาใด ๆ เพียงแค่ใช้ขั้นตอนวิธีการสับใด ๆ ...
Gabriele Petronella

8
@ithrandir นั่นไม่ใช่ความจริง array.shuffleในทับทิมหนึ่งจะไปสำหรับ ไม่จำเป็นต้องใช้เวอร์ชันของคุณเอง ฉันเดาว่า OP กำลังมองหาสิ่งที่คล้ายกัน
Linus Oleander

1
ระวังอย่าใช้อัลกอริธึมการสลับเพื่อสับไพ่
njzk2

คำตอบ:


627

คำตอบนี้ให้รายละเอียดวิธีการสลับด้วยอัลกอริธึมที่รวดเร็วและสม่ำเสมอ (Fisher-Yates) ใน Swift 4.2+ และวิธีการเพิ่มฟีเจอร์เดียวกันใน Swift เวอร์ชันก่อนหน้าต่างๆ การตั้งชื่อและลักษณะการทำงานสำหรับ Swift แต่ละเวอร์ชันตรงกับวิธีการเรียงลำดับ mutating และ nonmutating

Swift 4.2+

shuffleและshuffledเป็นภาษาเริ่มต้นของ Swift 4.2 ตัวอย่างการใช้งาน:

let x = [1, 2, 3].shuffled()
// x == [2, 3, 1]

let fiveStrings = stride(from: 0, through: 100, by: 5).map(String.init).shuffled()
// fiveStrings == ["20", "45", "70", "30", ...]

var numbers = [1, 2, 3, 4]
numbers.shuffle()
// numbers == [3, 2, 1, 4]

Swift 4.0 และ 4.1

ส่วนขยายเหล่านี้เพิ่มshuffle()วิธีในการรวบรวมที่ไม่แน่นอน (อาร์เรย์และบัฟเฟอร์ที่ไม่แน่นอนที่ไม่ปลอดภัย) และshuffled()วิธีการเรียงลำดับใด ๆ :

extension MutableCollection {
    /// Shuffles the contents of this collection.
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }

        for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            // Change `Int` in the next line to `IndexDistance` in < Swift 4.1
            let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
            let i = index(firstUnshuffled, offsetBy: d)
            swapAt(firstUnshuffled, i)
        }
    }
}

extension Sequence {
    /// Returns an array with the contents of this sequence, shuffled.
    func shuffled() -> [Element] {
        var result = Array(self)
        result.shuffle()
        return result
    }
}

การใช้งานเช่นเดียวกับในตัวอย่าง Swift 4.2 ด้านบน


สวิฟท์ 3

ส่วนขยายเหล่านี้เพิ่มshuffle()วิธีในการรวบรวมที่ไม่แน่นอนและshuffled()วิธีการในลำดับใด ๆ :

extension MutableCollection where Indices.Iterator.Element == Index {
    /// Shuffles the contents of this collection.
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }

        for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            // Change `Int` in the next line to `IndexDistance` in < Swift 3.2
            let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
            guard d != 0 else { continue }
            let i = index(firstUnshuffled, offsetBy: d)
            self.swapAt(firstUnshuffled, i)
        }
    }
}

extension Sequence {
    /// Returns an array with the contents of this sequence, shuffled.
    func shuffled() -> [Iterator.Element] {
        var result = Array(self)
        result.shuffle()
        return result
    }
}

การใช้งานเช่นเดียวกับในตัวอย่าง Swift 4.2 ด้านบน


สวิฟท์ 2

(ภาษาที่ล้าสมัย: คุณไม่สามารถใช้ Swift 2.x เพื่อเผยแพร่บน iTunes Connect ได้ตั้งแต่เดือนกรกฎาคม 2018)

extension MutableCollectionType where Index == Int {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffleInPlace() {
        // empty and single-element collections don't shuffle
        if count < 2 { return }

        for i in startIndex ..< endIndex - 1 {
            let j = Int(arc4random_uniform(UInt32(count - i))) + i
            guard i != j else { continue }
            swap(&self[i], &self[j])
        }
    }
}

extension CollectionType {
    /// Return a copy of `self` with its elements shuffled.
    func shuffle() -> [Generator.Element] {
        var list = Array(self)
        list.shuffleInPlace()
        return list
    }
}

การใช้งาน:

[1, 2, 3].shuffle()
// [2, 3, 1]

let fiveStrings = 0.stride(through: 100, by: 5).map(String.init).shuffle()
// ["20", "45", "70", "30", ...]

var numbers = [1, 2, 3, 4]
numbers.shuffleInPlace()
// [3, 2, 1, 4]

สวิฟท์ 1.2

(ภาษาที่ล้าสมัย: คุณไม่สามารถใช้ Swift 1.x เพื่อเผยแพร่บน iTunes Connect ได้ตั้งแต่เดือนกรกฎาคม 2018)

shuffle เป็นวิธีการกลายพันธุ์อาเรย์

ส่วนขยายนี้จะช่วยให้คุณสามารถสลับArrayอินสแตนซ์ที่ไม่แน่นอนได้:

extension Array {
    mutating func shuffle() {
        if count < 2 { return }
        for i in 0..<(count - 1) {
            let j = Int(arc4random_uniform(UInt32(count - i))) + i
            swap(&self[i], &self[j])
        }
    }
}
var numbers = [1, 2, 3, 4, 5, 6, 7, 8]
numbers.shuffle()                     // e.g., numbers == [6, 1, 8, 3, 2, 4, 7, 5]

shuffled เป็นวิธีการอาเรย์ที่ไม่กลายพันธุ์

ส่วนขยายนี้จะช่วยให้คุณดึงสำเนาที่สับได้ของArrayอินสแตนซ์:

extension Array {
    func shuffled() -> [T] {
        if count < 2 { return self }
        var list = self
        for i in 0..<(list.count - 1) {
            let j = Int(arc4random_uniform(UInt32(list.count - i))) + i
            swap(&list[i], &list[j])
        }
        return list
    }
}
let numbers = [1, 2, 3, 4, 5, 6, 7, 8]
let mixedup = numbers.shuffled()     // e.g., mixedup == [6, 1, 8, 3, 2, 4, 7, 5]

1
ในกรณีที่คุณต้องการฟังก์ชั่นรุ่นสวิฟท์ใน 1.2 ก็ต้องบิตของการปรับปรุงเป็นcountElementsจะหายไปและมันเปลี่ยนcountตอนนี้ส่งกลับT.Index.Distanceเพื่อให้ตอบสนองความต้องการข้อ จำกัด C.Index.Distance == Intที่จะอยู่ใน รุ่นนี้ควรใช้งานได้: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Airspeed Velocity

2
สิ่งเหล่านี้คือผลลัพธ์ที่แท้จริง - Fisher-Yates ควรคืนค่าการเปลี่ยนแปลงแบบสุ่มที่ไม่เอนเอียงของแหล่งที่มาดังนั้นจึงไม่มีข้อกำหนดว่าองค์ประกอบใดควรย้าย มีคือการรับประกันว่าจะไม่มีการย้ายองค์ประกอบมากกว่าหนึ่งครั้ง แต่บางครั้ง "ย้าย" คือการดัชนีเดียวกัน กรณีที่ง่ายที่สุดคือคิดว่า[1, 2].shuffled()ควรจะกลับมา[2, 1]ทุกครั้งหรือไม่
Nate Cook

1
ฉันเพิ่มif count > 0ที่ด้านบนของฟังก์ชั่นอาเรย์กลายพันธุ์เพื่อป้องกันการรับ "ข้อผิดพลาดร้ายแรง: ไม่สามารถสร้างช่วงที่มีปลาย <เริ่มต้น" เมื่อมันผ่านอาร์เรย์ที่ว่างเปล่า
Carl Smith

3
@Jan: ใช่เพิ่มguard i != j else { continue }ก่อนการแลกเปลี่ยน ฉันยื่นเรดาร์ แต่พฤติกรรมใหม่นั้นมีเจตนา
Nate Cook

3
ที่จริงshuffleInPlaceอาจมีปัญหาหากดัชนีการรวบรวมไม่เริ่มที่ศูนย์เช่นสำหรับการแบ่งส่วนอาร์เรย์ for i in 0..<count - 1 ควรเป็นfor i in startIndex ..< endIndex - 1(และจากนั้นการแปลงเป็น Swift 3 แทบจะไม่สำคัญ)
Martin R

131

แก้ไข: ดังที่ระบุไว้ในคำตอบอื่น ๆ Swift 4.2 ในที่สุดก็เพิ่มการสร้างตัวเลขสุ่มในไลบรารีมาตรฐานพร้อมด้วยการสับอาร์เรย์

อย่างไรก็ตามชุดGKRandom/ GKRandomDistributionใน GameplayKit ยังคงมีประโยชน์กับRandomNumberGeneratorโปรโตคอลใหม่- หากคุณเพิ่มส่วนขยายใน GameplayKit RNGs เพื่อให้สอดคล้องกับโปรโตคอลไลบรารีมาตรฐานใหม่คุณสามารถรับ:

  • RNG ที่ส่งได้ (ที่สามารถสร้างลำดับ "สุ่ม" เมื่อจำเป็นสำหรับการทดสอบ)
  • RNG ที่เสียสละความแข็งแกร่งเพื่อความเร็ว
  • RNG ที่สร้างการแจกแจงแบบไม่สม่ำเสมอ

... และยังคงใช้ประโยชน์จาก API แบบสุ่ม "ดั้งเดิม" ใหม่ที่ดีใน Swift

ส่วนที่เหลือของคำตอบนี้เกี่ยวข้องกับ RNGs และ / หรือการใช้ในคอมไพเลอร์ Swift รุ่นเก่า


มีคำตอบที่ดีอยู่แล้วที่นี่และภาพประกอบที่ดีว่าทำไมการเขียนเพลงของคุณเองอาจทำให้เกิดข้อผิดพลาดได้ถ้าคุณไม่ระวัง

ใน iOS 9, macOS 10.11 และ tvOS 9 (หรือใหม่กว่า) คุณไม่จำเป็นต้องเขียนเอง มีการใช้งานที่มีประสิทธิภาพและถูกต้องของ Fisher-Yatesใน GameplayKit (ซึ่งแม้ว่าชื่อจะไม่ได้มีไว้สำหรับเกมเท่านั้น)

หากคุณต้องการสลับแบบที่ไม่ซ้ำใคร:

let shuffled = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array)

หากคุณต้องการจำลองแบบสุ่มหรือชุดของสับให้เลือกและคัดสรรแหล่งสุ่มเฉพาะ เช่น

let lcg = GKLinearCongruentialRandomSource(seed: mySeedValue)
let shuffled = lcg.arrayByShufflingObjects(in: array)

ใน iOS 10 / MacOS 10.12 / tvOS 10 NSArrayนอกจากนี้ยังมีความสะดวกสบายสำหรับไวยากรณ์สับผ่านทางส่วนขยายบน แน่นอนว่าเป็นเรื่องยุ่งยากเล็กน้อยเมื่อคุณใช้ Swift Array(และสูญเสียประเภทองค์ประกอบเมื่อกลับมาที่ Swift):

let shuffled1 = (array as NSArray).shuffled(using: random) // -> [Any]
let shuffled2 = (array as NSArray).shuffled() // use default random source

แต่มันเป็นเรื่องง่ายที่จะทำเสื้อคลุม Swift ที่รักษาประเภทไว้สำหรับมัน:

extension Array {
    func shuffled(using source: GKRandomSource) -> [Element] {
        return (self as NSArray).shuffled(using: source) as! [Element]
    }
    func shuffled() -> [Element] {
        return (self as NSArray).shuffled() as! [Element]
    }
}
let shuffled3 = array.shuffled(using: random)
let shuffled4 = array.shuffled()

6
ทำให้ฉันสงสัยว่ายูทิลิตี้ที่มีประโยชน์อื่น ๆ สามารถพบได้ใน GameplayKit ที่ฉันไม่เคยสำรวจมาก่อน!
Richard Venable

6
การค้นหากราฟการค้นหาต้นไม้ระบบการปกครอง ... สิ่งต่างๆมากมายที่มีประโยชน์ทั้งในการออกแบบเกมและอื่น ๆ
rickster

5
ใน Swift 3 / iOS 10 สิ่งนี้ได้ถูกเปลี่ยนเป็น:let shuffled = lcg.arrayByShufflingObjects(in: array)
Evan Pon

30

ในSwift 2.0 GameplayKit อาจมาช่วยชีวิต! (รองรับโดยiOS9หรือใหม่กว่า)

import GameplayKit

func shuffle() {
    array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array)
}

5
การนำเข้า GameplayKit เพียงเพื่อให้ได้อาเรย์แบบสับไม่ได้ดูเหมือนความคิดที่ดี
Lope

3
ทำไม? มันเป็นส่วนหนึ่งของระบบไม่ได้เพิ่มไปยังไบนารี
Abizern

3
คุณยังสามารถกำหนดขอบเขตการนำเข้าได้อย่างง่ายดายimport GameplayKit.GKRandomSource
JRG-Developer

26

นี่คือบางสิ่งที่สั้นกว่าเล็กน้อย:

sorted(a) {_, _ in arc4random() % 2 == 0}

1
@moby sortฟังก์ชั่นต้องปิดเพื่อสั่งซื้อองค์ประกอบ การปิดนี้ใช้สองพารามิเตอร์ (elem1, elem2) และต้องส่งคืนจริงถ้าค่าแรกควรปรากฏขึ้นก่อนค่าที่สองและเท็จมิฉะนั้น ถ้าเรากลับบูลีนแบบสุ่มแทน ... จากนั้นเราก็รวมสิ่งทั้งหมด :)
Jean Le Moignan

2
นักคณิตศาสตร์คนใดที่นี่เพื่อยืนยันหรือหักล้างหรือไม่
Jean Le Moignan

9
ดังที่ pjs ชี้ให้เห็นในการตอบสนองต่อคำตอบที่คล้ายกันมาก ๆ สิ่งนี้จะไม่สร้างการกระจายของผลลัพธ์ ใช้สับเปลี่ยน Fisher-Yatesดังแสดงในคำตอบของ Nate Cook
Rob

1
นี่เป็นเคล็ดลับที่ฉลาด แต่มีความสามารถในด้านคุณภาพของการสับเปลี่ยน สำหรับหนึ่งการปิดนี้ควรใช้arc4random_uniform()เพราะในขณะนี้อยู่ภายใต้อคติโมดูโล ประการที่สองผลลัพธ์ขึ้นอยู่กับอัลกอริทึมการเรียงลำดับอย่างมาก (ซึ่งเราไม่รู้จักโดยไม่ได้ดูที่แหล่งที่มา)
Alexander - Reinstate Monica

1
ด้วยวิธีการที่เรียบง่ายกว่านี้ดูเหมือนว่าจะใช้งานได้ดี: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
markiv

7

ใช้อัลกอริทึมของ Nateฉันต้องการดูว่าจะเป็นอย่างไรกับ Swift 2 และส่วนขยายโปรโตคอล

นี่คือสิ่งที่ฉันมาด้วย

extension MutableCollectionType where Self.Index == Int {
    mutating func shuffleInPlace() {
        let c = self.count
        for i in 0..<(c - 1) {
            let j = Int(arc4random_uniform(UInt32(c - i))) + i
            swap(&self[i], &self[j])
        }
    }
}

extension MutableCollectionType where Self.Index == Int {
    func shuffle() -> Self {
        var r = self
        let c = self.count
        for i in 0..<(c - 1) {
            let j = Int(arc4random_uniform(UInt32(c - i))) + i
            swap(&r[i], &r[j])
        }
        return r
    }
}

ทีนี้ใคร ๆMutableCollectionTypeก็สามารถใช้วิธีการเหล่านี้ได้เพราะมันใช้Intเป็นIndex


6

ในกรณีของฉันฉันมีปัญหาบางอย่างในการสลับออบเจ็กต์ใน Array จากนั้นฉันก็เกาหัวของฉันและเดินไปเพื่อสร้างล้อใหม่

// swift 3.0 ready
extension Array {

    func shuffled() -> [Element] {
        var results = [Element]()
        var indexes = (0 ..< count).map { $0 }
        while indexes.count > 0 {
            let indexOfIndexes = Int(arc4random_uniform(UInt32(indexes.count)))
            let index = indexes[indexOfIndexes]
            results.append(self[index])
            indexes.remove(at: indexOfIndexes)
        }
        return results
    }

}

5

นี่เป็นเวอร์ชั่นของการดำเนินการสับเปลี่ยน Fisher-YatesของNateสำหรับSwift 4 (Xcode 9)

extension MutableCollection {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffle() {
        for i in indices.dropLast() {
            let diff = distance(from: i, to: endIndex)
            let j = index(i, offsetBy: numericCast(arc4random_uniform(numericCast(diff))))
            swapAt(i, j)
        }
    }
}

extension Collection {
    /// Return a copy of `self` with its elements shuffled
    func shuffled() -> [Element] {
        var list = Array(self)
        list.shuffle()
        return list
    }
}

การเปลี่ยนแปลงคือ:

  • ข้อ จำกัดIndices.Iterator.Element == Indexนี้เป็นส่วนหนึ่งของCollectionโปรโตคอลและไม่จำเป็นต้องกำหนดไว้ในส่วนขยายอีกต่อไป
  • แลกเปลี่ยนองค์ประกอบจะต้องทำโดยการโทรswapAt()ในการเก็บรวบรวมการเปรียบเทียบSE-0173 MutableCollection.swapAt(_:_:)เพิ่ม
  • ElementIterator.Elementเป็นนามแฝงสำหรับ

3

นี่คือสิ่งที่ฉันใช้:

func newShuffledArray(array:NSArray) -> NSArray {
    var mutableArray = array.mutableCopy() as! NSMutableArray
    var count = mutableArray.count
    if count>1 {
        for var i=count-1;i>0;--i{
            mutableArray.exchangeObjectAtIndex(i, withObjectAtIndex: Int(arc4random_uniform(UInt32(i+1))))
        }
    }
    return mutableArray as NSArray
}

3

Swift 4 สลับองค์ประกอบของอาร์เรย์ในการวนรอบที่ i คืออัตราส่วนการผสม

var cards = [Int]() //Some Array
let i = 4 // is the mixing ratio
func shuffleCards() {
    for _ in 0 ..< cards.count * i {
        let card = cards.remove(at: Int(arc4random_uniform(UInt32(cards.count))))
        cards.insert(card, at: Int(arc4random_uniform(UInt32(cards.count))))
    }
}

หรือด้วยส่วนขยาย Int

func shuffleCards() {
    for _ in 0 ..< cards.count * i {
        let card = cards.remove(at: cards.count.arc4random)
        cards.insert(card, at: cards.count.arc4random)
    }
}
extension Int {
    var arc4random: Int {
        if self > 0 {
            print("Arc for random positiv self \(Int(arc4random_uniform(UInt32(self))))")
        return Int(arc4random_uniform(UInt32(self)))
        } else if self < 0 {
            print("Arc for random negotiv self \(-Int(arc4random_uniform(UInt32(abs(self)))))")
            return -Int(arc4random_uniform(UInt32(abs(self))))
        } else {
            print("Arc for random equal 0")
            return 0
        }
    }
}

2

โซลูชัน Swift 3 ตามคำตอบ @Nate Cook: (ทำงานถ้าดัชนีเริ่มต้นด้วย 0 ดูความคิดเห็นด้านล่าง)

extension Collection {
    /// Return a copy of `self` with its elements shuffled
    func shuffle() -> [Generator.Element] {
        var list = Array(self)
        list.shuffleInPlace()
        return list
    } }

extension MutableCollection where Index == Int {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffleInPlace() {
        // empty and single-element collections don't shuffle
        if count < 2 { return }
        let countInt = count as! Int

    for i in 0..<countInt - 1 {
        let j = Int(arc4random_uniform(UInt32(countInt - i))) + i
            guard i != j else { continue }
            swap(&self[i], &self[j])
        }
    }
}

1
สิ่งนี้อาจผิดพลาดได้หากดัชนีการรวบรวมเริ่มต้นที่ 0 เช่นสำหรับชิ้นส่วนของอาร์เรย์ พยายามวิ่งvar a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()หลายครั้ง - ดูstackoverflow.com/a/37843901/1187415สำหรับวิธีแก้ไขที่ถูกต้อง
Martin R

2

นี่คือวิธีที่ทำได้ในวิธีที่ง่ายที่สุด import Gamplaykitสู่ VC ของคุณและใช้รหัสด้านล่าง ทดสอบใน Xcode 8

 import GameplayKit

 let array: NSArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]

 override func viewDidLoad() {
    super.viewDidLoad()

    print(array.shuffled())  
}

หากคุณต้องการได้รับสตริงสับจากอาเรย์คุณสามารถใช้รหัสด้านล่าง ..

func suffleString() {

    let ShuffleArray = array.shuffled()

    suffleString.text = ShuffleArray.first as? String

    print(suffleString.text!)

}

2

ด้วย Swift 3 หากคุณต้องการสลับอาเรย์เข้ากับตำแหน่งหรือรับอาเรย์แบบสับใหม่จากอาเรย์AnyIteratorสามารถช่วยคุณได้ แนวคิดคือการสร้างอาร์เรย์ของดัชนีจากอาเรย์ของคุณเพื่อสลับดัชนีเหล่านั้นด้วยAnyIteratorอินสแตนซ์และswap(_:_:)ฟังก์ชันและเพื่อแมปองค์ประกอบขององค์ประกอบนี้AnyIteratorอินสแตนซ์กับองค์ประกอบที่เกี่ยวข้องของอาร์เรย์


รหัสสนามเด็กเล่นต่อไปนี้แสดงวิธีการทำงาน:

import Darwin // required for arc4random_uniform

let array = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
var indexArray = Array(array.indices)
var index = indexArray.endIndex

let indexIterator: AnyIterator<Int> = AnyIterator {
    guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
        else { return nil }

    index = nextIndex
    let randomIndex = Int(arc4random_uniform(UInt32(index)))
    if randomIndex != index {
        swap(&indexArray[randomIndex], &indexArray[index])
    }

    return indexArray[index]
}

let newArray = indexIterator.map { array[$0] }
print(newArray) // may print: ["Jock", "Ellie", "Sue Ellen", "JR", "Pamela", "Bobby"]

คุณสามารถ refactor รหัสก่อนหน้าและสร้างshuffled()ฟังก์ชั่นภายในArrayส่วนขยายเพื่อรับอาร์เรย์แบบสับใหม่จากอาร์เรย์:

import Darwin // required for arc4random_uniform

extension Array {

    func shuffled() -> Array<Element> {
        var indexArray = Array<Int>(indices)        
        var index = indexArray.endIndex

        let indexIterator = AnyIterator<Int> {
            guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
                else { return nil }

            index = nextIndex                
            let randomIndex = Int(arc4random_uniform(UInt32(index)))
            if randomIndex != index {
                swap(&indexArray[randomIndex], &indexArray[index])
            }

            return indexArray[index]
        }

        return indexIterator.map { self[$0] }
    }

}

การใช้งาน:

let array = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
let newArray = array.shuffled()
print(newArray) // may print: ["Bobby", "Pamela", "Jock", "Ellie", "JR", "Sue Ellen"]
let emptyArray = [String]()
let newEmptyArray = emptyArray.shuffled()
print(newEmptyArray) // prints: []

เป็นทางเลือกแทนรหัสก่อนหน้าคุณสามารถสร้างshuffle()ฟังก์ชั่นภายในArrayส่วนขยายเพื่อสับเปลี่ยนอาร์เรย์ในสถานที่:

import Darwin // required for arc4random_uniform

extension Array {

    mutating func shuffle() {
        var indexArray = Array<Int>(indices)
        var index = indexArray.endIndex

        let indexIterator = AnyIterator<Int> {
            guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
                else { return nil }

            index = nextIndex                
            let randomIndex = Int(arc4random_uniform(UInt32(index)))
            if randomIndex != index {
                swap(&indexArray[randomIndex], &indexArray[index])
            }

            return indexArray[index]
        }

        self = indexIterator.map { self[$0] }
    }

}

การใช้งาน:

var mutatingArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
mutatingArray.shuffle()
print(mutatingArray) // may print ["Sue Ellen", "Pamela", "Jock", "Ellie", "Bobby", "JR"]

1

คุณสามารถใช้swapฟังก์ชั่นทั่วไปได้เช่นกันและใช้ Fisher-Yates ที่กล่าวถึง:

for idx in 0..<arr.count {
  let rnd = Int(arc4random_uniform(UInt32(idx)))
  if rnd != idx {
    swap(&arr[idx], &arr[rnd])
  }
}

หรือ verbose น้อยกว่า:

for idx in 0..<steps.count {
  swap(&steps[idx], &steps[Int(arc4random_uniform(UInt32(idx)))])
}

2
ทุกข์นี้จากที่อย่างน้อยมากร้ายแรงปิดโดยหนึ่งข้อผิดพลาดอธิบายไว้ที่นี่โดยค่าเสมอสลับจากตำแหน่งเดิม let rnd = Int(arc4random_uniform(UInt32(idx + 1)))นี้แก้ได้ด้วย นอกจากนี้ในปีงบประมาณคุณมักวนซ้ำจากarr.count - 1ลงไปเป็น1(หรือถ้าทำซ้ำจาก0ถึงarr.count - 1คุณเลือกดัชนีเช่นเนทแสดงในคำตอบที่ยอมรับ) ดูส่วนอัลกอริทึมสมัยใหม่ของการสนทนา Fisher-Yates
Rob

1

ทำงาน !! สิ่งมีชีวิตคืออาร์เรย์ที่จะสับเปลี่ยน

extension Array
{
    /** Randomizes the order of an array's elements. */
    mutating func shuffle()
    {
        for _ in 0..<10
        {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}

var organisms = [
    "ant",  "bacteria", "cougar",
    "dog",  "elephant", "firefly",
    "goat", "hedgehog", "iguana"]

print("Original: \(organisms)")

organisms.shuffle()

print("Shuffled: \(organisms)")


0

นี่คือวิธีการสับเปลี่ยนแถวลำดับหนึ่งด้วยเมล็ดพันธุ์ใน Swift 3.0

extension MutableCollection where Indices.Iterator.Element == Index {
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }


        for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            srand48(seedNumber)
            let number:Int = numericCast(unshuffledCount)
            let r = floor(drand48() * Double(number))

            let d: IndexDistance = numericCast(Int(r))
            guard d != 0 else { continue }
            let i = index(firstUnshuffled, offsetBy: d)
            swap(&self[firstUnshuffled], &self[i])
        }
    }
}


0

นี่คือสิ่งที่ฉันใช้:

import GameplayKit

extension Collection {
    func shuffled() -> [Iterator.Element] {
        let shuffledArray = (self as? NSArray)?.shuffled()
        let outputArray = shuffledArray as? [Iterator.Element]
        return outputArray ?? []
    }
    mutating func shuffle() {
        if let selfShuffled = self.shuffled() as? Self {
            self = selfShuffled
        }
    }
}

// Usage example:

var numbers = [1,2,3,4,5]
numbers.shuffle()

print(numbers) // output example: [2, 3, 5, 4, 1]

print([10, "hi", 9.0].shuffled()) // output example: [hi, 10, 9]

0

ตัวอย่างง่ายๆ:

extension Array {
    mutating func shuffled() {
        for _ in self {
            // generate random indexes that will be swapped
            var (a, b) = (Int(arc4random_uniform(UInt32(self.count - 1))), Int(arc4random_uniform(UInt32(self.count - 1))))
            if a == b { // if the same indexes are generated swap the first and last
                a = 0
                b = self.count - 1
            }
            swap(&self[a], &self[b])
        }
    }
}

var array = [1,2,3,4,5,6,7,8,9,10]
array.shuffled()
print(array) // [9, 8, 3, 5, 7, 6, 4, 2, 1, 10]

0

Working Array Extension (การกลายพันธุ์ & การไม่กลายพันธุ์)

Swift 4.1 / Xcode 9

คำตอบยอดนิยมเลิกใช้แล้วดังนั้นฉันจึงใช้เวลากับตัวเองในการสร้างส่วนขยายของตัวเองเพื่อสับเปลี่ยนอาร์เรย์ใน Swift, Swift 4.1 (Xcode 9) เวอร์ชั่นใหม่ล่าสุด:

extension Array {

// Non-mutating shuffle
    var shuffled : Array {
        let totalCount : Int = self.count
        var shuffledArray : Array = []
        var count : Int = totalCount
        var tempArray : Array = self
        for _ in 0..<totalCount {
            let randomIndex : Int = Int(arc4random_uniform(UInt32(count)))
            let randomElement : Element = tempArray.remove(at: randomIndex)
            shuffledArray.append(randomElement)
            count -= 1
        }
        return shuffledArray
    }

// Mutating shuffle
    mutating func shuffle() {
        let totalCount : Int = self.count
        var shuffledArray : Array = []
        var count : Int = totalCount
        var tempArray : Array = self
        for _ in 0..<totalCount {
            let randomIndex : Int = Int(arc4random_uniform(UInt32(count)))
            let randomElement : Element = tempArray.remove(at: randomIndex)
            shuffledArray.append(randomElement)
            count -= 1
        }
        self = shuffledArray
    }
}

โทรแบบสุ่มไม่เปลี่ยนแปลง[Array] -> [Array]:

let array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]

print(array.shuffled)

สิ่งนี้จะพิมพ์arrayตามลำดับแบบสุ่ม


โทรสลับกลายพันธุ์[Array] = [Array]:

var array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]

array.shuffle() 
// The array has now been mutated and contains all of its initial 
// values, but in a randomized shuffled order

print(array) 

สิ่งนี้พิมพ์arrayตามลำดับปัจจุบันซึ่งมีการสับแบบสุ่มแล้ว


หวังว่ามันจะใช้ได้ผลกับทุกคนหากคุณมีคำถามข้อเสนอแนะหรือความคิดเห็นโปรดถาม!


0

ใน SWIFT 4

func createShuffledSequenceOfNumbers(max:UInt)->[UInt] {

    var array:[UInt]! = []
    var myArray:[UInt]! = []
    for i in 1...max {
        myArray.append(i)
    }
    for i in 1...max {
        array.append(i)
    }
    var tempArray:[Int]! = []
    for index in 0...(myArray.count - 1) {

        var isNotFinded:Bool = true
        while(isNotFinded){

            let randomNumber = arc4random_uniform(UInt32(myArray.count))
            let randomIndex = Int(randomNumber)

            if(!tempArray.contains(randomIndex)){
                tempArray.append(randomIndex)

                array[randomIndex] = myArray[index]
                isNotFinded = false
            }
        }
    }

    return array
}

0

หากคุณต้องการใช้ฟังก์ชั่นวนรอบ Swift For แบบง่ายให้ใช้สิ่งนี้ ->

var arrayItems = ["A1", "B2", "C3", "D4", "E5", "F6", "G7", "H8", "X9", "Y10", "Z11"]
var shuffledArray = [String]()

for i in 0..<arrayItems.count
{
    let randomObject = Int(arc4random_uniform(UInt32(items.count)))

    shuffledArray.append(items[randomObject])

    items.remove(at: randomObject)
}

print(shuffledArray)

Swift Array ใช้นามสกุล ->

extension Array {
    // Order Randomize
    mutating func shuffle() {
        for _ in 0..<count {
            sort { (_,_) in arc4random() < arc4random() }
        }
    }
}

0

ตั้งแต่ Swift 4.2 มีฟังก์ชั่นที่ใช้งานง่ายสองอย่าง:

// shuffles the array in place
myArray.shuffle()

และ

// generates a new array with shuffled elements of the old array
let newArray = myArray.shuffled()

-2

นี่คือรหัสบางส่วนที่ทำงานในสนามเด็กเล่น คุณไม่จำเป็นต้องนำเข้าดาร์วินในโครงการ Xcode จริง

import darwin

var a = [1,2,3,4,5,6,7]

func shuffle<ItemType>(item1: ItemType, item2: ItemType) -> Bool {
    return drand48() > 0.5
}

sort(a, shuffle)

println(a)

7
สิ่งนี้ทำให้การกระจายของผลลัพธ์ไม่สม่ำเสมอ นอกจากนี้ยังจะเป็น O (n log n) ซึ่งการสับเปลี่ยน Fisher-Yatesจะให้ผลลัพธ์ที่กระจายอย่างสม่ำเสมอในเวลา O (n)
pjs

นอกจากนี้ยังdrand48()ให้ตัวเลขสุ่มหลอกเดียวกันทุกครั้งเว้นแต่ว่าคุณจะตั้งค่าเมล็ดด้วยเช่นsrand48(Int(arc4random()))
Kametrixom

-3

มันหยุดที่ "swap (& self [i], & self [j])" เมื่อฉันอัพเกรดรุ่น xCode เป็น 7.4 beta
ข้อผิดพลาดร้ายแรง: ไม่สนับสนุนการสลับตำแหน่งด้วยตัวเอง

ฉันพบเหตุผลที่ i = j (ฟังก์ชั่นการสลับจะเกิดการระเบิด)

ดังนั้นฉันจึงเพิ่มเงื่อนไขดังนี้

if (i != j){
    swap(&list[i], &list[j])
}

YA! มันโอเคสำหรับฉัน.


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