首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 實作 24 點遊戲演算法:遞迴窮舉四則運算組合完整解析

     編輯:PHP 綜合知識

24 點遊戲規則:給定 4 張撲克牌 (1-13),使用 + - × ÷ 及括號,運算結果等於 24。本文提供基於遞迴回溯的完整 PHP 實作,可輸出所有可行算式。

演算法核心思路

  1. 將每張牌視為獨立運算式 (如 "5")。
  2. 從陣列中任選兩個運算式 A、B,產生 6 種組合:(A+B)、(A-B)、(B-A)、(A×B)、(A÷B)、(B÷A) (除數非零)。
  3. 將組合結果替換回陣列,元素數 -1,遞迴處理。
  4. 當陣列剩 1 元素時,eval() 計算結果,若與 24 差距 < 1e-6 視為成功,拋出例外回傳算式字串。

完整類別實作

<?php
class TwentyFourSolver {
    private $target = 24;
    private $precision = 1e-6;
    private $solutions = [];

    public function solve(array $numbers) {
        $this->solutions = [];
        // 輸入驗證
        if (count($numbers) !== 4) {
            throw new InvalidArgumentException('必須提供 4 個數字');
        }
        foreach ($numbers as $n) {
            if (!is_numeric($n) || $n < 1 || $n > 13) {
                throw new InvalidArgumentException('數字必須為 1-13');
            }
        }
        // 將數字轉為字串運算式陣列
        $exprs = array_map('strval', $numbers);
        try {
            $this->search($exprs, 4);
        } catch (Exception $e) {
            // 找到解時會拋出例外,這裡捕獲並收集
            $this->solutions[] = $e->getMessage();
        }
        return $this->solutions;
    }

    private function search(array &$exprs, int $level) {
        if ($level === 1) {
            // 使用 @ 抑制 eval 警告,結果以字串回傳
            $result = @eval('return ' . $exprs[0] . ';');
            if ($result !== false && abs($result - $this->target) <= $this->precision) {
                throw new Exception($exprs[0]); // 拋出找到的算式
            }
            return;
        }

        // 雙重迴圈選取兩個不同運算式
        for ($i = 0; $i < $level; $i++) {
            for ($j = $i + 1; $j < $level; $j++) {
                $a = $exprs[$i];
                $b = $exprs[$j];

                // 將 $j 位置移至最後,騰出 $i 位置存放新運算式
                $exprs[$j] = $exprs[$level - 1];

                // 6 種運算組合
                $operations = [
                    "($a + $b)",
                    "($a - $b)",
                    "($b - $a)",
                    "($a * $b)",
                ];
                // 除法需檢查除數非零
                if ((float)$b !== 0.0) $operations[] = "($a / $b)";
                if ((float)$a !== 0.0) $operations[] = "($b / $a)";

                foreach ($operations as $op) {
                    $exprs[$i] = $op;
                    $this->search($exprs, $level - 1);
                }

                // 還原陣列狀態 (回溯)
                $exprs[$i] = $a;
                $exprs[$j] = $b;
            }
        }
    }
}

// 使用範例
$solver = new TwentyFourSolver();
$solutions = $solver->solve([4, 8, 8, 8]);
if ($solutions) {
    echo "找到 " . count($solutions) . " 種解法:\n";
    foreach (array_unique($solutions) as $sol) {
        echo "  $sol\n";
    }
} else {
    echo "無解\n";
}
// 輸出範例:
// 找到 3 種解法:
//   ((8 / (8 - 8 / 4)) * 8)
//   ((8 * 8) / (8 / 4)) ... 等
?>

關鍵技術細節

  • 浮點數比較:使用 abs($result - 24) < 1e-6 避免精度誤差。
  • eval() 安全性:此處輸入完全受控 (僅數字與運算子),但生產環境建議自行實作簡易解析器或使用 symfony/expression-language。
  • 去重:相同數字排列會產生重複解,使用 array_unique() 過濾。
  • 效能:4 數字僅 7680 種組合,瞬間完成;擴展到 5-6 數字需加入剪枝 (如中間結果超出合理範圍提前返回)。

擴展方向

  • 支援自訂目標數字 (不只 24)。
  • 加入乘冪、開根號、階乘等進階運算子。
  • 輸出步驟動畫或教學模式。
  • 前端整合:Vue/React + WebWorker 避免阻塞主執行緒。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved