N 皇后問題:在 N×N 的棋盤上放置 N 個皇后,使其互不攻擊(同行、同列、同對角線最多一個皇后)。求所有合法擺放方案數。經典回溯算法入門案例。
k 層節點表示前 k 行的擺放選擇。假設已放置皇后位置存於數組 $queens,索引為行號,值為列號。檢查第 $row 行第 $col 列是否合法:
function isSafe($queens, $row, $col) {
foreach ($queens as $r => $c) {
// 同列
if ($c == $col) return false;
// 同主對角線(行差 = 列差)
if (abs($row - $r) == abs($col - $c)) return false;
// 同副對角線已包含在上式中(絕對值)
}
return true;
}
$n = 8; // 棋盤大小
$solutions = []; // 存儲所有解
$queens = []; // 當前部分解:索引=行,值=列
function solveNQueens($row) {
global $n, $solutions, $queens;
if ($row === $n) {
// 找到一個完整解,複製當前狀態
$solutions[] = $queens;
return;
}
for ($col = 0; $col < $n; $col++) {
if (isSafe($queens, $row, $col)) {
$queens[$row] = $col; // 做選擇
solveNQueens($row + 1); // 遞歸下一行
unset($queens[$row]); // 撤銷選擇(回溯)
}
}
}
// 執行
$start = microtime(true);
solveNQueens(0);
$time = microtime(true) - $start;
echo "N=$n 共有 " . count($solutions) . " 種解法\n";
echo "耗時: " . round($time * 1000, 2) . " ms\n";
// 可選:打印前幾個解的棋盤圖
foreach (array_slice($solutions, 0, 3) as $idx => $sol) {
echo "\n解法 #" . ($idx + 1) . ":\n";
for ($r = 0; $r < $n; $r++) {
$line = '';
for ($c = 0; $c < $n; $c++) {
$line .= ($sol[$r] == $c) ? '♛ ' : '· ';
}
echo $line . "\n";
}
}
| 優化點 | 說明 |
|---|---|
| 位運算代替數組 | 用三個整數位掩碼分別表示佔用的列、主對角線、副對角線,O(1) 判斷衝突,極大提速。 |
| 對稱性剪枝 | 利用棋盤對稱性,僅搜索前半列,結果乘 2(奇數 N 中央列單獨處理)。 |
| 迭代代替遞歸 | 手動棧模擬,避免函數調用開銷(PHP 遞歸深度受限)。 |
| C 擴展 / FFI | 核心搜索邏輯用 C 實現,PHP 僅負責調度與輸出。 |
function solveBit($n, $row, $cols, $diag1, $diag2, &$count) {
if ($row === $n) { $count++; return; }
// 可用位置 = ~(cols | diag1 | diag2) & ((1<<$n)-1)
$available = ~($cols | $diag1 | $diag2) & ((1 << $n) - 1);
while ($available) {
$pos = $available & -$available; // 取最低位 1
$available -= $pos;
solveBit($n, $row + 1, $cols | $pos, ($diag1 | $pos) << 1, ($diag2 | $pos) >> 1, $count);
}
}
$count = 0;
solveBit(8, 0, 0, 0, 0, $count); // 8 皇后:92 解,毫秒級完成
原文使用 microtime() 直接相減,但 microtime() 返回字符串 "秒 微秒",直接相減會導致字符串轉換錯誤。正確做法:
$start = microtime(true); // 直接返回浮點秒數 // ... 執行代碼 ... $elapsed = microtime(true) - $start; // 精確到微秒
N 皇后是理解回溯、DFS、剪枝、狀態表示的絕佳案例。PHP 雖非高性能語言,但通過位運算優化也能在毫秒級解決 14 皇后(365,596 解)。實際項目中,若需大規模組合優化搜索,建議將核心算法下沉至 C/C++/Rust,PHP 負責業務協調。