数独算法

程序 = 数据结构+算法

说明:

1.   在计算机内部使用0..8表示1..9。在pc和用户之间的接口使用1..9,这里的人机接口是显示和矩阵初始化配置。

2. 需要一个配置文件,用来配置数独的原始数据,大致内容如下

// memo 

// format x,y,v 

// line begining with '/' or ' ' will be ignored 

1,4,3 

1,9,4



数据结构

1. int16_t maze[x][y]  用来表示数独的状态。 

     a. 如果maze[x][y]<0,那么maze[x][y]上有确定的值为-maze[x][y]。比如,如果maze[0][0]=-7,意思是maze[0][0]是确定的数字为7。

     b.如果maze[x][y]>0,表示该x,y位置上可以使用那些可能的数字。比如,如果maze[0][0]=7=B000000111,表示位置[0][0]只可能放0, 1, 2,其他的数字绝对不可能;

2. uint16_t mask[SCALE+1]   用来保存位数的bit表示,比如针对0,mask[0]=000000001。使用mask主要做位运算,计算某个位置上可以放置那些数字

3. int16_t*maze_p[SCALE*3][SCALE]   maze_p是对maze的不同视角的解释,包括:以行的角度解释maze,以列的角度解释maze,和以cell(一个宫格)解释的maze。具体作用后面分析算法做解释。

 

算法:

数独的算法其实很简单,不需要复杂的什么深度搜索,核心思想就是:针对某些element,找到唯一解,然后再多次迭代,依次找到其他的值。如何找唯一解是关键。我们知道,数独的约束条件是:每行、每列和每个cell(宫)都是0..8,不能重复。因此,找唯一解的方法是:统计0..8各个数字出现的可能性,如果可能性只能是1,就是唯一解。

以下面红色maze[0][0]为例:

1.  首先看第一行,maze[0][0]只可能是数字12478,用二进制表示就是 110010110

2.  再看第一列,maze[0][0]只可能是数字023456,用二进制表示就是001111101。和第一个条件与,110010110 && 001111101 = 000010100。这里得出的结论就是,maze[0][0]只能是2和4.

3.  再看第一个cell(宫格), maze[0][0]只可能是数字125678,用二进制表示就是111100110。和前面的结果与,111100110 && 000010100= 000000100。这里得出的结论就是,maze[0][0]只能是2.

 

0

 

 

5

3

 

 

6

 

 

 

 

 

 

 

 

 

 

3

4

 

 

 

 

 

 

8

 

 

 

 

 

 

 

 

7

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

算法的思想是:依次计算每行,每列,和每个宫,计算每个element里面可以存放数字的可能性。如果某个数字的可能性是1,就说明找到了这个element的唯一解。

就这样,多次迭代后,很快就可以算出所有的解。

 

下面是使用后面的源程序的结果。第一个矩阵是原始数据,'-'表示未确定之数。下面的这个例子经过9次迭代,就算出了结果。

- - - 3 - - - 8 4
- 4 2 5 8 - - - -
- - 5 4 - 7 - 9 -
7 8 - 1 - - - 6 -
- 6 1 8 5 - - - -
- - 3 - 7 - 8 - -
- 9 - 7 - - 6 - 8
- 2 - - 4 8 - - -
- - 8 - 9 - 4 - 3


- - - 3 - - 5 8 4
- 4 2 5 8 - - - -
8 3 5 4 - 7 - 9 -
7 8 - 1 - - - 6 -
- 6 1 8 5 - - - -
- - 3 9 7 6 8 - -
- 9 - 7 - - 6 - 8
3 2 - - 4 8 - - -
- - 8 - 9 - 4 - 3


- - - 3 - - 5 8 4
- 4 2 5 8 - - - -
8 3 5 4 - 7 - 9 -
7 8 - 1 - - - 6 5
- 6 1 8 5 - - - -
- - 3 9 7 6 8 - -
- 9 - 7 - - 6 - 8
3 2 - 6 4 8 - - -
- - 8 2 9 - 4 - 3


- 7 6 3 - - 5 8 4
- 4 2 5 8 - - - -
8 3 5 4 - 7 - 9 -
7 8 - 1 - - - 6 5
- 6 1 8 5 - - - -
- - 3 9 7 6 8 - -
- 9 - 7 - - 6 2 8
3 2 - 6 4 8 - 5 -
6 - 8 2 9 - 4 - 3


- 7 6 3 - - 5 8 4
- 4 2 5 8 - - - 6
8 3 5 4 6 7 - 9 -
7 8 9 1 - - - 6 5
- 6 1 8 5 - - - -
- - 3 9 7 6 8 - -
5 9 - 7 - - 6 2 8
3 2 7 6 4 8 - 5 -
6 1 8 2 9 - 4 - 3


