ITコラム.01原森ITコラム.02原森ITコラム.03原森ITコラム.04原森ITコラム.05そして2進数の活用ネタの紹介「ジャンケンプログラム」2進数とビットマスクを組み合わせると、入門書に載ってないようなテクニックに使えたりしますジャンケンプログラムについての考察ネタです今回はアルゴリズム...プログラムのアイデア紹介です。活用事例にもなっています。文で記述が面倒な条件判定をしているサンプルが提示され、それを二次元配列にすると素敵、といった記事がいくつか発見できるかと思います。記述が二次元配列で簡単になる』という多次元配列の基本活用テクニックを紹介するための記事になっている、という印象です。(又は配列)で判定する仕様では、複数人ジャンケンに対応するものを作るのがとても大変...場合によっては不可能、というものなのです。elseタイプを二次元配列にするネタから紹介し、それを複数人にする時の大変さ(というか無理ゲーさ)を説明します。その後、違う視点で複数人(2人以上、10人でも50人でも対応可)でジャンケンするプログラムを考察し、その時にビット演算を使うアイデアの紹介をしています。innerHTML変更等で画面表示する処理は省略}javascript版のジャンケンプログラムの例。プレイヤーが入力する引数値は、グーの時は0、チョキの時は1、パーの時は2、とします。※結果を画面に出力する処理は本題ではないので省略しています。else文で判定するという教科書通りの例が紹介されたりします。文字列を取得して判定に使っています。プログラムとしては、わざわざ文字列で比較しなくても数値のままでいいと思うのですが、サンプルとして分かりやすくするためにそういう例が多いのだろうと思ったりします。zyanken_type_ifの動作テストジャンケンの手を選んでください。elseのサンプルを作って眺めてみます。判定数値を表にすると、そのまま二次元配列のインデックス番号になる、というアイデアが浮かびやすいかと思います。elseが二次元配列を使うと簡単にという見出しで紹介されていたりします。innerHTML変更等で画面表示する処理は省略}要するに、文字列判定ではなく数値判定時のp値やc値をそのまま二次元配列の引数にしている、というアイデアです。zyanken_type_arの動作テストジャンケンの手を選んでください。ツリーを考えてみます。人をA、コンピュータをBとして書くと...Aの手が[0(G)]の場合、Bの手が[0(G)]なら、引き分けBの手が[1(C)]なら、Aの勝ちBの手が[2(P)]なら、Bの勝ちAの手が[1(C)]の場合、Bの手が[0(G)]なら、Bの勝ちBの手が[1(C)]なら、引き分けBの手が[2(P)]なら、Aの勝ちAの手が[2(P)]の場合、Bの手が[0(G)]なら、Aの勝ちBの手が[1(C)]なら、Bの勝ちBの手が[2(P)]なら、引き分けこのアイデアのままで、3人対戦を考えてみると、こんなifelseツリーに...Aの手が[0(G)]の場合、Bの手が[0(G)]の場合、Cの手が[0(G)]なら、引き分けCの手が[1(C)]なら、AとBの勝ちCの手が[2(P)]なら、Cの勝ちBの手が[1(C)]の場合、Cの手が[0(G)]なら、AとCの勝ちCの手が[1(C)]なら、Aの勝ちCの手が[2(P)]なら、引き分けBの手が[2(P)]の場合、Cの手が[0(G)]なら、Bの勝ちCの手が[1(C)]なら、引き分けCの手が[2(P)]なら、BとCの勝ちAの手が[1(C)]の場合、Bの手が[0(G)]の場合、Cの手が[0(G)]なら、BとCの勝ちCの手が[1(C)]なら、Bの勝ちCの手が[2(P)]なら、引き分けBの手が[1(C)]の場合、Cの手が[0(G)]なら、Cの勝ちCの手が[1(C)]なら、引き分け【以下略】プログラムの授業などで先生が2人対戦ジャンケンプログラムのみを教えて、3人、5人で対戦するプログラムを作れ、などと課題を出したりする場合はかなり意地悪かもしれません。2表を縦にすると長すぎるのでAの数値で3分割して横に配置してます。数値の並びが3桁の3進数になっています。10進数にすると0から26まで手の組み合わせパターンがあることがわかります。else/多次元配列タイプで作ると、27個の判定結果をifelse又は多次元配列(3人は3次元)で書かなければいけない、ということです。多次元配列で簡単に...などと言っている場合ではなくなってしまいます。判定で作るのは不可能です。対戦者が何人いるかわからないので総当たりのifelse文や多次元配列データが用意できないからです。ジャンケンプログラムを作りたいという目的からみれば、不完全、ということなのです。視点を変えてジャンケンのルールで全判定結果を表にしてみますプレイヤーの数は不明として数を限定せず、勝敗条件を表にしてみます。次の表では、あり(誰かがその手を出した)場合を1、なし(誰も出してない)を0としています。ジャンケンのルールでは、出された手が2種類の場合のみ勝ち負けが決まり、3種類の手が出るか、全員同じ手の時は引き分けです。GCPで引きわけ出てる手のありなしのみに注目すると、このコラムでずっと紹介してきた2進数になりました。ジャンケンの手は3種類なので3桁の2進数であり、しかも0(GCPのどの手も出ない)はないので、結果のパターン数は7つになります。どれかになれば、勝敗判定ができる、ということが判明しました。(ビットごとのOR)です。なんやねんANDの時のように書いてみますと、OR演算子は以下のような計算になります。1両方が0の時以外は1になるというものです。銀河鉄道999を40代のおじさんたちが青春の幻影を泣きながら思い出すような計算なのです。ビットごとのORは[|]記号を使います。0|2=2、2|4=6、6|1=7、という感じです。10進数で書くと意味不明な感じですが、2進数にすると000|010=010、010|100=110、110|001=111、となり、なんとなく雰囲気は伝わるかと思います。2進数1桁目のビットマスク、すなわち001でビットごとのORを計算してみます。なった)次に、その数値に2桁目のビットマスク(010)でビットごとのORを計算してみます。なった)最後に、この数値に3桁目のビットマスク(100)でビットごとのORを計算してみます。これはすなわち、GCPで引きわけ、ということです。してやれば、何人プレイヤーがいても、大量のifelse文を書かずに複数人ジャンケンの結果を判定する値を作ることができる、ということになります。GCPで引きわけ■複数人ジャンケンは、各プレイヤーが3種類のどれかの手を出す■3種類の手がすべて出てる、又は全員が同じ手の時はあいこで引き分け■2種類の手の場合は勝敗のある2人ジャンケンと同じ。演算(ビットごとのOR)して、上記の表の判定値を作ります。いうビット演算が複数人ジャンケンのアルゴリズムのメインアイデアとなります。k|=1<<2、という計算になります。プログラム時にはその値判定もしておくと無駄な計算をはぶけるかと思います。n人分のループc[i]=Math.floor(Math.random()*3);//pcの手(0~2)if(k!==7)k|=1<<c[i];//pcの手を[|演算]}//後はx[k]で結果の文字列作成し画面表示したり、//配列cや引数pから個別の勝ち負けを画面表示したり...}この関数では、引数pに人間プレイヤーの手の数値(0~2)、引数nに対戦するPCの人数を渡します。初期値0のkに人間プレイヤーの手を[|演算]し、その後、n人のPCの手をランダムに作って配列cに入れ、kが7になってない場合のみPCの手を[|演算]します。後はkの値を配列xのインデックス番号にして結果を表示するなどになります。zyanken_type_orの動作テスト※このテストはx[k]の結果と手のみを表示しています。対戦するコンピュータの数を選んでください。50人と対戦ジャンケンの手を選んでください。r+='が'+x[k]+'勝ち';}if(e)e.innerHTML=r+'<hr>'+s+'<br>(参考:k='+k+')';}zyanken_type_or2の動作テスト対戦するコンピュータの数を選んでください。50人と対戦ジャンケンの手を選んでください。elseで手を比較する形になっていて、それを二次元配列で簡単に、というオチになるものが多い印象です。真剣にジャンケンプログラムが作りたい...という人(がいるのかどうかは不明だけど)が複数人でも対応可能なジャンケンプログラムを作る参考(回答)になるように考察してみました。ジャンケンプログラム(を作ってほしい)と聞いて、最初から複数人ジャンケンを想定するかどうかで考えたり作業したりの時間が変化するかと思いますのでいきなり検索する癖のある人は、多数意見と思っているgoogle検索上位に出てくる結果に惑わされてしまい、複数人を想定して考えると全く異なるアルゴリズムで作れるという視点(プログラムの判定条件)に気づかない場合があるかもしれません。プログラムは同じ結果を出すためにいろんな方法(作り方)があるので、いろいろ視点を変えて最適な手法を考えるのが重要ポイントだと思ったりします。総当たり判定方法から、「Gが出てるか?Cが出てるか?Pが出てるか?」に視点変更することで人数無制限のジャンケンプログラムのアルゴリズムを作りました。20人、50人対戦なんてのは何回やっても結果はほぼ引き分けになってて、誰かが勝つのは奇跡的かと思ったりしました...ついでに紹介するとアンケートフォームの複数選択状態を表現する数値を作る場合も|演算が使えます以前のコラムで複数選択のあるアンケートで「1:映画=1」「2:音楽=2」「3:釣り=4」「4:パソコン=8」「5:車=16」という選択値のビットマスク値を加算して、選択状態を表す数値を作る、という紹介をした時には単純に、ビットマスク値を加算する形で紹介しました。いう値を作ることができます。複数人ジャンケンプログラムの場合もビットマスク値を加算する形でも作れますが、何も考えずに出た手の値から作ったビットマスク値を片っ端から加算するだけだと、判定結果の値が7を超えてしまう可能性があるので、値が7を超えないように判定文を入れることが必須となる点がポイントとなります。いうことで、今回の最適な手法は「|演算」ではないかと思い紹介してみました。『片手で31カウントの理由を理解すると、両手で1023カウントを説明できるようになる』(記事1の内容)2進数の基本活用方法が理解できると「パーミッション設定」の意味もわかるようになります。『複数の選択状態を示す10進数値から情報を取り出す時はビットマスクで[&](ビットごとのAND)を使ってビット演算する』(記事2の内容)2進数の[111]は10進数の[7]。単なるビット演算それに、人間がその計算結果を奇数偶数判定として使うという意味を与えることで、単なる計算がアイデアになる』(記事3の内容)原森IT研の考察結果紹介。