<?php
/*
php数独求解,时间大约在1分钟
*/
$nums[0] = array(0, 5, 0, 0, 1, 0, 0, 0, 9, );
$nums[1] = array(0, 1, 0, 0, 0, 0, 6, 0, 0, );
$nums[2] = array(2, 0, 0, 0, 9, 0, 0, 0, 0, );
$nums[3] = array(0, 4, 0, 0, 0, 3, 7, 0, 0, );
$nums[4] = array(0, 2, 0, 0, 0, 0, 0, 8, 0, );
$nums[5] = array(0, 0, 5, 7, 0, 0, 0, 2, 0, );
$nums[6] = array(0, 0, 0, 0, 5, 0, 0, 0, 1, );
$nums[7] = array(0, 0, 3, 0, 0, 0, 0, 4, 0, );
$nums[8] = array(9, 0, 0, 0, 8, 0, 0, 3, 0, );
findShuDu($nums);
exit;
function findShuDu($arr)
{
$paramArray = array($arr);
//向$paramArray增加数组, 参数用引用传递
cycleArray($paramArray);
$resultArray = $paramArray[count($paramArray) - 1];
echo "\n";
showArray($arr);
echo "\n";
showArray($resultArray);
if (calcArray($resultArray))
{
//echo 'success' . "\n";
}
else
{
echo 'fail' . "\n";
}
}
//验证结果是否有效
function calcArray($arr)
{
for($i = 0; $i < 9; $i ++)
{
$sum1 = 0;
$sum2 = 0;
for($j = 0; $j < 9; $j ++)
{
$sum1 += $arr[$i][$j];
$sum2 += $arr[$j][$i];
}
if (
$sum1 != 45 || $sum2 != 45
)
{
echo 'check fail: ' . $i . ' ' . $j . "\n";
return false;
}
}
return true;
}
//显示结果数组
function showArray($arrLast)
{
for($i = 0; $i < 9; $i ++)
{
//echo "array(";
for($j = 0; $j < 9; $j ++)
{
echo $arrLast[$i][$j];
echo ', ';
}
echo "\n";
//echo "),\n";
}
}
//获取一个位置有效的可选数字的数组
function getPositionAvailArray($arr)
{
$arrNew = array();
for($i = 0; $i < 9; $i ++)
{
for($j = 0; $j < 9; $j ++)
{
//不为0的位置跳过
if ($arr[$i][$j] > 0)
{
continue;
}
//可用数字
$hv = array(1, 2, 3, 4, 5, 6, 7, 8, 9);
//取消行已有数字
for($k = 0; $k < 9; $k ++)
{
$a = $arr[$i][$k];
if ($a > 0)
{
$key = array_search($a, $hv);
if (false !== $key)
{
//$key 为false时不能用unset,不然会出错
unset($hv[$key]);
}
}
}
//取消列已有数字
for($k = 0; $k < 9; $k ++)
{
$a = $arr[$k][$j];
if ($a > 0)
{
$key = array_search($a, $hv);
if (false !== $key)
{
unset($hv[$key]);
}
}
}
//取消小方块中已有数字
$otherI1 = intval($i / 3);
$otherJ1 = intval($j / 3);
$otherI = $otherI1 * 3;
$otherJ = $otherJ1 * 3;
for ($ii = $otherI; $ii<$otherI + 3; $ii ++)
{
for ($jj = $otherJ; $jj<$otherJ + 3; $jj ++)
{
$a = $arr[$ii][$jj];
if ($a > 0)
{
$key = array_search($a, $hv);
if (false !== $key)
{
unset($hv[$key]);
}
}
}
}
if (empty($hv))
{
/*
echo '出现无效数字 ';
echo sprintf("行: %s列: %s\n", $i + 1, $j + 1);
showArray($arr);
*/
return false;
}
$arrNew[$i][$j] = $hv;
}
}
return $arrNew;
}
//递归循环得到有效数组,参数用引用传递,每次得到有效数组后增加到最后
function cycleArray(&$numArray)
{
//取最后一个数组
$arr = $numArray[count($numArray) - 1];
$info = getPositionAvailArray($arr);
if (false === $info)
{
return false;
}
$positionI = -1;
$positionJ = -1;
$availCountMin = 9;
//找出可选数字最少的位置
for($i = 0; $i < 9; $i ++)
{
for($j = 0; $j < 9; $j ++)
{
$availCount = count($info[$i][$j]);
if ($availCount > 0 && $availCount < $availCountMin)
{
$positionI = $i;
$positionJ = $j;
$availCountMin = $availCount;
}
}
}
if ($positionI >= 0 && $positionJ >= 0)
{
$newInfo = $info[$positionI][$positionJ];
foreach ($newInfo as $key => $value)
{
echo sprintf("[%s][%s]: %s;\t", $positionI, $positionJ, $value);
$arr[$positionI][$positionJ] = $value;
//unset的bug存在的地方, 如果用[]构建数组, 同时之前用unset取消一些元素,那么key值可能并不是按顺序递增,所以用count()得到数组元素个数来作为key值.被注释的代码是有问题的代码.
//var_dump(array_keys($numArray));
//$numArray[] = $arr;
//var_dump(array_keys($numArray));
//------------------------------
$numArray[count($numArray)] = $arr;
$ret = cycleArray($numArray);
if ($ret)
{
return true;
}
else
{
//取消最后一个数组元素
unset($numArray[count($numArray) - 1]);
}
}
return false;
}
return true;
}