- 7 6 3 - - 5 8 4
- 4 2 5 8 - 7 - 6
8 3 5 4 6 7 - 9 -
7 8 9 1 - 4 - 6 5
- 6 1 8 5 - 9 - 7
- 5 3 9 7 6 8 - -
5 9 4 7 - - 6 2 8
3 2 7 6 4 8 - 5 -
6 1 8 2 9 5 4 7 3


- 7 6 3 - - 5 8 4
- 4 2 5 8 - 7 3 6
8 3 5 4 6 7 - 9 -
7 8 9 1 - 4 3 6 5
- 6 1 8 5 - 9 4 7
- 5 3 9 7 6 8 1 2
5 9 4 7 - - 6 2 8
3 2 7 6 4 8 1 5 9
6 1 8 2 9 5 4 7 3


- 7 6 3 - 2 5 8 4
- 4 2 5 8 9 7 3 6
8 3 5 4 6 7 2 9 1
7 8 9 1 2 4 3 6 5
2 6 1 8 5 3 9 4 7
4 5 3 9 7 6 8 1 2
5 9 4 7 3 1 6 2 8
3 2 7 6 4 8 1 5 9
6 1 8 2 9 5 4 7 3


9 7 6 3 1 2 5 8 4
1 4 2 5 8 9 7 3 6
8 3 5 4 6 7 2 9 1
7 8 9 1 2 4 3 6 5
2 6 1 8 5 3 9 4 7
4 5 3 9 7 6 8 1 2
5 9 4 7 3 1 6 2 8
3 2 7 6 4 8 1 5 9
6 1 8 2 9 5 4 7 3


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "main.h"

int16_t maze[SCALE][SCALE];
uint16_t mask[SCALE+1];
int16_t* maze_p[SCALE*3][SCALE];	
char s_buf[1024];

void preConfig()
	{
	char *file = "config.txt";
	FILE* fh = fopen(file, "r");
	char *token;
	
	if(!fh)
		{
		printf("cannot open %s\n", file);
		return;
		}

	while(fgets(s_buf, 1024, fh)){
		int counter = 0;
		int x=0,y=0,v=0;
		
		if(('/' == s_buf[0]) || (' ' == s_buf[0]) || ('\n' == s_buf[0]))
			continue;

		token = strtok( s_buf, ",");   
		while(token !=NULL)  
			{
			if(0 == counter)
			 	x = atoi(token);
			 else if(1 == counter)
			 	y = atoi(token);
			 else if(2 == counter){
			 	v = atoi(token);
				break;
			 	}
			 
			 counter++;
			 token=strtok(NULL, ","); 
			} 

		//check validation
		if((x>SCALE) || (y>SCALE) ||(v>SCALE))
			{
			printf("%d %d %d each should be less than %d", x,y,v, SCALE);
			continue;
			}
		else
			; //printf("%d %d %d %d\n", x,y,v, counter);
		
		if(counter>0)
			maze[x-1][y-1] = -(v-1); 
			
		}
	}

	
void init()
{
	int i,j,m,n;
	int counter;
	int x,y;
	
	j=1;
	mask[0]=1;
	for(i=1;i<SCALE;i++){
		j *=2;
		mask[i]=j;
		}
	mask[SCALE] = (2<<(SCALE-1))-1;
	
	for(i=0;i<SCALE;i++)
		for(j=0;j<SCALE;j++)
			maze[i][j] = mask[SCALE];

	counter=0;
	for(i=0;i<SCALE;i++)
		{
		for(j=0;j<SCALE;j++)
			maze_p[counter][j] = &maze[i][j];
		counter++;	
		}
	
	for(i=0;i<SCALE;i++)
		{
		for(j=0;j<SCALE;j++)
			maze_p[counter][j] = &maze[j][i];
		counter++;
		}
	
	for(i=0;i<CELL_SIZE;i++)
		for(j=0;j<CELL_SIZE;j++)
			{
			for(m=0;m<CELL_SIZE;m++)
				for(n=0;n<CELL_SIZE;n++)
					{
					int t = m*CELL_SIZE+n;
					x = i*CELL_SIZE + m;
					y = j*CELL_SIZE + n;
					maze_p[counter][t] = &maze[x][y];
					}
			
			counter++;
			}
	
	preConfig();
}

char* printB(uint16_t v){
	static char string[10];
	int i;

	for(i=0;i<SCALE;i++) 	string[i] = '0';
	
	i=SCALE;
	while(i-->0)
		{
		string[i] = v%2?'1':'0';
		v=v/2;
		}
	return string;
	}
	
