Làm cách nào để xáo trộn một mảng trong Swift?


305

Làm cách nào để tôi chọn ngẫu nhiên hoặc xáo trộn các thành phần trong một mảng trong Swift? Ví dụ: nếu mảng của tôi bao gồm 52 thẻ chơi, tôi muốn xáo trộn mảng để xáo trộn bộ bài.


2
Điều này không cụ thể đối với bất kỳ ngôn ngữ. Chỉ cần áp dụng bất kỳ thuật toán xáo trộn nào ...
Gabriele Petronella

8
@Mithrandir Điều đó không đúng. Trong Ruby người ta sẽ đi array.shuffle. Không cần thực hiện phiên bản của riêng bạn. Tôi đoán OP đang tìm kiếm một cái gì đó tương tự.
Linus Oleander

1
tuy nhiên, hãy cẩn thận, không sử dụng bất kỳ thuật toán xáo trộn nào để xáo trộn bộ bài.
njzk2

Câu trả lời:


627

Câu trả lời này chi tiết cách xáo trộn với thuật toán nhanh và đồng nhất (Fisher-Yates) trong Swift 4.2+ và cách thêm tính năng tương tự trong các phiên bản trước của Swift. Việc đặt tên và hành vi cho mỗi phiên bản Swift phù hợp với các phương pháp sắp xếp đột biến và không biến đổi cho phiên bản đó.

Swift 4.2+

shuffleshuffledlà bản gốc bắt đầu Swift 4.2. Ví dụ sử dụng:

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 và 4.1

Các tiện ích mở rộng này thêm một shuffle()phương thức vào bất kỳ bộ sưu tập có thể thay đổi nào (mảng và bộ đệm có thể thay đổi không an toàn) và một shuffled()phương thức cho bất kỳ chuỗi nào:

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

Cách sử dụng tương tự như trong ví dụ Swift 4.2 ở trên.


Swift 3

Các tiện ích mở rộng này thêm một shuffle()phương thức vào bất kỳ bộ sưu tập có thể thay đổi nào và một shuffled()phương thức cho bất kỳ chuỗi nào:

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

Cách sử dụng tương tự như trong ví dụ Swift 4.2 ở trên.


Swift 2

(ngôn ngữ lỗi thời: bạn không thể sử dụng Swift 2.x để xuất bản trên iTunes Connect bắt đầu từ tháng 7 năm 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
    }
}

Sử dụng:

[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]

Swift 1.2

(ngôn ngữ lỗi thời: bạn không thể sử dụng Swift 1.x để xuất bản trên iTunes Connect bắt đầu từ tháng 7 năm 2018)

shuffle như một phương pháp mảng đột biến

Tiện ích mở rộng này sẽ cho phép bạn xáo trộn một thể hiện có thể thay đổi Arraytại chỗ:

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 như một phương pháp mảng không biến đổi

Tiện ích mở rộng này sẽ cho phép bạn truy xuất một bản sao được xáo trộn 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
Trong trường hợp bạn muốn phiên bản chức năng trong Swift 1.2, nó cần một chút cập nhật khi countElementskhông còn nữa, và nó sẽ thay thế count, bây giờ trả về một T.Index.Distanceràng buộc cần phải được bật C.Index.Distance == Int. Phiên bản này sẽ hoạt động: gist.github.com/airspeedswift/03d07a9dc86fabdc370f
Airspeed Velocity

2
Đó là những kết quả đầu ra thực tế, các bộ ba Fisher-Yates sẽ trả về một hoán vị ngẫu nhiên không thiên vị của nguồn, do đó không có yêu cầu nào về một yếu tố cụ thể phải di chuyển. Có một sự đảm bảo rằng không có động thái yếu tố nhiều hơn một lần, nhưng đôi khi "di chuyển" là để chỉ số tương tự. Trường hợp đơn giản nhất là nghĩ về tinh [1, 2].shuffled()ranh mà trở lại [2, 1]mỗi lần?
Nate Cook

1
Tôi đã thêm if count > 0ở đầu hàm mảng đột biến, để tránh nhận "lỗi nghiêm trọng: Không thể tạo Phạm vi với kết thúc <start" khi nó được truyền vào một mảng trống.
Carl Smith

