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

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

詳細はこちら
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

4回答

237閲覧

JavaScript アルゴリズム

t-cool

総合スコア71

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2019/10/02 12:54

編集2019/10/02 13:21

0からnまでを要素として含む同じ3つの配列から、
重複のないように、3つずつ要素を含む配列を作成する関数はどう定義すればいいでしょうか?

function make3Array (n){  let arr = []; // 処理 // return arr; }

のような関数を考えています。

例えば、nが9の場合は、

[0,1,2,3,4,5,6,7,8,9] [0,1,2,3,4,5,6,7,8,9] [0,1,2,3,4,5,6,7,8,9]

を重複のないように並べ替えて、以下のような2次元配列を生成したいです。

[  [0,3,7],  [2,0,8],  [5,6,2],  [7,2,6],  [1,8,3],  [8,7,1],  [9,1,5],  [6,9,4],  [3,4,9],  [4,5,0], ]

ご教授いただけるとありがたいです。
よろしくお願いします。

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

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

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

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

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

hatena19

2019/10/02 13:17

内側の3の数字も重複がないようにということですか。 例えば [2,2,3] はNG。
jun68ykt

2019/10/02 13:18

こんにちは。ご質問の事例だと、並べ替え後の2次元配列には 0 から 9 までの数字が、各々 3個ずつ存在しているという理解でよいでしょうか?(ちなみにご質問に挙げられている、並べ替え後の2次元配列には、0 が 2個しか含まれていなく、5 が 4個含まれています。)
t-cool

2019/10/02 13:23

質問での例が間違えていました。 nが9の場合は、0 から 9 までの数字が、各々 3個ずつ存在しているという意味でした。 あと、並び順はランダムで並べたいです。説明不足ですいません。
t-cool

2019/10/02 13:26

"内側の3の数字も重複がないようにということですか。例えば [2,2,3] はNG。" はい。内側の数字も重複がなく、ランダムな並びに並べ替えたいです。
guest

回答4

0

[  [0,1,2],  [1,2,3],  [2,3,4],  [3,4,5],  [4,5,6],  [5,6,7],  [6,7,8],  [7,8,9],  [8,9,0],  [9,0,1], ]

こう並べるのが簡単です。必要なパラメーターは n だけで、元の配列のことは考える必要ありません。

[8,9,0]

これは次のように求められます。

[(8+0)%(n+1),(8+1)%(n+1),(8+2)%(n+1)]

追記

JavaScript

1const n = 9; 2const rows = n + 1; 3const columns = 3; 4 5let exclude = [...Array(columns).keys()].map(column => []); 6 7const dest = [...Array(rows).keys()].map(row => { 8 let result = []; 9 for (let i = 0; i < columns; i++) { 10 let array = [...Array(rows).keys()]. 11 filter(a => exclude[i].findIndex(b => a === b) < 0). 12 filter(a => result.findIndex(b => a === b) < 0); 13 const r = Math.floor(Math.random() * array.length); 14 result.push(array[r]); 15 exclude[i].push(array[r]); 16 } 17 return result; 18}); 19 20console.log(dest);

投稿2019/10/02 13:17

編集2019/10/02 14:40
Zuishin

総合スコア28669

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

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

t-cool

2019/10/02 13:25

ご助言ありがとうございます。 質問の例のように、並び順はランダムで並べたいです。 質問の説明が中途半端ですいません。
tanishi_a

2019/10/02 13:46 編集

組み合わせの配列を作ったあと、 その配列をシャッフルするのと分けたら良いと思いますよ。
guest

0

冗長だし、がっさいですが、
(しかも書き方も古い)
下記でいかがでしょうか。

発想を変え、三つの配列を、
いったん一つの配列にまとめ、
そこから、一個一個にランダムにとってきた数値を入れていくやり方です。

正直、同じ発想でも、もっとスマートな方法があるかと。
(while使いまくってるのもけっこう危険)

javascript

1function make3Array (n, column) { 2 const arr = [], 3 baseArr = []; 4 5 // ベースとなる、n * columnの配列を作成 6 for (let ci = 0; ci < column; ci++) { 7 const insertOneArr = []; 8 for (let ni = 0; ni < n + 1; ni++) { 9 insertOneArr.push(ni); 10 } 11 baseArr.push(insertOneArr); 12 } 13 14 // ベースを一つにまとめた配列を用意 15 const allSerial = []; 16 baseArr.forEach(val => { 17 val.forEach(vi => { 18 allSerial.push(vi); 19 }); 20 }); 21 22 // まとめた配列がなくなるまで繰り返す 23 while (allSerial.length > 0) { 24 let insertArr = []; 25 // columnの数だけ、入るよう繰り返す 26 while (insertArr.length <= column && allSerial.length > 0) { 27 // まとめた配列のランダムのインデックスを用意 28 let rn = Math.floor( Math.random() * allSerial.length ); 29 // 一つの配列にその数値が含まれる限り、ランダムインデックスを入れなおす 30 while ( insertArr.includes(allSerial[rn]) ) { 31 rn = Math.floor( Math.random() * allSerial.length ); 32 } 33 // 値渡しにするために、変数に格納 34 const ti = allSerial[rn]; 35 // 一つの配列に格納 36 insertArr.push(ti); 37 // まとめ配列の数値要素を削除 38 allSerial.splice(rn, 1); 39 // 一つの配列が、columnの数になったら、最終的に返したい配列にプッシュ 40 if (insertArr.length == column) { 41 // 値渡しにするため、concatメソッドを使用 42 arr.push(insertArr.concat()); 43 // まとめ配列が残っている限り、一つ要素用配列をクリア 44 if (allSerial.length > 0) { 45 insertArr = []; 46 } 47 } 48 } 49 } 50 51 return arr; 52} 53 54console.log(make3Array(9, 3));

投稿2019/10/02 16:37

miyabi_takatsuk

総合スコア9555

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

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

0

ベストアンサー

こんにちは

ご質問にある

0からnまでを要素として含む同じ3つの配列から、

という趣旨に沿って、これら3つの配列 ar1 , ar2, ar3 を作成するステップを以下のように考えました。

(1) 与えられた n によって、 0 から n までを要素とする配列 ar1 を作成する。

例えば、 n が 9 のとき、 ar1は、 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

(2) 上記 (1) で得られた ar1 をランダムに並び替え、かつ、以下の条件

  • 0 以上 n 以下の任意の整数 i に対して、ar1[i] ≠ ar2[i]

を満たす配列 ar2 を作成する。

例えば、 n が 9 のとき、 ar2として、 [6, 8, 4, 9, 5, 1, 7, 3, 0, 2] を作成

(3) 上記 (1) で得られた ar1 をランダムに並び替え、かつ、以下の条件

  • 0 以上 n 以下の任意の整数 i に対して、ar1[i] ≠ ar3[i] かつ ar2[i] ≠ ar3[i]

を満たす配列 ar3 を作成する。

例えば、 n が 9 のとき、 ar2として、 [2, 6, 5, 4, 0, 8, 1, 9, 7, 3] を作成

(4) 上記 (1)(2)(3) で得られた ar1 , ar2 , ar3 の同じ位置の要素を集めた配列を要素とした配列を作成

上記の例だと、以下が得られます。

[[0, 6, 2], [1, 8, 6], [2, 4, 5], [3, 9, 4], [4, 5, 0], [5, 1, 8], [6, 7, 1], [7, 3, 9], [8, 0, 7], [9, 2, 3]]

以下は上記の各ステップをコードにしたものです。

javascript

1function sample(ary) { 2 return ary[Math.floor(Math.random() * ary.length)]; 3} 4 5function make3Array (n) { 6 7 // 0 から n までを要素として含む配列 ar1 を作成 8 const ar1 = [...Array(n+1)].map((_, i) => i) 9 10 // ar1 と同じ位置に同じ要素を持たない配列 ar2 を作成 11 let ar2 12 while(true) { 13 ar2 = [] 14 ar1.forEach((_, i) => { 15 const candidates = ar1.filter(e => e !== ar1[i] && !ar2.includes(e)) 16 ar2.push(sample(candidates)) 17 }) 18 if (typeof ar2[n] !== 'undefined') 19 break 20 } 21 22 // ar1, ar2 と同じ位置に同じ要素を持たない配列 ar3 を作成 23 let ar3 24 while(true) { 25 ar3 = [] 26 ar1.forEach((_, i) => { 27 const candidates = ar1.filter(e => e !== ar1[i] && e !== ar2[i] && !ar3.includes(e)) 28 ar3.push(sample(candidates)) 29 }) 30 if (typeof ar3[n] !== 'undefined') 31 break 32 } 33 34 // ar1, ar2, ar3 の同じ位置の要素を集めた配列を要素とした配列に変換 35 return ar1.map((_, i) => [ar1[i], ar2[i], ar3[i]]) 36} 37

以上、参考になれば幸いです。

追記

以下、別解です。

3つの配列

を、 ar1, ar2, ar3 として作ることは先の回答と変わりありませんが、配列をシャッフルするために lodash の _.shuffle を使い、配列を回転(rotate)させる関数を、stackoverflow のこの回答から拝借します。

javascript

1function arrayRotate(arr, reverse) { 2 if (reverse) arr.unshift(arr.pop()); 3 else arr.push(arr.shift()); 4 return arr; 5} 6 7function make3Array (n) { 8 9 // 0 から n までをランダムな順番で含む配列を作成する。 10 const ar1 = _.shuffle([...Array(n+1)].map((_, i) => i)) 11 12 // ar1 をローテートした配列を得る 13 const ar2 = arrayRotate([...ar1]) 14 15 // ar2 をローテートした配列を得る 16 const ar3 = arrayRotate([...ar2]) 17 18 // ar1, ar2, ar3 の同じ位置の要素を集めた配列を要素とした配列を得る 19 const ar = ar1.map((_, i) => [ar1[i], ar2[i], ar3[i]]) 20 21 // 上記で得られた配列をシャッフルした配列を返す 22 return _.shuffle(ar) 23}

投稿2019/10/02 14:33

編集2019/10/02 18:51
jun68ykt

総合スコア9058

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

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

退会済みユーザー

退会済みユーザー

2019/10/02 14:53

この方法だと乱数の出目によってはar3が正しく生成できないことはありませんか?たとえば、n=4でar1=[0, 1, 2, 3, 4], ar2=[2, 4, 0, 1, 3], ar3=[4, 2, 1, 0, undefined] みたいな感じで。n=9であれば発生頻度は低くなるとは思いますが確率0にはできないのでは。
jun68ykt

2019/10/02 15:06

@kichirb3さん ご指摘ありがとうございます。確認したところ、おっしゃる通りでした。 @t-coolさん 上記の回答で、先に書いたほうのコードだと、kichirb3さんからご指摘のように常に正しく動作するとはいえないものになっております。こちらのほう、修正できたら修正します。追記のほうに記載した別解のほうもご検討ください。
Zuishin

2019/10/02 15:22

別解はちょっとずるいですね。数字が斜めに並んで規則性がはっきり出ています。
jun68ykt

2019/10/02 15:33

@Zuishinさん ご指摘ありがとうございます。最後にもう一度シャッフルして数字が斜めに並ばないようにしました。
Zuishin

2019/10/02 15:44

やっぱりよく見ると規則性が見て取れますが、ぱっと見わかりません。こんな方法もあるんですね。
jun68ykt

2019/10/02 16:27

@Zuishinさん もうひとこえやろうと思えば、ar1からar2を得るとき、および、 ar2 からar3 を得るときの、ローテートの回数が現状1つずつなのを、このローテート回数も乱数で得るようにすればよいかなと思います。(回数によっては、a1, ar2, ar3 のいずれか2つが一致してしまうこともあるので、それを避けるような回数を得る必要はありますが。)
jun68ykt

2019/10/02 18:39 編集

@kichirb3さん @t-coolさん kichirb3さんの先のコメントからのご指摘の件に対応した修正版を、回答のほうに反映しました。修正の方法としては単純で、ar2 および ar3 を作る際に、undefined を含まないように作ることができるまで、作成を繰り返し試みます。
guest

0

配列をシャッフルするアルゴリズムはフィッシャー–イェーツのシャッフル - Wikipediaが有名ですね。
JSでの実装例は下記。
JavaScript アルゴリズムで配列をシャッフルする - Qiita

一つ目の[0,1,2,3,4,5,6,7,8,9]の配列を上記の方法でシャッフルする。

二つ目の[0,1,2,3,4,5,6,7,8,9]の配列をシャッフルして、一つ目と比較して重複があったら再トライ、重複がないのが出現するまで繰り返す。

三つ目の配列もシャッフルして、一つ目、二つ目と比較して重複がなくなるまで繰り返す。

というアルゴリズムはどうでしょう。

無駄が多いのでもっとスマートなアルゴリズムかあるとは思います。

投稿2019/10/02 13:46

編集2019/10/02 13:48
hatena19

総合スコア34073

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問