void printMaze()
	{
	int i,j;
	for(i=0;i<SCALE;i++)
		{
		for(j=0;j<SCALE;j++)
			{
			printf("%c ", maze[i][j]>0?'-':(-maze[i][j]+'1'));
			}
		printf("\n");
		}

	printf("\n");
	}

bool isSolved()
{
	int i,j;
	
	for(i=0;i<SCALE;i++)
		for(j=0;j<SCALE;j++)
			if(maze[i][j]>0)
				return false;

	return true;
}

void statistics(uint8_t* number_static, int16_t value, uint8_t* array, int num)
{
	int i=0;
	while(i<SCALE)
		{
		if(value %2){
			number_static[i]++;
			array[i] = num;		//use array to remember the location of the number i
			}

		i++;
		value = value / 2;
		}
}

void process()
	{
	int i,j;
	int result;
	int16_t* element;
	uint8_t stat[SCALE];
	uint8_t loc_marker[SCALE];
	
	for(i=0;i<3*SCALE;i++)
		{
		//get the mask for the 9 elements
		result=0;
		for(j=0;j<SCALE;j++)
			{
			element = maze_p[i][j];
			if(*element<=0)
				result |= mask[-*element];
			}
		//apply the mask to each >0 element 
		for(j=0;j<SCALE;j++)
			{
			element = maze_p[i][j];
			if(*element>0)
				*element &= ~result;
			}
		//statistics
		for(j=0;j<SCALE;j++)
			{
			stat[j]=0x0;
			loc_marker[j]=0;
			}
		for(j=0;j<SCALE;j++)
			{
			element = maze_p[i][j];
			if(*element>0)
				{//to statistics
				statistics(stat, *element, loc_marker, j);
				}
			else
				{
				stat[-*element]=0;	//means number of j has been decided
				}
			}
		//check statistics results
		for(j=0;j<SCALE;j++)
			{
			if(1 == stat[j])
				{//find answer for number of j
				*maze_p[i][loc_marker[j]] = -j;
				}
			}
		}
	}

int main()
	{
	int counter=0;
	init();
	//printMaze();

	do{
		process();
		printMaze();
		counter++;
		}while(!isSolved());

	printf("Counter=%d\n",counter);
	return 1;
	}

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数独游戏可以通过编写算法来自动解决。Python是一种流行的编程语言,常用于编写算法。下面是一个简单的数独算法代码示例,用Python编写: ``` def solve_sudoku(board): find = find_empty(board) if not find: return True else: row, col = find for i in range(1,10): if valid(board, i, (row, col)): board[row][col] = i if solve_sudoku(board): return True board[row][col] = 0 return False def valid(board, num, pos): # Check row for i in range(len(board[0])): if board[pos[0]][i] == num and pos[1] != i: return False # Check column for i in range(len(board)): if board[i][pos[1]] == num and pos[0] != i: return False # Check 3x3 box box_x = pos[1] // 3 box_y = pos[0] // 3 for i in range(box_y*3, box_y*3 + 3): for j in range(box_x * 3, box_x*3 + 3): if board[i][j] == num and (i,j) != pos: return False return True def print_board(board): for i in range(len(board)): if i % 3 == 0 and i != 0: print("- - - - - - - - - - - - -") for j in range(len(board[0])): if j % 3 == 0 and j != 0: print(" | ", end="") if j == 8: print(board[i][j]) else: print(str(board[i][j]) + " ", end="") def find_empty(board): for i in range(len(board)): for j in range(len(board[0])): if board[i][j] == 0: return (i, j) return None board = [ [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0] ] # Update the board with valid numbers board = [ [0,2,3,0,0,0,0,8,0], [0,0,0,7,0,0,0,0,1], [6,0,0,0,0,2,0,0,0], [0,3,0,0,0,0,5,0,7], [0,0,9,0,2,0,6,3,0], [0,7,0,0,0,0,9,4,2], [7,0,0,0,0,9,0,0,0], [0,0,0,0,0,3,0,0,0], [2,0,1,0,0,0,0,6,0] ] print_board(board) solve_sudoku(board) print("\nSolved Board:") print_board(board) ``` 这个算法的实现递归调用`find_empty`函数来查找空的格子,然后使用`valid`函数判断这个格子是否可以填写数字,如果可以则填写数字并通过递归调用进行下一步验证,如果无法填写,则返回上一个步骤继续寻找其他可行解。这个算法非常直接简单,适合初学者练习编写算法。 需要注意的是,这个算法并不一定可以解决所有的数独问题,有一些特定类型的问题可能无法解决。因此,如果您需要解决复杂的数独问题,请参考更复杂、更完整的算法实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值