3
@Jan: Có, thêm guard i != j else { continue }trước khi trao đổi. Tôi đã nộp một radar, nhưng hành vi mới là cố ý.
Nate Cook

3
Thực tế shuffleInPlacecó thể sụp đổ nếu các chỉ số bộ sưu tập không bắt đầu từ 0, ví dụ cho một lát mảng. for i in 0..<count - 1 nên for i in startIndex ..< endIndex - 1(và sau đó việc chuyển đổi sang Swift 3 trở nên gần như không đáng kể).
Martin R

131

Chỉnh sửa: Như đã lưu ý trong các câu trả lời khác, Swift 4.2 cuối cùng đã thêm việc tạo số ngẫu nhiên vào thư viện chuẩn, hoàn thành với việc xáo trộn mảng.

Tuy nhiên, GKRandom/ GKRandomDistributionbộ trong GameplayKit vẫn có thể hữu ích với RandomNumberGeneratorgiao thức mới - nếu bạn thêm các tiện ích mở rộng cho GameplayKit RNG để tuân thủ giao thức thư viện tiêu chuẩn mới, bạn có thể dễ dàng nhận được:

  • RNG có thể gửi được (có thể tái tạo chuỗi "ngẫu nhiên" khi cần để thử nghiệm)
  • RNG hy sinh sự mạnh mẽ cho tốc độ
  • RNG sản xuất phân phối không đồng đều

... và vẫn sử dụng các API ngẫu nhiên "gốc" mới trong Swift.

Phần còn lại của câu trả lời này liên quan đến các RNG như vậy và / hoặc việc sử dụng chúng trong các trình biên dịch Swift cũ hơn.


Đã có một số câu trả lời tốt ở đây, cũng như một số minh họa tốt về lý do tại sao việc viết shuffle của riêng bạn có thể dễ bị lỗi nếu bạn không cẩn thận.

Trong iOS 9, macOS 10.11 và tvOS 9 (hoặc mới hơn), bạn không phải tự viết. Có một triển khai hiệu quả, chính xác của Fisher-Yates trong GameplayKit (mặc dù tên này không chỉ dành cho trò chơi).

Nếu bạn chỉ muốn một shuffle độc ​​đáo:

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

Nếu bạn muốn có thể sao chép một xáo trộn hoặc một loạt các xáo trộn, hãy chọn và chọn một nguồn ngẫu nhiên cụ thể; ví dụ

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

Trong iOS 10 / macOS 10.12 / tvOS 10, cũng có một cú pháp tiện lợi để xáo trộn thông qua một tiện ích mở rộng trên NSArray. Tất nhiên, điều đó hơi cồng kềnh khi bạn đang sử dụng Swift Array(và nó mất loại phần tử khi quay lại Swift):

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

Nhưng thật dễ dàng để tạo một trình bao bọc Swift bảo quản kiểu cho nó:

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
Làm cho tôi tự hỏi những tiện ích hữu ích khác có thể được tìm thấy trong GameplayKit mà tôi chưa bao giờ khám phá!
Richard Venable

6
Tìm kiếm đồ thị, tìm kiếm cây, hệ thống quy tắc ... rất nhiều thứ hữu ích cả trong thiết kế trò chơi và mặt khác.
gà trống

5
Trong Swift 3 / iOS 10, điều này đã được thay đổi thành:let shuffled = lcg.arrayByShufflingObjects(in: array)
Evan Pon

30

Trong Swift 2.0 , GameplayKit có thể đến giải cứu! (được hỗ trợ bởi iOS9 trở lên)

import GameplayKit

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

5
nhập GameplayKit chỉ để lấy mảng bị xáo trộn nghe có vẻ không phải là một ý tưởng tuyệt vời
Lope

3
Tại sao? Đó là một phần của hệ thống, không thêm vào nhị phân.
Abizern

3
Bạn cũng có thể phạm vi nhập để đơn giảnimport GameplayKit.GKRandomSource
JRG-Developer

26

Đây là một cái gì đó có thể ngắn hơn một chút:

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

