ここから本文です

プログラミング初学者です。 現在JavaScriptを勉強中であり、あるJavaScriptの...

ota********さん

2020/4/600:29:30

プログラミング初学者です。

現在JavaScriptを勉強中であり、あるJavaScriptのソースコードを拝見し、理解できない部分があったので質問させていただきます。

function randomExtraction(array, num) {
let a = array;
let t = [];
let r = [];
let l = a.length;
let n = num < l ? num : l;
while (n-- > 0) {
let i = Math.floor(Math.random() * l | 0;
r[n] = t[i] || a[i];
--l;
t[i] = t[l] || a[l];
}
return r;
}

上記の関数は「配列から任意の個数の要素を重複無しでランダムに抽出する関数」を示しているらしいのですが、上記コードの解説をしていただけないでしょうか?

特に let i = Math.floor(Math.random() * l | 0; と t[i] = t[l] || a[l]; の部分が理解できません。
前者は Math.floor(Math.random()*l); と同じ動作で問題ないでしょうか?
後者は t[l]を最初に判定させる意味が理解できませんでした。

拙い文章で失礼いたしました。
初学者の記述ゆえに質問の意図が不明な場合は、申し訳ございません。

皆様の知恵をお貸しいただければ幸いです。
どうぞよろしくお願いいたします。

閲覧数:
18
回答数:
1
お礼:
250枚

違反報告

ベストアンサーに選ばれた回答

cra********さん

2020/4/921:22:28

まず
let i = Math.floor(Math.random() * l | 0;
ですが、閉じカッコがなくこのままではエラーになります。
let i = Math.floor(Math.random() * l); または
let i = Math.random() * l | 0;
であるはずで、どちらも 0 ~ l-1 の間の整数を求めるものです。

「| 0」は少々テクニカルな方法ですが、単に切り捨てを意味するものです。(もし詳しく知りたければ「javascript ビット演算」で検索)


さて、重複なしで抽出するためにどのような処理がされているのでしょうか

a: 元の配列
t: 元の配列の抜けを埋める配列
r: 結果の配列
l: 元の配列aで未使用の個数

まず初めに
a: [99, 22, 33, 77, 44, 11]
t: []
であったとします。iに2が選ばれたとするとa[2]に当たる33が使われます。今後iに2が選ばれたときのために、代わりにa[l]の11を持ってきてt[2]に11を入れます。
a: [99, 22, 33, 77, 44, 11]
t: [ , , 11, , , ]
次回以降は、99,22,11,77,44から選びます。

同様にiに3が選ばれると77が使用済みになるので、同様にa[l]から持ってきて
a: [99, 22, 33, 77, 44, 11]
t: [ , , 11, 44, , ]
次回以降は、99,22,11,44から選びます。

続いてiに0が選ばれると99が使用済みになるので、同様にa[l]から数字を持ってきてしまうと……
a: [99, 22, 33, 77, 44, 11]
t: [77, , 11, 44, , ]
次回以降は、77,22,11から選び……ません、77が復活してしまいました。補完用のt[l]があるときは優先しなければいけませんね。a[l]ではなくt[l]を使えば、
a: [99, 22, 33, 77, 44, 11]
t: [44, , 11, 44, , ]
次回以降は、44,22,11,44から選ぶことになります。

aとtの両方に数があれば、tを優先して使うという発想なのでしょう。



ところで、もう少しJavaScriptの関数を用いて短めのコードを考えてみました
function randomExtraction(array, num){
const a = array.concat();
const r = [];
let n = Math.min(num, a.length);
while(n-- > 0) {
const i = Math.floor(Math.random() * a.length);
r.push(a[i]);
a.splice(i, 1);
}
return r;
}
2行目 a = array.concat() →元配列をコピーしてaに代入
7行目 r.push(a[i]) → r末尾にa[i]を追加
8行目 a.splice(i, 1) → aのi番目を削除してつめる

質問した人からのコメント

2020/4/9 21:54:44

詳しく解説いただき、ありがとうございました!
とても勉強になりました!

この質問につけられたタグ

みんなで作る知恵袋 悩みや疑問、なんでも気軽にきいちゃおう!

Q&Aをキーワードで検索:

Yahoo! JAPANは、回答に記載された内容の信ぴょう性、正確性を保証しておりません。
お客様自身の責任と判断で、ご利用ください。
本文はここまでです このページの先頭へ

「追加する」ボタンを押してください。

閉じる

※知恵コレクションに追加された質問は選択されたID/ニックネームのMy知恵袋で確認できます。

不適切な投稿でないことを報告しました。

閉じる