CakePHP5入門【WebAPI編⑭】ナンバーズ3を予想①
A子
これって結構「当てになる」という印象だよね
いや、むしろ「当たる」?
B美
でも、それについては私も同意するわ
なんだったら「前々回」と「前回」の数字の組み合わせだけで「今回」の当選番号が決まってるってケースもあったし…
C菜
あと、ある数字が2回連続した場合、3回目にも同じ数字が出現する…って法則(?)もありましたよ~
A子
「○○○」だけじゃなく「○○×○」とか「○×○○」「○×○×○」ってパターンもあったね
(「○○」は同じ数字が連続してる…ってことね)
B美
(「ナンバーズ4」については到底当てられる気がしないし…(苦笑))
A子
「前回(1回前)」「前々回(2回前)」だけじゃなく、「3回前」と「4回前」も見ないとダメかな
C菜
(「◎」が今回分の予想数字です~)
B美
| ケース | スコア | 備考 |
|---|---|---|
| 1回前に出現 | +3 | ○◎ |
| 2回前に出現 | +3 | ○○◎,○×◎ |
| (1回前または2回前の どちらかに出現していて、かつ) 3回前に出現 | +2 | ○×○◎,○○×◎ |
| (2回前だけに出現していて、かつ) 4回前に出現 | +1 | ○×○×◎ |
という感じでどうかしら?
A子
| ケース | スコア |
|---|---|
| ○○◎ | 6 |
| ○×○◎,○○×◎ | 5 |
| ○×○×◎ | 4 |
| ×○◎,○×◎ | 3 |
…ってことかな?
C菜
(各桁ごとじゃなく、各桁の合計で判断して~)
その場合、最低点が「0」で、最高点が「8」の9段階評価になりますよ~
B美
あくまでもメインは「引っ張り」で、出目の偏りのほうはサブ的な位置付けになるわけね
A子
ダメなら、また別のやり方を考えれば良いじゃん
B美
WebAPIを呼び出した日の予想ってことになるわね
A子
「曜日」や「時間」の判定も必要じゃない?
だって、土曜日や日曜日に呼び出した場合は「月曜日」分の予想になるわけだし…
あと、夜の8時にアクセスした場合、その日の分の予想はおかしいよね
(抽選は夜7時過ぎくらいのはず…)
C菜
B美
大晦日(12月31日)と正月三が日(1月1日~1月3日)については、たとえ平日でも抽選は無いわ
C菜
|
1.WebAPIを呼び出した日の「年月日」及び「時間」を求める
2.「年月日」から曜日を求め、土曜日なら「+2」、日曜日なら「+1」する (この場合の予想日は「月曜日」となる) 3.その日が年末年始かどうかを判定し、そうであれば「年月日」を1月4日とする (1月4日が土日であるかどうかの判定も行う) 4.2と3の条件に合致しない場合、「時間」が【0:00~17:59】の間にあるかどうかを判定する (これ以外の時間の場合、エラーを返す) 5.その「年月日」を予想日ととらえ、最新のデータを4件取得してスコアを算出する |
というアルゴリズムでどうでしょうか~?
A子
あ、4番って、平日の18時以降はエラーにするってこと?
B美
(スクレイピングのクーロン設定を22時にしたから…)
A子
B美
翌日が大晦日や土曜日って場合もあるし…
A子
B美
(話はそれからよ)
C菜
「num3Prediction()」というメソッド名にしましょう~
(URLでは「…/api/num3_prediction」という呼出しになりますね~)
A子
| 1.WebAPIを呼び出した日の「年月日」及び「時間」を求める |
時間を扱うわけだから、当然「DateTime」クラスになるよね?
| use Cake\I18n\DateTime; |
C菜
|
$date = DateTime::now();
$year = intval($date->format('Y')); $month = intval($date->format('n')); $day = intval($date->format('j')); $hour = intval($date->format('G')); |
あ、整数化(intval)は念のためです~
A子
|
2.「年月日」から曜日を求め、土曜日なら「+2」、日曜日なら「+1」する
(この場合の予想日は「月曜日」となる) |
「曜日」を求めるのに「SixSevenWeekComponent」を使う?
C菜
| $week = $date->format('w'); |
これで0(日曜日)から6(土曜日)までの数値が得られますね~
A子
|
//日曜日ならば1日進める
if ($week == 0) { $date->modify('+1 day'); } //土曜日ならば2日進める if ($week == 6) { $date->modify('+2 day'); } |
C菜
|
3.その日が年末年始かどうかを判定し、そうであれば「年月日」を1月4日とする
(1月4日が土日であるかどうかの判定も行う) |
こんな感じでしょうか~?
|
//年末年始判定
$month = intval($date->format('n')); $day = intval($date->format('j')); if ($month == 12 && $day == 31) { $date = $date->modify('+4 day'); } if ($month == 1 && $day == 1) { $date = $date->modify('+3 day'); } if ($month == 1 && $day == 2) { $date = $date->modify('+2 day'); } if ($month == 1 && $day == 3) { $date = $date->modify('+1 day'); } |
A子
|
$week = $date->format('w');
//日曜日ならば1日進める if ($week == 0) { $date->modify('+1 day'); } //土曜日ならば2日進める if ($week == 6) { $date->modify('+2 day'); } |
C菜
では、次です~
|
4.2と3の条件に合致しない場合、「時間」が【0:00~17:59】の間にあるかどうかを判定する
(これ以外の時間の場合、エラーを返す) |
(年末年始以外の)平日の場合、時間判定を行いますね~
|
//エラーフラグ
$error_flag = false; //時間判定 $hour = intval($date->format('G')); if (in_array($hour, [18, 19, 20, 21, 22, 23])) { $error_flag = true; } |
B美
A子
|
//平日フラグ(true…平日,false…土日または年末年始)
$weekday_flag = true; |
んで、もしも土日や年末・年始の条件に合致した場合、「false」を代入するの
…ってわけで、時間判定はこうなるね
|
//平日ならば時間判定
if ($weekday_flag) { $hour = intval($date->format('G')); if (in_array($hour, [18, 19, 20, 21, 22, 23])) { $error_flag = true; } } |
B美
| $date = DateTime::now(); |
の箇所を
| $date = new DateTime('2026-04-01 20:00:00'); |
…って感じで(一時的に)変えれば、任意の日時を検証できるからね
(上記の例は、2026年4月1日(水)の夜8時ってこと)
C菜
|
//ナンバーズ3を予想
public function num3Prediction() { //エラーフラグ $error_flag = false; //平日フラグ(true…平日,false…土日または年末年始) $weekday_flag = true; //現在の日時を取得 $date = DateTime::now(); //曜日判定 $week = $date->format('w'); //日曜日ならば1日進める if ($week == 0) { $date = $date->modify('+1 day'); $weekday_flag = false; } //土曜日ならば2日進める if ($week == 6) { $date = $date->modify('+2 day'); $weekday_flag = false; } //年末年始判定 $month = intval($date->format('n')); $day = intval($date->format('j')); if ($month == 12 && $day == 31) { $date = $date->modify('+4 day'); $weekday_flag = false; } if ($month == 1 && $day == 1) { $date = $date->modify('+3 day'); $weekday_flag = false; } if ($month == 1 && $day == 2) { $date = $date->modify('+2 day'); $weekday_flag = false; } if ($month == 1 && $day == 3) { $date = $date->modify('+1 day'); $weekday_flag = false; } //再度、曜日判定 $week = $date->format('w'); //日曜日ならば1日進める if ($week == 0) { $date = $date->modify('+1 day'); } //土曜日ならば2日進める if ($week == 6) { $date = $date->modify('+2 day'); } //平日ならば時間判定 if ($weekday_flag) { $hour = intval($date->format('G')); if (in_array($hour, [18, 19, 20, 21, 22, 23])) { $error_flag = true; } } //最終年月日・曜日・六曜 $year = intval($date->format('Y')); $month = intval($date->format('n')); $day = intval($date->format('j')); $seven = $this->SixSevenWeek::getSeven($year, $month, $day); $six = $this->SixSevenWeek::getSix($date->format('Y-m-d')); ・・・(予想処理)・・・ if ($error_flag) { //ステータス(時間外エラー) $status = [ 'code' => 500, 'message' => 'Access Denied' ]; $result = null; } else { //ステータス(成功) $status = [ 'code' => 200, 'message' => 'Success' ]; //日付情報 $lottery = [ 'lottery_date_str' => $year.'年'.$month.'月'.$day.'日', 'lottery_week_str1' => $seven['week_str_long'], 'lottery_week_str2' => $seven['week_str_short'], 'lottery_rokuyo_str' => $six['rokuyo_str'] ]; //予想結果 $prediction = [ 'num3' => null ]; //日付情報と予想結果を結合 $result = array_merge($lottery, $prediction); } //JSON化する前の連想配列(最終形態) $json = [ 'status' => $status, 'result' => $result ]; //JSONエンコードした結果を返す $this->autoRender = false; header("Content-Type: application/json"); echo json_encode($json); } |
これで日付のテストができますよ~
A子
B美
| 5.その「年月日」を予想日ととらえ、最新のデータを4件取得してスコアを算出する |
私のほうでやってみたわよ
(「$record」という配列にスコアを加算していくからね)
|
//予想処理
$record = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; if (!$error_flag) { //フラグ配列 $pre1 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; //1回前に出現しているか(出現した場合「1」) $pre2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; //2回前に出現しているか(出現した場合「1」) $pre3 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; //3回前に出現しているか(出現した場合「1」) //最新のデータを4件取得 $numbers = $this->Numbers->find()->order(['lottery_time' => 'desc'])->limit(4); foreach ($numbers as $idx => $val) { $num3_place1 = $val->num3_place1; $num3_place10 = $val->num3_place10; $num3_place100 = $val->num3_place100; $num3_array = [$num3_place100, $num3_place10, $num3_place1]; $num3_array = array_unique($num3_array); //重複排除 //1回前 if ($idx == 0) { foreach ($num3_array as $num) { $record[$num] += 3; $pre1[$num] = 1; } } //2回前 if ($idx == 1) { foreach ($num3_array as $num) { $record[$num] += 3; $pre2[$num] = 1; } } //3回前 if ($idx == 2) { foreach ($num3_array as $num) { if ($pre1[$num] == 1 && $pre2[$num] == 1) { //1回前と2回前の両方に出現している場合、何もしない } elseif ($pre1[$num] == 1 || $pre2[$num] == 1) { //1回前と2回前のどちらかに出現している場合、3回目にも出現していれば加点 $record[$num] += 2; $pre3[$num] = 1; } } } //4回前 if ($idx == 3) { foreach ($num3_array as $num) { if ($pre1[$num] == 0 && $pre2[$num] == 1 && $pre3[$num] == 0) { //2回前だけに出現している場合、4回目を見る $record[$num] += 1; } } } } ・・・(ここで最頻値による加点)・・・ } |
C菜
|
//ナンバーズ3における曜日と六曜の最頻値
//$type:1…曜日(七曜)ごと,2…六曜ごと //$value:SixSevenWeekコンポーネントで求めた配列 //戻り値は最頻値の添字を配列で返す private function num3_mode($type, $value) { //二つのタイプの振り分け(データベース検索条件) switch ($type) { case 1:$where = "lottery_week_int={$value['week_int']}"; break; case 2:$where = "lottery_rokuyo_int={$value['rokuyo_int']}"; break; } $field1 = 'num3_place1'; $field10 = 'num3_place10'; $field100 = 'num3_place100'; //numbersテーブルを検索(一の位) $connection = ConnectionManager::get('default'); $sql =<<<EOF select {$field1},count(*) as cnt from numbers where {$where} group by {$field1} order by {$field1}; EOF; $result = $connection->execute($sql)->fetchAll('assoc'); $place1 = (new Collection($result))->extract('cnt')->toList(); //numbersテーブルを検索(十の位) $sql =<<<EOF select {$field10},count(*) as cnt from numbers where {$where} group by {$field10} order by {$field10}; EOF; $result = $connection->execute($sql)->fetchAll('assoc'); $place10 = (new Collection($result))->extract('cnt')->toList(); //numbersテーブルを検索(百の位) $sql =<<<EOF select {$field100},count(*) as cnt from numbers where {$where} group by {$field100} order by {$field100}; EOF; $result = $connection->execute($sql)->fetchAll('assoc'); $place100 = (new Collection($result))->extract('cnt')->toList(); //全ての桁を合計する $result_array = []; for ($i = 0; $i < 10; $i++) { $result_array[$i] = $place1[$i] + $place10[$i] + $place100[$i]; } //最頻値の添字を求める $max = max($result_array); $max_idxs = array_keys($result_array, $max, true); return $max_idxs; } |
A子
…ということは、さっきのB美のコードの末尾に以下のコードを追加すれば良いのかな?
|
//最頻値で加点
$seven_array = $this->num3_mode(1, $seven); foreach ($seven_array as $num) { $record[$num] += 1; } $six_array = $this->num3_mode(2, $six); foreach ($six_array as $num) { $record[$num] += 1; } |
これでどうよ
B美
A子もなかなかやるようになったわね(感心、感心…)
それじゃ、ここまでをテストするためにJSONの中に「$record」を入れてみましょう
|
//予想結果
$prediction = [ 'num3' => null ]; |
の箇所をこうするの
|
//予想結果
$prediction = [ 'num3' => $record ]; |
これで各数字のスコアが分かるってわけ
C菜
A子
んじゃ、ブラウザからアクセスしてみよう
ちなみに、アクセスした日は2026年4月12日の日曜日だったから、この結果は翌日の月曜日分ってことだね
C菜
B美
| 実施回 | 当選番号 |
|---|---|
| 第6956回 | 228 |
| 第6957回 | 941 |
| 第6958回 | 944 |
| 第6959回 | 047 |
どうやらきちんとスコア計算ができてるみたいね
A子
「9」は5点じゃないの?
C菜
(おそらく「1」と「9」に1点ずつ入っていると推測できます~)
A子
いやー
明日(4月13日)の抽選結果が楽しみだね
B美
(ちょっと長くなっちゃったからね(苦笑))
あと、読者の皆さんのほうでも確認できるようにしたから、興味があればこのリンクをクリックしてみてね
(リンクをクリックすると別ウィンドウで開きます)