1
@moby sortHàm cần đóng để sắp xếp các phần tử. Việc đóng này lấy hai tham số (elem1, elem2) và phải trả về true nếu giá trị đầu tiên xuất hiện trước giá trị thứ hai và ngược lại là sai. Nếu chúng ta trả lại một boolean ngẫu nhiên thay vào đó ... thì chúng ta chỉ cần trộn lẫn tất cả mọi thứ :)
Jean Le Moignan

2
Bất kỳ nhà toán học ở đây để xác nhận hoặc từ chối?
Jean Le Moignan

9
Như pjs đã chỉ ra để đáp lại một câu trả lời rất giống nhau, điều này sẽ không tạo ra sự phân phối kết quả thống nhất. Sử dụng Shuffle Fisher-Yates như trong câu trả lời của Nate Cook.
Cướp

1
Đây là một mẹo thông minh, nhưng rất tệ về chất lượng của shuffle. Đối với một, đóng cửa này nên sử dụng arc4random_uniform(), vì nó hiện đang chịu sự thiên vị modulo. Thứ hai, đầu ra phụ thuộc rất nhiều vào thuật toán sắp xếp (chúng ta không biết đến nguồn mà không nhìn vào nguồn).
Alexander - Tái lập lại

1
Tiếp tục với cách tiếp cận đơn giản hơn này, điều này dường như hoạt động khá độc đáo: collection.sorted { _,_ in arc4random_uniform(1) == 0 }
markiv

7

Sử dụng thuật toán của Nate, tôi muốn xem giao diện của Swift 2 và giao thức.

Đây là những gì tôi đã đưa ra.

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

Bây giờ, bất kỳ MutableCollectionTypecó thể sử dụng các phương thức được cung cấp mà nó sử dụng Intnhư là mộtIndex


6

Trong trường hợp của tôi, tôi đã gặp một số vấn đề trong việc hoán đổi các đối tượng trong Array. Sau đó, tôi gãi đầu và đi phát minh lại bánh xe.

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

Đây là phiên bản triển khai của Shate trong chương trình xáo trộn Fisher-Yates cho 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
    }
}

Những thay đổi là:

  • Ràng buộc Indices.Iterator.Element == Indexbây giờ là một phần của Collectiongiao thức và không cần phải áp đặt cho phần mở rộng nữa.
  • Các yếu tố trao đổi phải được thực hiện bằng cách gọi swapAt()vào bộ sưu tập, so sánh SE-0173 AddMutableCollection.swapAt(_:_:) .
  • Elementlà một bí danh cho Iterator.Element.

3

Đây là những gì tôi sử dụng:

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 Xáo trộn các phần tử của một mảng trong một vòng lặp for trong đó i là tỷ lệ pha trộn

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

Hoặc với phần mở rộng 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

Giải pháp Swift 3, theo câu trả lời @Nate Cook: (hoạt động nếu chỉ số bắt đầu bằng 0, xem bình luận bên dưới)

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
Điều này có thể sập nếu các chỉ số bộ sưu tập bắt đầu từ 0, ví dụ cho một lát mảng. Cố gắng chạy var a = [1, 2, 3, 4, 5, 6][3..<6]; a.shuffleInPlace()nhiều lần. - Xem stackoverflow.com/a/37843901/1187415 để biết giải pháp chính xác.
Martin R

2

Đây là cách nó được thực hiện một cách đơn giản nhất. import Gamplaykitvào VC của bạn và sử dụng mã dưới đây. Đã thử nghiệm trong Xcode 8.

 import GameplayKit

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

 override func viewDidLoad() {
    super.viewDidLoad()

    print(array.shuffled())  
}

Nếu bạn muốn lấy Chuỗi xáo trộn từ Mảng, bạn có thể sử dụng mã bên dưới ..

func suffleString() {

    let ShuffleArray = array.shuffled()

    suffleString.text = ShuffleArray.first as? String

    print(suffleString.text!)

}

2

Với Swift 3, nếu bạn muốn xáo trộn một mảng tại chỗ hoặc lấy một mảng được xáo trộn mới từ một mảng, AnyIteratorcó thể giúp bạn. Ý tưởng là tạo ra một mảng các chỉ mục từ mảng của bạn, để xáo trộn các chỉ mục đó với một AnyIteratorthể hiện và swap(_:_:)hàm và ánh xạ từng phần tử của thể hiện này AnyIteratorvới phần tử tương ứng của mảng.


