🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

3回答

3471閲覧

swift - 重複しない整数で配列を作る方法

sandalwalk

総合スコア77

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2017/06/15 23:33

swift3で、(例えば)10個の異なる整数で構成される配列を作りたいのですが、上手く行きません。以下の様な方法でできると考えたのですがやはり重複してしまいます。どの様にすれば同じ1〜10の整数を使い、重複しない整数で構成される配列を作れるでしょうか。

let Length =10 var integerArray = Array(repeating: 0, count: Length) integerArray[0] = Int(arc4random_uniform(UInt32(Length))) for i in 1...Length-1 { for n in 0...i-1 { repeat{ integerArray[i] = Int(arc4random_uniform(UInt32(Length))) }while(integerArray[n]==integerArray[i]) } }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fuzzball

2017/06/16 00:25

アルゴリズム的な話であれば「アルゴリズム」タグを付けた方が良いかと。また、「乱数 重複」で検索すれば同様の質問が見つかりますよ。
guest

回答3

0

ベストアンサー

コードの方法ではintegerArray[i]が0~i-1の範囲の数と同じにならないことを確認しようとしていますが、repeatの度に乱数を設定しているためn=k(0<k<i-1)時点でせっかっく0~k-1まで重複していないことを確認したとしてもk回目でそれを無視して新たな乱数を代入してしまうことにより「0~k-1までと重複していない」条件が失われてしまいます。

今の論理を生かすなら

iを1~9までループ { while (0~i-1までの中にintegerArray[i]と同じ値が存在する) { integerArray[i]へ新たな乱数を設定 } }

としなければうまくいきません。


ちなみに、質問者さんの方法では乱数の出目の具合によっては何度もリトライする必要があり、後半になればなるほど「重複した値になる確率が高くなるため」非常に遅くなります。

普通は、1~10までを配列へ設定しておき、各要素に対して、順番に「自分以降の要素のいずれか(自分も含む)と交換する」といった方法を使います。そうすることで9回の繰り返しで必ず完了します。

投稿2017/06/16 00:26

編集2017/06/16 00:35
KSwordOfHaste

総合スコア18402

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2017/06/16 00:35 編集

おっと失礼「0~9までを配列へ設定しておき」は「1~10」ですね。回答を編集しました
sandalwalk

2017/06/16 05:03

勉強になりました。
guest

0

swift

1let Length = 10 2var integerArray = Array(repeating: 0, count: Length) 3for i in 0..<Length { 4 integerArray[i] = i+1 5} 6print(integerArray) 7//=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

質問文中に「乱数」って書かれていないので。

真面目に

swift

1let Length = 10 2let array = [(Int, UInt32)](repeating: (0, 0), count: Length) 3 4//1~10に乱数で重みを付ける 5let weighted = array.enumerated().map {($0.0+1, arc4random_uniform(UInt32.max))} 6print(weighted) 7//=> [(1, 2394627293), (2, 3231339695), (3, 1429293380), (4, 2040053551), (5, 1055190483), (6, 2379829461), (7, 1949144040), (8, 522764666), (9, 1567954026), (10, 1966320976)] 8 9//重みでソートする 10let sorted = weighted.sorted {$0.1 > $1.1} 11print(sorted) 12//=> [(2, 3231339695), (1, 2394627293), (6, 2379829461), (4, 2040053551), (10, 1966320976), (7, 1949144040), (9, 1567954026), (3, 1429293380), (5, 1055190483), (8, 522764666)] 13 14//ソート結果から1~10を取り出す 15let integerArray = sorted.map {$0.0} 16print(integerArray) 17//=> [2, 1, 6, 4, 10, 7, 9, 3, 5, 8]

まとめると、

swift

1let integerArray = [(Int, UInt32)](repeating: (0, 0), count: 10) 2 .enumerated().map {($0.0+1, arc4random_uniform(UInt32.max))} 3 .sorted {$0.1 > $1.1} 4 .map {$0.0} 5print(integerArray) 6//=> [6, 3, 9, 8, 5, 10, 7, 1, 2, 4]

投稿2017/06/16 00:29

編集2017/06/16 05:53
fuzzball

総合スコア16733

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2017/06/16 00:47

これは手厳しい・・・た、確かにそうですがw;
guest

0

理由:
常にiの位置は変わらないので、常にi番目と同じかどうか
しか比較せず、以前の値の比較が抜け落ちてるから。

対応:
余り大きくないならSet使えばいいのではないでしょか。
余り多いと重複が増えすぎる場合があるので、処理が
重くなりがちです。
拾う数値の範囲を分割して複数回に分ければ多少は良く
なります。
swift殆ど書いたことが無いので、少しミスがあるかも
ですし、バージョン書いてなかったのでその辺りは修正
してください。

swift

1var checkSet = Set<Int>() 2let Length =10 3var integerArray = Array(repeating: 0, count: Length) 4 5integerArray[0] = Int(arc4random_uniform(UInt32(Length))) 6checkSet.insert(integerArray [0])// 0番目もチェック対象に含める 7for i in 1...Length-1 { 8 repeat{ 9 integerArray[i] = Int(arc4random_uniform(UInt32(Length))) 10 // すでにあるかどうかチェック 11 }while(checkSet .contains(integerArray[i])) 12 // 配列に入れたので、チェック済みとしSETに追加 13 checkSet.insert(integerArray[i]) 14}

投稿2017/06/16 00:17

編集2017/06/16 01:42
kanimaru

総合スコア1013

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sandalwalk

2017/06/16 00:26

頂いたコードで確認しましたが、やはり重複しています。ちなみにswift3です。頂いたコードはswiftでもエラーなしで動作しますし、この手の方法はどの言語でも同じ方法で動作するはずかと思うのですが。。
kanimaru

2017/06/16 00:45

完全ならランダムの値取るのは拾えず本当に重くなるので KSwordOfHasteさんのおっしゃる通り 予め数値を用意しておくべきですね。失礼しました。 あと、バグになってたぶんは編集させていただきました。
kanimaru

2017/06/16 00:47

あと、数値の範囲が決まってるならfuzzballさんのコードで最後に配列内シャッフルする方がいいとおもいますよ。
fuzzball

2017/06/16 01:30

typoです。insertArray -> integerArray
kanimaru

2017/06/16 01:43

fuzzballさん、ご指摘感謝です!修正しました。
sandalwalk

2017/06/16 05:03

修正頂きありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問