スクエニ ITエンジニア ブログ

Chat AI と debug する Javascript 配列シャッフル

こんにちは、ホシイです。
これを読んでいるあなたは、自分が AI ではないという確たる自覚をお持ちでしょうか? 🤔

無作為に並べ替えがしたい

コロナ禍以降、わたしの所属するチームでは、オンラインでデイリーミーティングをしています。日々の状況・情報の共有について、チームメンバーがひとりずつ順に数分話すのですが、毎日順番がおなじだとつまらないので順番を変えるようにしています。最初の頃は心のなかでテキトーに順を決めていたのですが、なにしろ面倒ですし、議論が盛り上がったときなどは抜けてしまったりなんならおなじ人を二度呼んでしまうこともあったりしました。こういうのはまさに機械が得意なことです。

ミーティング前に手元でぱっと node で実行できるように、JavaScript のワンライナーを書きました。

["幸", "中原", "宮前", "川崎", "高津", "多摩", "麻生"].sort(() => Math.random() - .5)

やっていることは非常に単純で、sort() に使われる比較で、内容 (名前文字列) を無視して毎回 random() で 1/2 の確率で基準を変動させています。これでランダムに順番が変わるでしょう。

よしよしと 2、3 日これでやってみたのですが、どうも結果が偏っているように感じます。 手元で何度か実行してみると、体感でわかるくらいに。なんとなく、元の配列から順番が変わっていない要素が多いように感じるのです。

実際に数千回ほど実行してカウントしてみると、ソート先の場所に大きく偏りがあることがわかりました。以下は、先頭の要素が最終的にそれぞれの位置に落ち着いた回数を示すグラフです。

頻度

多少の偏りはたのしめる要素ですが、これではちょっと極端です…。 また、端に近い要素 (先頭、末尾に近い要素) ほど偏っている様子がありました。

考えてみるとたしかに、sort() するときに順番を変える比較基準が五分五分で毎回変わると、元の位置からあまり離れていかないような気もします。(sort の algorithm 次第ではありますが)

そこで、以下のように処理を変えてみました。

["幸", "中原", "宮前", "川崎", "高津", "多摩", "麻生"]
    .map(o => ({ n: o, r: Math.random() })).sort((a, b) => a.r - b.r).map(o => o.n)

最初に比較に使用する random() 値を要素ごとに決定して、それによって sort() します。map() を挟んでいるので長く見えますが、まだまだシンプルです。

これで試行回数を多くすると回数が均等に分散するのが観測できるようになりました。

何が起きているのか?

web 検索をすると、まったくおなじようなことを言っているのを Stack Overflow に見つけました。

How to randomize (shuffle) a JavaScript array?

ここにある説明や、リンク先の記事はたいへん興味深く、辿っていくとそれだけでおなかいっぱいになりそうです。

やはり Array.sort() は毎回基準が変わるような比較関数を渡されるようにはできておらず、その場合の結果は意図しないものになり得るようです。

以下に、上記ページに載っていた shuffle 用の function をひとつ転載させていただきます。Fisher-Yates という有名な手法のようです。

(転載)

function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
}

なるほど、これはうまく・効率よく動きそうです。

さて。

タイトル詐欺はここまでとしましょう。
実は、ここまでの記事は数ヶ月前に書いたものでした。今になってこれを引っ張り出してきたのは理由があります。今話題の、AI サービスを用いると自分でやったことがずいぶん効率化できるのではと思ったのです。

最初に、以下は ChatGPT で試した結果です。

ChatGPT
右側が切れていますが、やろうとしていることはわたしが最初に書いたコードに似ています。続きの部分でもしっかりやりたいこと (実行ごとに偏り無く並べ替えをしたいこと) は伝わっていそうですが、提案内容は Math.random() の代わりに crypto API を使用すると良い、というものでした。
やってみるとわかりますが、これでも結果の偏りは発生します。

実際、これだけでもたいしたものだと思っていたのですが… 以下、より最近利用可能になった、Bing AI で試した結果です。(聞き方が最初から違うのでフェアではないですが)

Bing AI (1)
驚くことにこの時点で、ほとんど文句が言えない結果が出ています。

日々の実行に便利なように、一行で書けないかな? という意図で頼んでみると…

Bing AI (2)
最初から偏りについて意識していた様子で返答をしてきました。

うーんさすが。最後に確認をしておこうと思い、確認用のコードを書くのも面倒なのでそれもおねがいしてみました。

Bing AI (3)
そしたらなんと、実行までやってくれたじゃないですか…
仕組み上、この数値ももしかしたら (実際の試行ではなく) 生成されたものという可能性もあるので、手元でも実行してみましたが、大きな偏りは無さそうでした。

結果的にたどり着く結論がおなじだとしても、そこに至るまでの時間や精神力の消費が、web 検索 〜 試行錯誤を繰り返すのとはかなり異なります。自分で考えることも大切ですが、日々業務すべてをそうする必要はないですし、こういった経験は素早く数をこなしたほうが力になることもあります。利用にはいろいろと考えさせられます。

まとめ

今回は思わぬところから、JavaScript やデータ処理の勉強になりました。
さらに同時に、AI によってこれからの学習や仕事のしかたの変化も感じることができました。

結果として、毎日のミーティングで、話す順を穏やかな気持で決めることができるようになりました。
日々、ちいさな違和感を見逃さずに過ごしたいなと思います。

この記事を書いた人

記事一覧
SQUARE ENIXでは一緒に働く仲間を募集しています!
興味をお持ちいただけたら、ぜひ採用情報ページもご覧下さい!