Mã Playground sau đây cho thấy cách thức hoạt động của nó:

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"]

Bạn có thể cấu trúc lại mã trước đó và tạo một shuffled()hàm bên trong một Arraytiện ích mở rộng để có được một mảng được xáo trộn mới từ một mảng:

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

}

Sử dụng:

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: []

Thay thế cho mã trước đó, bạn có thể tạo một shuffle()hàm bên trong Arraytiện ích mở rộng để xáo trộn một mảng tại chỗ:

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

}

Sử dụng:

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

1

Bạn cũng có thể sử dụng swapchức năng chung và triển khai Fisher-Yates đã đề cập:

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

hoặc ít dài dòng hơn:

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

2
Ít nhất, điều này phải chịu một lỗi nghiêm trọng do một lỗi được mô tả ở đây, theo đó một giá trị luôn được hoán đổi từ vị trí ban đầu của nó. Điều này được khắc phục với let rnd = Int(arc4random_uniform(UInt32(idx + 1))). Ngoài ra, trong FY, bạn thường lặp đi lặp lại từ arr.count - 1xuống 1(hoặc nếu bạn lặp từ 0đến arr.count - 1, bạn chọn chỉ số như Nate hiển thị trong câu trả lời được chấp nhận). Xem phần Thuật toán hiện đại của cuộc thảo luận về Fisher-Yates.
Cướp

1

làm!!. sinh vật là mảng để xáo trộn.

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

Đây là cách xáo trộn một mảng với một hạt giống trong 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
let shuffl = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: arrayObject)

0

Đây là những gì tôi sử dụng:

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

Ví dụ đơn giản:

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

Mở rộng mảng làm việc (đột biến & không biến đổi)

Swift 4.1 / Xcode 9

Câu trả lời hàng đầu không được chấp nhận, vì vậy tôi đã tự mình lấy nó để tạo tiện ích mở rộng của riêng mình để xáo trộn một mảng trong phiên bản mới nhất của 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
    }
}

Gọi Shuffle không đột biến [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)

Điều này in arraytheo thứ tự ngẫu nhiên.


Gọi đột biến ngẫu nhiên [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) 

Điều này in arraytheo thứ tự hiện tại của nó, đã được xáo trộn ngẫu nhiên.


Hy vọng điều này sẽ làm việc cho tất cả mọi người, nếu bạn có bất kỳ câu hỏi, đề xuất hoặc nhận xét nào, vui lòng hỏi!


0

Trong 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

Nếu bạn muốn sử dụng chức năng vòng lặp Swift For đơn giản, hãy sử dụng chức năng này ->

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 hậu tố sử dụng tiện ích mở rộng ->

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

0

Kể từ swift 4.2, có hai chức năng tiện dụng:

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

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

-2

Đây là một số mã chạy trong sân chơi. Bạn sẽ không cần nhập Darwin trong một dự án Xcode thực tế.

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
Điều này cung cấp một phân phối không thống nhất của các kết quả. Nó cũng sẽ là O (n log n), trong đó một shuffle Fisher-Yates sẽ cho kết quả phân phối đồng đều trong thời gian O (n).
pjs

Cũng drand48()cung cấp các số ngẫu nhiên giả giống nhau mọi lúc, trừ khi bạn đặt hạt giống vớisrand48(Int(arc4random()))
Kametrixom

-3

Nó dừng ở "hoán đổi (& self [i], & self [j])" khi tôi nâng cấp phiên bản xCode lên 7.4 beta.
lỗi nghiêm trọng: hoán đổi vị trí với chính nó không được hỗ trợ

Tôi tìm thấy lý do i = j (chức năng trao đổi sẽ bùng nổ)

Vì vậy, tôi thêm một điều kiện như dưới đây

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

YA! Nó ổn với tôi.


Đây dường như là một nhận xét về câu trả lời của Chris , không phải là một câu trả lời cho câu hỏi ban đầu.
Mogsdad
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.