This arctical will also be published in English at http://www.cnblogs.com/LarryAtCNBlog/p/4307965.html
數獨是一個填字遊戲,廣爲人知的是9X9類型的,下面是一個算法,也就是由每次從可確定的數中推算同行或同列的可能值,如果可能值只爲1個,那就可以直接確定該單元格的值,如果可能的值的數量大於或等於2,則做循環和單元格值的假設,推算出答案。由於數獨答案不唯一,因此加了一個參數定義返回的答案數量。
param( # 返回幾個答案 [int]$HowManyAnswersYouWanttoGet = 1 ) $SudokuMatrix = @( @(0,0,0, 0,0,0, 0,0,3), @(0,0,0, 0,0,0, 0,4,0), @(0,5,1, 6,0,0, 0,0,0), @(0,3,0, 0,0,8, 0,0,2), @(9,0,0, 1,6,0, 0,0,0), @(0,6,0, 0,5,4, 0,0,0), @(5,4,0, 0,0,0, 0,2,0), @(0,0,3, 4,0,2, 0,0,0), @(0,0,8, 3,0,0, 7,1,0) ) # 循環每個元素,給沒有值的元素加上 1-9 的數組。 for($i = 0; $i -lt 9; $i++){ for($j = 0; $j -lt 9; $j++){ if(!$SudokuMatrix[$i][$j]){ $SudokuMatrix[$i][$j] = 1..9 }else{ $SudokuMatrix[$i][$j] = @($SudokuMatrix[$i][$j]) } } } # 循環每個元素,比較橫向和縱向數據,從除去所有可能的值。 function GoLoop($arr){ $NewArr = @($null) * 9 for($i = 0; $i -lt 9; $i++){ $NewArr[$i] = $arr[$i].PSObject.Copy() } for($i = 0; $i -lt 9; $i++){ for($j = 0; $j -lt 9; $j++){ if($NewArr[$i][$j].Count -ne 1){ for($k = 0; $k -lt 9; $k++){ if($NewArr[$i][$k].Count -eq 1 -and $NewArr[$i][$j].Count -ne 1){ $NewArr[$i][$j] = @($NewArr[$i][$j] | ?{$_ -ne $NewArr[$i][$k][0]}) } if($NewArr[$k][$j].Count -eq 1 -and $NewArr[$i][$j].Count -ne 1){ $NewArr[$i][$j] = @($NewArr[$i][$j] | ?{$_ -ne $NewArr[$k][$j][0]}) } } } } } return $NewArr } # 循環每個元素,如果可能的值數量變爲0,返回true,說明計算錯誤。 function VerifyZero($arr){ for($i = 0; $i -lt 9; $i++){ for($j = 0; $j -lt 9; $j++){ if($arr[$i][$j].Count -eq 0){ return $i, $j, $true } } } return $i, $j, $false } # 找到元素中可能的值數量最小的那個,返回該元素的位置。 function FindSmallest($arr){ foreach($k in 2..9){ for($i = 0; $i -lt 9; $i++){ for($j = 0; $j -lt 9; $j++){ if($arr[$i][$j].Count -eq $k){ return $i, $j, $k } } } } } # 計算數組中有多少個元素已經被確認了值。 function CountConfirmedNumber($arr){ $NumberConfirmed = 0 for($i = 0; $i -lt 9; $i++){ for($j = 0; $j -lt 9; $j++){ if($arr[$i][$j].Count -eq 1){ $NumberConfirmed++ } } } return $NumberConfirmed } $AnswerCount = 0 $Results = @() function GoCalculate($arr){ $NewArray = GoLoop($arr) # verify no zero option! $ZeroPosition = VerifyZero($NewArray) if($ZeroPosition[2]){ # Write-Host "0 option found: [$($ZeroPosition[0])][$($ZeroPosition[1])]" return } # confirm current numbers if((CountConfirmedNumber($NewArray)) -eq 81){ $Script:AnswerCount++ Write-Host "An answer captured, ID: $AnswerCount" -ForegroundColor Green # Write-Host "81 numbers confirmed." $Script:Results += $null $Script:Results[-1] = $NewArray return } # find the nearest(to [0][0]) and smallest(2 to 9) option element. $Smallest = FindSmallest($NewArray) $OptionsStack = @($NewArray[$Smallest[0]][$Smallest[1]]) # Write-Host "Row: $($Smallest[0]); Col: $($Smallest[1]); Option: $($OptionsStack -join ' ')" foreach($Option in $OptionsStack){ # Write-Host "Set [$($Smallest[0])][$($Smallest[1])] to: $Option" $NewArray[$Smallest[0]][$Smallest[1]] = @($Option) if($AnswerCount -lt $HowManyAnswersYouWanttoGet){ GoCalculate($NewArray) } } } # 觸發 GoCalculate($SudokuMatrix) # 輸出結果 $Results | %{ if($_ -eq $null){return} Write-Host "Answer:" -ForegroundColor Yellow for($i = 0; $i -lt 9; $i++){ for($j = 0; $j -lt 9; $j++){ Write-Host "$($_[$i][$j][0]) " -NoNewline -ForegroundColor yellow } Write-Host "`n" } }