24 點遊戲規則:給定 4 張撲克牌 (1-13),使用 + - × ÷ 及括號,運算結果等於 24。本文提供基於遞迴回溯的完整 PHP 實作,可輸出所有可行算式。
"5")。A、B,產生 6 種組合:(A+B)、(A-B)、(B-A)、(A×B)、(A÷B)、(B÷A) (除數非零)。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 避免精度誤差。symfony/expression-language。array_unique() 過濾。