CakePHP5入門【WebAPI編⑰】ボックス予想
A子
それを組み合わせて「ナンバーズ3」用の3けたの数字を作るとするじゃん?
B美
それがどうかしたの?
A子
候補となる数字が4つだったらすぐにできるわけよ(結果も4つしかないからね)
でも、候補数字が5つや6つの場合って、ちょっと大変だなぁ…って思ってね
C菜
A子
ダブル(112や355等)やトリプル(111や222等)は考慮しなくて良いからさ
WebAPIとしてじゃなくて、Webアプリの便利機能として作ってみたいんだよね
B美
それはちょっと面白いわね
やってみましょうか
C菜
機能としては以下の二点を押さえておきたいです~
|
・6つのドロップダウンリストを作り、最低でも4つ以上選択されていないとエラー
・重複した値を選択できないようにする |
重複排除をどのように行えば良いのか、さっぱり分かりませんけど~(苦笑)
A子
ちょっと質問してみるよ
A子
(CSSフレームワークの)Bulmaとの連携がうまくいかなくて、3~4回試行錯誤したよ
C菜
A子
こんな感じかな
あ、ファイル名は「templates/Top/combination_input.php」ね
C菜
(中身は空ですけど~)
|
//指定された数字を組み合わせて「ボックス」用の値を作成(入力画面)
public function combinationInput() { } |
そのあと、ブラウザからアクセスしてみました~
| http://192.168.1.205/numapp/top/combination_input |
A子
うん、一覧から「3」が消えてるね
(「2」と「4」の間に本来あるべき「3」が無い…なお「5」以降は見えてないだけで、ちゃんとあるよ)
C菜
A子
|
//指定された数字を組み合わせて「ボックス」用の値を作成(結果画面)
public function combinationResult() { if ($this->request->is('post')) { $digits = $this->request->getData('digits'); } } |
B美
その書き方じゃ、6つ全て選んだときは良いけど、4つや5つのときって困るわよ
C菜
B美
["0", "1", "2", "3", "", ""]
になるの
なので、こう書くべきね
|
//指定された数字を組み合わせて「ボックス」用の値を作成(結果画面)
public function combinationResult() { if ($this->request->is('post')) { $digits = array_filter($this->request->getData('digits'), 'strlen'); } } |
A子
$digits = array_filter($this->request->getData('digits'));
…ってことね
B美
["1", "2", "3"]
C菜
A子
んじゃ、次はその数字の組み合わせを作る方法だね
ChatGPT先生に聞いてみたら、こういうメソッドを教えてもらったよ
|
/**
* Combination 組み合わせ nCr (r ≦ n) * * @param array $arr 元となる配列 * @param int $r 1セットあたりの要素数 * @return null|array */ private function combination(array $arr, int $r): ?array { //数値添字配列にする $arr = array_values($arr); $n = count($arr); $result = []; //条件不一致 if ($n < 1 || $n < $r) { return null; } //1個選ぶ場合 if ($r === 1) { foreach ($arr as $item) { $result[] = [$item]; } } //2個以上 if ($r > 1) { foreach ($arr as $key => $item) { $newArr = array_slice($arr, $key + 1); //再帰 $recursion = $this->combination($newArr, $r - 1); if ($recursion !== null) { foreach ($recursion as $one_set) { array_unshift($one_set, $item); $result[] = $one_set; } } } } return $result; } |
B美
[
["0", "1", "2"],
["0", "1", "3"],
["0", "2", "3"],
["1", "2", "3"]
]
ここから
["012", "013", "023", "123"]
を作るには…
|
//指定された数字を組み合わせて「ボックス」用の値を作成(結果画面)
public function combinationResult() { $box = []; if ($this->request->is('post')) { $digits = array_filter($this->request->getData('digits'), 'strlen'); $box_set = $this->combination($digits, 3); //ボックスの組み合わせを作成 foreach ($box_set as $row) { sort($row, SORT_NUMERIC); $candidate = ($row[0] * 100) + ($row[1] * 10) + ($row[2] * 1); array_push($box, sprintf("%03d", $candidate)); //ゼロパディングした3けたの文字列 } } } |
…って感じかな
これで「$box」の中身は、["012", "013", "023", "123"]になるはず…
C菜
あとは、これをビューファイル(templates/Top/combination_result.php)で表示するだけですね~
A子
ここまでやったんだから、もう少し機能を追加しよう
各ボックス値の出現頻度(回数)や直近の出現時期なんかを調べられないかな?
C菜
ちょっと面倒です~(苦笑)
B美
現状のスキーマ構造でも大丈夫よ
例えば、ボックス値「012」を検索したいときって、「百の位」「十の位」「一の位」の中で(最小値が「0」、最大値が「2」、それ以外が「1」)という条件で検索すれば良いのよ
A子
C菜
B美
SQLで書けば、こうなるわ
(「012」を調べる場合)
|
select lottery_time,lottery_date_dt,lottery_date_str, num3_str
from numbers where least(num3_place100, num3_place10, num3_place1)=0 and (num3_place100+num3_place10+num3_place1)-least(num3_place100, num3_place10, num3_place1)-greatest(num3_place100, num3_place10, num3_place1)=1 and greatest(num3_place100, num3_place10, num3_place1)=2 order by lottery_date_dt desc; |
あ、最後にソート(order by)してるのは、直近の出現時期を求めるためね
A子
C菜
問題は「1」と等しいかを調べてる箇所なんですけど、全部足したものから最小値と最大値を引く~?
え~っと、あ!
(0+1+2)-0-2って、確かに1になりますね~
B美
面倒だから「ConnectionManager」を使って、直接さっきのSQL文を実行すれば良いと思うわよ
(というか、CakePHP風に書くやり方が分からん(苦笑))
A子
んじゃ、やってみるか
|
$result = [];
・・・ //各ボックス値の詳細情報を取得 //直近の出現時期とその当選番号、過去の出現頻度等 $connection = ConnectionManager::get('default'); foreach ($box as $value) { //各桁の分解 $digit = str_split($value); //データベース検索 $sql =<<<EOF select lottery_time,lottery_date_dt,lottery_date_str, num3_str from numbers where least(num3_place100, num3_place10, num3_place1)={$digit[0]} and (num3_place100+num3_place10+num3_place1)-least(num3_place100, num3_place10, num3_place1)-greatest(num3_place100, num3_place10, num3_place1)={$digit[1]} and greatest(num3_place100, num3_place10, num3_place1)={$digit[2]} order by lottery_date_dt desc; EOF; $search_result = $connection->execute($sql)->fetchAll('assoc'); //結果を格納 if (count($search_result) > 0) { $recentry = $search_result[0]['lottery_date_str']; //直近の出現時期 $lottery_time = $search_result[0]['lottery_time']; //直近の実施回 $num3 = $search_result[0]['num3_str']; //直近のストレート数字 $frequentry = count($search_result); //出現頻度(回数) array_push($result, [$value, $recentry, $lottery_time, $num3, $frequentry]); } else { array_push($result, [$value, '-', '-', '-', 0]); } } |
こんなもんでどう?
B美
A子もなかなかやるようになったじゃない
結果である「$result」の中身は…
[
[ボックス値, 直近の出現時期, 直近の実施回, 直近のストレート数字, このボックス値の出現回数],
・・・(以下、同じ形式の配列が並ぶ)・・・
]
…って感じかな?
C菜
この「$result」の中身の並びを「出現時期の昇順」や「出現回数の降順」にすることはできないでしょうか~?
一次元配列の中身をソートするのは「sort」関数でできますけど、これって「配列の配列」ですよね~?
B美
そういうときは「array_multisort」関数を使うの
まぁ、書き方が少し難しいから、それは私のほうでやっておきましょう
|
//直近の出現時期が古いものから順に並べ替え(ソートには「実施回」を使用)
array_multisort(array_column($result, 2), SORT_ASC, $result); |
A子
つまり「出現時期の年月日」かな?
B美
配列はゼロスタートだから、先頭から3列目の「実施回」ね
(てか、コメントにも書いてるじゃない(苦笑))
C菜
出現時期で使ってる「lottery_date_str」って、日本語だからですよ~
(○○年○月○日という形式はソートに適さないですもんね~)
A子
んじゃ、「出現回数の降順」にするなら…
| array_multisort(array_column($result, 4), SORT_ASC, $result); |
…ってこと?
B美
降順にするなら、「array_multisort」関数に渡す第二引数を「SORT_DESC」に変えてね
| array_multisort(array_column($result, 4), SORT_DESC, $result); |
C菜
あ、ビューファイル(templates/Top/combination_result.php)については、難しくないので(ここでの掲示は)省略しますね~
A子
あ、その前にトップページにリンクを作ったよ
↓
C菜
えっと~
「0」「4」「6」「7」「8」の5つを入力して「実行」ボタンを押してみますね~
A子
お!
ボックス値として二番目に「467」があるね
C菜
最新のデータだと、「467」はもっと下位になると思うんですけど~
(2026年5月14日に出現したばっかりだし…)
B美
更新をさぼってたから、2026年5月1日時点のデータみたい(汗)
最新のデータだったら、こうなるわね
A子
(まだ2026年5月14日のデータが当選番号として登録されてない状態…ってこと)
B美
さて、これで当初の目的は達成したかしら
C菜
読者の皆さんもぜひ使ってみてください~
A子

