MATLAB数独求解程序
使用回溯算法实现数独求解。这个程序可以解决任何有效的数独谜题,并提供了图形用户界面(GUI)用于输入和显示数独。
程序
classdef SudokuSolver < handle
% SUDOKUSOLVER 数独求解器类
properties
board % 数独棋盘 (9x9矩阵,0表示空格)
originalBoard % 原始数独棋盘(用于区分用户输入和求解结果)
solution % 数独的解
isSolved = false % 标记是否已解决
% GUI组件
fig % 主窗口
axesHandle % 数独棋盘坐标轴
cellTexts % 格子文本对象 (9x9)
solveButton % 求解按钮
clearButton % 清除按钮
loadButton % 加载示例按钮
statusText % 状态文本
end
methods
function obj = SudokuSolver()
% SUDOKUSOLVER 构造函数
obj.initializeBoard();
obj.createGUI();
end
function initializeBoard(obj)
% INITIALIZEBOARD 初始化数独棋盘
obj.board = zeros(9, 9);
obj.originalBoard = zeros(9, 9);
obj.isSolved = false;
end
function createGUI(obj)
% CREATEGUI 创建图形用户界面
% 创建主窗口
obj.fig = figure('Name', 'MATLAB数独求解器', ...
'NumberTitle', 'off', ...
'MenuBar', 'none', ...
'Position', [100, 100, 600, 650], ...
'Resize', 'off', ...
'CloseRequestFcn', @(~,~) delete(obj));
% 创建数独棋盘坐标轴
obj.axesHandle = axes('Parent', obj.fig, ...
'Position', [0.1, 0.25, 0.8, 0.7], ...
'XLim', [0, 9], ...
'YLim', [0, 9], ...
'YDir', 'reverse', ...
'XTick', [], ...
'YTick', [], ...
'Box', 'on', ...
'DataAspectRatio', [1, 1, 1]);
% 绘制网格线
hold on;
for i = 0:9
lineWidth = 1;
if mod(i, 3) == 0
lineWidth = 2; % 每3条线加粗
end
line([i, i], [0, 9], 'Color', 'k', 'LineWidth', lineWidth);
line([0, 9], [i, i], 'Color', 'k', 'LineWidth', lineWidth);
end
hold off;
% 创建格子文本对象
obj.cellTexts = gobjects(9, 9);
for row = 1:9
for col = 1:9
obj.cellTexts(row, col) = text(col-0.5, row-0.5, '', ...
'HorizontalAlignment', 'center', ...
'VerticalAlignment', 'middle', ...
'FontSize', 16, ...
'FontWeight', 'bold', ...
'ButtonDownFcn', {@obj.cellClicked, row, col});
end
end
% 创建按钮
obj.solveButton = uicontrol('Style', 'pushbutton', ...
'String', '求解', ...
'Position', [50, 20, 80, 30], ...
'Callback', @(~,~) obj.solveSudoku(), ...
'FontSize', 12);
obj.clearButton = uicontrol('Style', 'pushbutton', ...
'String', '清除', ...
'Position', [150, 20, 80, 30], ...
'Callback', @(~,~) obj.clearBoard(), ...
'FontSize', 12);
obj.loadButton = uicontrol('Style', 'pushbutton', ...
'String', '加载示例', ...
'Position', [250, 20, 100, 30], ...
'Callback', @(~,~) obj.loadExample(), ...
'FontSize', 12);
% 创建状态文本
obj.statusText = uicontrol('Style', 'text', ...
'String', '请输入数独题目', ...
'Position', [370, 20, 180, 30], ...
'FontSize', 12, ...
'HorizontalAlignment', 'left');
% 设置窗口键盘回调
set(obj.fig, 'KeyPressFcn', @obj.keyPressed);
% 更新显示
obj.updateDisplay();
end
function cellClicked(obj, ~, ~, row, col)
% CELLCLICKED 单元格点击回调函数
if obj.isSolved
return;
end
% 获取当前值
currentValue = obj.board(row, col);
% 循环增加数值 (0->1->2->...->9->0)
newValue = mod(currentValue, 9) + 1;
% 更新棋盘
obj.board(row, col) = newValue;
obj.originalBoard(row, col) = newValue;
% 更新显示
obj.updateDisplay();
end
function keyPressed(obj, ~, event)
% KEYPRESSED 键盘按键回调函数
if obj.isSolved
return;
end
% 获取当前选中的对象
currentObj = gco;
% 检查是否是文本对象
if isempty(currentObj) || ~any(currentObj == obj.cellTexts(:))
return;
end
% 找到点击的单元格
[row, col] = find(obj.cellTexts == currentObj);
% 处理数字键
if ismember(event.Key, {'0','1','2','3','4','5','6','7','8','9'})
newValue = str2double(event.Key);
obj.board(row, col) = newValue;
obj.originalBoard(row, col) = newValue;
% 处理退格键和删除键
elseif ismember(event.Key, {'backspace', 'delete'})
obj.board(row, col) = 0;
obj.originalBoard(row, col) = 0;
end
% 更新显示
obj.updateDisplay();
end
function updateDisplay(obj)
% UPDATEDISPLAY 更新数独棋盘显示
for row = 1:9
for col = 1:9
value = obj.board(row, col);
if value == 0
set(obj.cellTexts(row, col), 'String', '');
else
set(obj.cellTexts(row, col), 'String', num2str(value));
end
% 设置文本颜色
if obj.originalBoard(row, col) ~= 0
set(obj.cellTexts(row, col), 'Color', 'blue'); % 原始题目为蓝色
else
set(obj.cellTexts(row, col), 'Color', 'red'); % 求解结果为红色
end
end
end
end
function solveSudoku(obj)
% SOLVESUDOKU 求解数独
set(obj.statusText, 'String', '正在求解...');
drawnow;
% 复制当前棋盘
obj.solution = obj.board;
% 检查数独是否有效
if ~obj.isValidSudoku(obj.solution)
set(obj.statusText, 'String', '无效的数独题目!');
return;
end
% 尝试求解
if obj.solve(obj.solution)
obj.isSolved = true;
obj.board = obj.solution;
set(obj.statusText, 'String', '求解完成!');
else
set(obj.statusText, 'String', '无解!');
end
% 更新显示
obj.updateDisplay();
end
function success = solve(obj, board)
% SOLVE 递归回溯求解数独
% 找到一个空位置
[row, col] = obj.findEmptyCell(board);
% 如果没有空位置,说明已经解决
if row == -1
success = true;
return;
end
% 尝试填入1-9的数字
for num = 1:9
if obj.isValidMove(board, row, col, num)
% 尝试填入数字
board(row, col) = num;
% 递归尝试解决剩下的部分
if obj.solve(board)
success = true;
return;
end
% 如果失败,回溯
board(row, col) = 0;
end
end
% 如果没有数字有效,返回失败
success = false;
end
function [row, col] = findEmptyCell(~, board)
% FINDEMPTYCELL 找到下一个空单元格
for row = 1:9
for col = 1:9
if board(row, col) == 0
return;
end
end
end
row = -1;
col = -1;
end
function valid = isValidMove(~, board, row, col, num)
% ISVALIDMOVE 检查在指定位置填入数字是否有效
% 检查行
if any(board(row, :) == num)
valid = false;
return;
end
% 检查列
if any(board(:, col) == num)
valid = false;
return;
end
% 检查3x3宫格
startRow = 3 * floor((row-1)/3) + 1;
startCol = 3 * floor((col-1)/3) + 1;
if any(any(board(startRow:startRow+2, startCol:startCol+2) == num))
valid = false;
return;
end
valid = true;
end
function valid = isValidSudoku(obj, board)
% ISVALIDSUDOKU 检查数独题目是否有效
% 检查行
for row = 1:9
rowValues = board(row, :);
rowValues = rowValues(rowValues ~= 0);
if length(rowValues) ~= length(unique(rowValues))
valid = false;
return;
end
end
% 检查列
for col = 1:9
colValues = board(:, col);
colValues = colValues(colValues ~= 0);
if length(colValues) ~= length(unique(colValues))
valid = false;
return;
end
end
% 检查3x3宫格
for boxRow = 1:3
for boxCol = 1:3
startRow = 3 * (boxRow-1) + 1;
startCol = 3 * (boxCol-1) + 1;
boxValues = board(startRow:startRow+2, startCol:startCol+2);
boxValues = boxValues(boxValues ~= 0);
if length(boxValues) ~= length(unique(boxValues))
valid = false;
return;
end
end
end
valid = true;
end
function clearBoard(obj)
% CLEARBOARD 清除数独棋盘
obj.initializeBoard();
obj.updateDisplay();
set(obj.statusText, 'String', '棋盘已清除');
obj.isSolved = false;
end
function loadExample(obj)
% LOADEXAMPLE 加载示例数独题目
% 一个中等难度的数独示例
example = [
5, 3, 0, 0, 7, 0, 0, 0, 0;
6, 0, 0, 1, 9, 5, 0, 0, 0;
0, 9, 8, 0, 0, 0, 0, 6, 0;
8, 0, 0, 0, 6, 0, 0, 0, 3;
4, 0, 0, 8, 0, 3, 0, 0, 1;
7, 0, 0, 0, 2, 0, 0, 0, 6;
0, 6, 0, 0, 0, 0, 2, 8, 0;
0, 0, 0, 4, 1, 9, 0, 0, 5;
0, 0, 0, 0, 8, 0, 0, 7, 9;
];
obj.board = example;
obj.originalBoard = example;
obj.isSolved = false;
obj.updateDisplay();
set(obj.statusText, 'String', '示例已加载');
end
function delete(obj)
% DELETE 析构函数
if ishandle(obj.fig)
delete(obj.fig);
end
end
end
methods (Static)
function run()
% RUN 启动数独求解器
solver = SudokuSolver();
end
function solveFromCommandLine(board)
% SOLVEFROMCOMMANDLINE 从命令行求解数独
% 输入: 9x9矩阵,0表示空格
if nargin == 0
% 如果没有提供输入,使用示例
board = [
5, 3, 0, 0, 7, 0, 0, 0, 0;
6, 0, 0, 1, 9, 5, 0, 0, 0;
0, 9, 8, 0, 0, 0, 0, 6, 0;
8, 0, 0, 0, 6, 0, 0, 0, 3;
4, 0, 0, 8, 0, 3, 0, 0, 1;
7, 0, 0, 0, 2, 0, 0, 0, 6;
0, 6, 0, 0, 0, 0, 2, 8, 0;
0, 0, 0, 4, 1, 9, 0, 0, 5;
0, 0, 0, 0, 8, 0, 0, 7, 9;
];
end
% 创建求解器实例
solver = SudokuSolver();
solver.board = board;
solver.originalBoard = board;
solver.updateDisplay();
% 求解
solver.solveSudoku();
% 显示结果
if solver.isSolved
fprintf('数独求解成功:\n');
disp(solver.board);
else
fprintf('数独无解\n');
end
end
end
end
方法一:使用图形界面
-
在MATLAB命令窗口中运行:
SudokuSolver.run(); -
在打开的图形界面中:
- 点击格子可以循环输入数字1-9,再次点击会清零
- 也可以使用键盘直接输入数字(0-9)
- 点击"求解"按钮求解数独
- 点击"清除"按钮清空棋盘
- 点击"加载示例"按钮加载一个示例数独
方法二:从命令行求解
% 定义一个数独矩阵(0表示空格)
sudokuBoard = [
5, 3, 0, 0, 7, 0, 0, 0, 0;
6, 0, 0, 1, 9, 5, 0, 0, 0;
0, 9, 8, 0, 0, 0, 0, 6, 0;
8, 0, 0, 0, 6, 0, 0, 0, 3;
4, 0, 0, 8, 0, 3, 0, 0, 1;
7, 0, 0, 0, 2, 0, 0, 0, 6;
0, 6, 0, 0, 0, 0, 2, 8, 0;
0, 0, 0, 4, 1, 9, 0, 0, 5;
0, 0, 0, 0, 8, 0, 0, 7, 9;
];
% 求解数独
SudokuSolver.solveFromCommandLine(sudokuBoard);
参考代码 数独求解程序 www.youwenfan.com/contentcnf/23166.html
说明
这个数独求解器使用回溯算法,这是一种经典的暴力搜索算法,适用于解决约束满足问题如数独。算法步骤如下:
- 找到下一个空单元格
- 尝试填入数字1-9
- 检查填入的数字是否满足数独规则(行、列、3x3宫格内无重复)
- 如果有效,递归尝试解决剩下的部分
- 如果所有数字都无效,回溯到上一步
这个数独求解器展示了MATLAB在实现算法和创建图形用户界面方面的强大能力。
浙公网安备 33010602011771号