Swiftで配列内の要素をランダム化またはシャッフルするにはどうすればよいですか?たとえば、私の配列が52枚のトランプカードで構成されている場合、私はデッキをシャッフルするためにシャッフルしたいです。
この回答では、Swift 4.2以降で高速かつ一様なアルゴリズム(Fisher-Yates)を使ってシャッフルする方法と、以前のさまざまなバージョンのSwiftに同じ機能を追加する方法について詳しく説明します。各Swiftバージョンの命名と動作は、そのバージョンの変更ソート方法と非変更ソート方法に一致します。
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]
これらの拡張機能は、すべての可変コレクション(配列と安全でない可変バッファ)に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の例と同じ使い方。
これらの拡張は、可変コレクションに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の例と同じ使い方。
(時代遅れの言語:2018年7月以降、iTunes ConnectでSwift 2.xを使用して公開することはできません)
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]
(時代遅れの言語:2018年7月以降、iTunes ConnectでSwift 1.xを発行することはできません)
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]
編集:他の答えで述べたように、Swift 4.2 最後に は標準ライブラリに乱数生成を追加します。配列シャッフリングを完了します。
ただし、GameplayKitのGKRandom
/GKRandomDistribution
スイートは、新しいRandomNumberGenerator
プロトコルでも便利です。新しい標準ライブラリプロトコルに準拠するようにGameplayKit RNGに拡張機能を追加すると、簡単に入手できます。
...それでも、SwiftのNiceの新しい「ネイティブな」ランダムAPIを利用してください。
この答えの残りの部分は、そのようなRNGおよび/または古いSwiftコンパイラでのそれらの使用に関するものです。
ここにはすでにいくつかの良い答えがあります。また、注意しないと、なぜ自分のシャッフルを書くのが間違いを起こしやすいのかを示す良いイラストもいくつかあります。
IOS 9、macOS 10.11、およびtvOS 9(またはそれ以降)では、自分で書く必要はありません。 GameplayKitには 効率的で正しいFisher-Yatesの実装 があります(これは名前にもかかわらず、ゲーム専用ではありません)。
あなただけのユニークなシャッフルが欲しいなら:
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()
Swift 2.0では、GameplayKitが役に立ちます。 (iOS9以降でサポート)
import GameplayKit
func shuffle() {
array = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(array)
}
これはもう少し短いかもしれません:
sorted(a) {_, _ in arc4random() % 2 == 0}
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
でもこれらのメソッドを使用できます。
私の場合は、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
}
}
これはSwift 4(Xcodeのための NateのFisher-Yatesシャッフル の実装のバージョンです。 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()
を呼び出す必要があります。compare SE-0173 MutableCollection.swapAt(_:_:)
を追加します。Element
はIterator.Element
のエイリアスです。これは私が使っているものです:
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
}
Swift 4forループで配列の要素をシャッフルします。ここで、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
}
}
}
@Nate Cookの答えに従った、Swift 3の解決策:(インデックスが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])
}
}
}
これが最も簡単な方法で行われます。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!)
}
Wikipedia でFisher – Yatesシャッフルの記事を作成する
a)。鉛筆と紙の方法:
func shuffle<T>(_ array:inout [T]){
var temp = [T]()
for _ in array{
/*Generating random number with length*/
let random = arc4random_uniform(UInt32(array.count))
/*Take the element from array*/
let elementTaken = array[Int(random)]
/*Append it to new tempArray*/
temp.append(elementTaken)
/*Remove the element from array*/
array.remove(at: Int(random))
}
/* array = tempArray*/
array = temp
}
b)。現代的な方法:(Durstenfeldのバージョン)
func shuffle<T>(_ array:inout [T]){
var length = array.count
for _ in array{
/*Generating random number with length*/
let random = arc4random_uniform(UInt32(length))
/*Check before index of two elements not same*/
if length-1 != Int(random){
swap(&array[length-1], &array[Int(random)])
}
length -= 1
}
}
Extension:
a)。鉛筆と紙の方法:
extension Array{
mutating func shuffled(){
var temp = [Element]()
for _ in self{
/*Generating random number with length*/
let random = arc4random_uniform(UInt32(self.count))
/*Take the element from array*/
let elementTaken = self[Int(random)]
/*Append it to new tempArray*/
temp.append(elementTaken)
/*Remove the element from array*/
self.remove(at: Int(random))
}
/* array = tempArray*/
self = temp
}
}
b)。現代的な方法:(Durstenfeldのバージョン)
extension Array{
mutating func shuffled(){
var length = self.count
for _ in self{
/*Generating random number with length*/
let random = arc4random_uniform(UInt32(length))
/*Check before index of two elements not same*/
if length-1 != Int(random){
/*Swaping elements, If same index then there is no swap*/
// swap(&self[length-1], &self[Int(random)]) -> Swift 3.0
self.swapAt(length-1, Int(random)) //-> Swift 4.0
}
length -= 1
}
}
}
参照:
/* By using shuffle functions*/
var a = [1,2,3,4,5,6,7,8,9,10]
for _ in 1...10{
self.shuffle(&a)
/*For shuffled extension, a.shuffled()*/
print(a)
}
注:空の配列も使用できます。
出力:
[6、2、10、5、1、8、9、4、3、7]
[7、1、9、8、2、10、5、6、4、3]
[8、9、6、10、5、2、7、4、3、1]
[10、1、7、4、8、9、3、5、2、6]
[8、1、6、9、3、7、4、5、10、2]
[4、3、7、9、1、5、8、6、10、2]
[7、3、4、9、10、1、6、5、2、8]
[3、6、2、4、5、8、9、7、1、10]
[5、1、2、10、6、9、7、3、8、4]
[7、9、3、8、2、1、5、4、6、10]
他のSwiftバージョンが間もなく確認される場合は、私にお知らせください。
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"]
配列から新しいシャッフルされた配列を取得するために、前のコードをリファクタリングしてArray
拡張子の中にshuffled()
関数を作成することができます。
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: []
前のコードの代わりに、配列を適切にシャッフルするためにArray
拡張子の中にshuffle()
関数を作成することができます。
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"]
Swift 4.2では、 可変shuffle
と 不変shuffled
の両方のメソッドがあります。乱数生成と配列のもの についてはこちら でもっと読むことができます。
あなたは一般的なswap
関数を同様に使用して、言及されたFisher-Yatesを実装することができます:
for idx in 0..<arr.count {
let rnd = Int(arc4random_uniform(UInt32(idx)))
if rnd != idx {
swap(&arr[idx], &arr[rnd])
}
}
詳細以下
for idx in 0..<steps.count {
swap(&steps[idx], &steps[Int(arc4random_uniform(UInt32(idx)))])
}
動作します!有機体はシャッフルするための配列です。
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)")
一番の答えは廃止予定なので、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
を出力します。これはすでにランダムにシャッフルされています。
これがすべての人に役立つことを願っています、質問、提案、またはコメントがあれば、遠慮なく聞いてください!
単純な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() }
}
}
}
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
}
これは、Swift 3.0で1つの配列をシードでシャッフルする方法です。
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])
}
}
}
これは私が使っているものです:
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]
let shuffl = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: arrayObject)
Swift 4.2では、2つの便利な機能があります。
// shuffles the array in place
myArray.shuffle()
そして
// generates a new array with shuffled elements of the old array
let newArray = myArray.shuffled()
簡単な例:
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]