解决数独问题用人工智能还是量子计算?

简介: 解决数独问题用人工智能还是量子计算?

作为一种有趣的棋盘游戏,数独诞生100周年之后,它是如何成为计算研究的焦点之一的呢?探索如何使用人工智能或量子计算机从头开始创建一个智能数独求解器。

640.png

在深入探究之前,先来了解一下历史

马克•布洛赫说:“历史被称为学科之母。”那么,让我们来谈谈著名的数独游戏是如何诞生的吧。这个故事可以追溯到19世纪末,起源于法国。法国日报《世纪报》(Le Siecle)发布了一款9x9大小的猜谜游戏,它需要算术运算而不是逻辑运算,它的数字是两位数,而不是1- 9。它的游戏性质与数独游戏(Sudoku)类似,即把横排、列和对角线的数字相加,也会得到相同的数字。1979年,退休的建筑师和puzzler Howard Garns被认为是现代数独游戏的创造者,该游戏以数字地名的名义首次在戴尔杂志上发表。1986年,日本一家名为Nikoli的拼图公司首次以Sudoku的名字出版了这个拼图。

640.png

在解决数独游戏的问题框架

数独是一个约束满足问题(CSP)的真实例子,因为变量集、域集和约束集都是有限的。我们必须在一个9x9表中输入1-9之间的数字,这样每一行、每列和每3x3子表中的数字都只包含一个数字。Sudoku也存在另一种变化,即Diagonal Sudoku,它在表对角线的每个对角线中都规定了一组额外的约束,每个数字必须准确地具有一次特征。我们知道约束满足域,最优解必须满足所有约束,或更具体地说,它应该遵守游戏规则。最优解将满足集合中的所有约束,从而解决难题。

计算上,可以用非确定性多项式时间(NP)解决求解数独的约束,因为可以使用一些非常特殊的蛮力算法来解决约束,并且也可以在多项式时间内测试解集的有效性,其中输入 该问题与多项式长度的一组解有关。完全解决的数独就是拉丁方格的示例(如Euler所述,n x n数组填充有n个不同的符号)。数独问题可以认为是图形着色问题,其中我们仅需要使用9种颜色对图形进行着色,而裸露的字母可以认为是部分颜色。

使用人工智能算法集满足约束

计算科学的基本原理是依靠逻辑来满足某些约束的能力。在解决数独问题时,我们必须训练求解器以寻找除基本规则外的一些特定的获胜模式。因此,问题在于系统不仅在盲目地遵循规则,而且在考虑其近期和长期影响的同时做出一些决策。这些模式称为启发式。类似于巧遇游戏知识和技巧的专家玩家,仅了解基本规则并不能使他们成为游戏专家。因此,当我们开发算法并解决问题时,我们必须牢记有用的启发式方法,我们还应将其包含在程序中,以使其在获胜时变得更聪明,更有用。

对于我们的Sudoku Solver,我们将输入81个数字的序列作为字符串,并用'。'(句号)表示未解决的数字。为了解决该问题,我们将“。”替换为可以放入该单元格的所有可能数字。

根据数独的限制,我们不能在任何单元格附近的行,列或3x3子正方形中多次使用一个数字。在对角数独的情况下,我们还必须考虑相同的约束。我们首先用所有可能的数字1到9替换句点。我们使用以下grid_values函数以编程方式进行此操作。

#Forthesakeofcaluclationwetakerowsasalphaneumericandcolumnsasnumeric.
rows='ABCDEFGHI'columns='123456789'boxes= [r+cforrinrowsforcincolumns] #everypossiblecellcombinationinthegrid.
defgrid_values(grid):
"""Take in the Unsolved Sudoku Sequence and replaces the unsolved boxes initially with allpossible values which can get into that cell. Lastly returns a dictionary containing thevalues at all cell positions along with cells."""values= []
every_digits='123456789'forningrid:
ifc=='.':   #replacingeveryunsolvedvaluewitheverypossiblevalueinitially.
values.append(every_digits)
else:         #ifalreadysolved, causingitnochange.
values.append(c)
assertlen(values) ==81returndict(zip(boxes, values)) #returningthesudokugridwithallpossiblecellvalues.

640.png

首先在所有未解决的单元格中分配所有可能的值。

现在,我们用1到9之间的所有可能数字替换了未解决的单元格,从数独的基本规则中我们知道,如果数字已经在该行,列和3x3子字段中使用过,我们就不能使用它两次。 因此,让我们消除它们,如果我们在未解决的网格中遇到它们时,我们最初已经用所有可能的数字填充了它们。因此,让我们看一下如何使用消除python方法从未解决的单元中消除那些不相关的数字。

columns_reversed=columns[::-1] #reversingthecolumnsforcalculatingtheDiagonalUnits.
defmake_combinations(m, n):
"""Takes in input of generally iterables and creates all possible combintation out of them.args:a: string iterableb: string iterablereturn : list of all possible combination."""return [x+yforxinmforyinn]
row_units= [make_combinations(r, columns) forrinrows]
column_units= [make_combinations(rows, c) forcincolumns]
sub_square_units= [make_combinations(m, n) formin ('ABC', 'DEF', 'GHI')
fornin ('123','456','789')]
diagonal_1_units= [[rows[i]+columns[i] foriinrange(len(rows))]]
diagonal_2_units= [[rows[i]+columns_reversed[i] foriinrange(len(rows))]]
diagonal_units=diagonal_1_units+diagonal_2_unitsall_units=row_units+column_units+square_units+diagonal_unitsunits=dict((b, [uforuinall_unitsifbinu]) forbinboxes)
peers=dict((b, set(sum(units[b], [])) - {b}) forbinboxes)
defeliminate(values):
"""Eliminate the redundant numbers from the unsolved cells if the number already appeared oncein the peer of the current cell.What we do here is we erase that redundant number from the unsolved value cells if appeared once."""solved_cells= [boxforboxinvalues.keys() iflen(values[box]) ==1] #cellissolvedifthere's only one digitfor box in solved_cells:value_at_cell = values[box]   # retrieve the current value at that cell.for peer in peers[box]:       # check for the cell'speersifthevalueappearsagain.
values[peer] =values[peer].replace(value_at_cell, '')
returnvalues#returnthemodifiedvaluesdictionary.

因此,在满足这些约束的同时,有时我们会遇到一些只能放置一个数字的单元格,但除该数字外,其他数字对于该特定单元格都不再可行。我们首先要填写这些内容。有了适当的解决方案。我们称此为“唯一选择”,它是解决数独网格单元的最简单的启发式方法。

defonly_choice(values):
"""If in order to satisfy the constraints of the Sudoku Puzzle there is only a single viable optionwe fill in the Cell with that option only and thereby obtain a solve for the cell."""forunitinall_units:     #searchingacrossallthevicinityofthecell.
fordigitin'123456789':  
to_be_filled= [cellforcellinunitifunitinvalues[unit]]
iflen(to_be_filled) ==1: #ifthereexistsonlyasinglecellintheunitwhichisnotsolvedvalues[to_be_filled[0]] =digit#Wefillinthecellwithitsproperanswer.    
returnvalues

在迄今为止围绕约束满足的过程中,可能会出现以下情况:一个单元中将有两个未解决的像元(考虑行,列和3x3子正方形),其中只能分配两个特定的剩余数。因此,这两个数字可以有效地从同一单元中其他单元格上的可能数字中删除。这种启发式方法称为裸双胞胎。该算法的实现专门制作了网格值的深层副本,并检查了裸胎双胞胎的可行性,即是否存在两个仅能接受两个特定值的未解决像元,如果可行,它将继续进行并从其他两个值中删除这两个值 同一单元中的单元格。我们使用如下所示的nude_twins函数以编程方式实现它:

defnaked_twins(values):
"""If there are two unsolved cells in a same unit exist such that it can only be filled by onlytwo specific digits, then those two digits can be safely removed from all other cells in the same unit."""twins_possible= [unitforunitinvalues.keys() iflen(values[unit]) ==2]  
twins= [[unit1, unit2] forunit1intwins_possibleforunit2inpeers[unit1]
ifset(values[unit1]) == (set(values[unit2]))]   #confimedNakedTwinsfortwinintwins:
unit1=twin[0]
unit2=twin[2]
peers1=set(peers[unit1])  
peers2=set(peers[unit2])
common_peers=peers1&peers2#findingtheintersectionbetweenthepeersofthetwonakedtwinelementforpeerincommon_peers:
iflen(values[peer]) >1:
forvalueinvalues[unit1]:
values[peer] =values[peer].replace(val, '')) #Erasingthevalues.
returnvalues

现在,我们尝试通过重复应用这三个约束满足算法并检查它是否卡住并且无法进一步减少,来尽可能地减少难题。我们通过使用reduce_puzzle函数以编程方式执行此操作。我们要做的是在for循环中调用前三个函数,并在网格值的输入和输出序列中的已解决单元数相同时终止该函数,这意味着不能再进一步减小它 仅约束满足算法。

defreduce_puzzle(values):
"""Applying the 4 Constraint Satisfaction Algorithms until it is not further reducible.Checking if the Number of Solved Cells between the iteration."""solved_values= [unitforunitinvalues.keys() iflen(values[unit]) ==1] #consideringsolvedcellsstuck=False#booleanflagtodeterminetheendofloopwhilenotstuck:
prev_solved_values=len([unitforunitinvalues.keys() iflen(values[unit]) ==1]) #checkpoint1values=eliminate(values) #applyingEliminationCSPvalues=only_choice(values) #applyingOnlyChoiceCSPvalues=naked_twins(values) #applyingNakedTwinsCSPafter_solved_values=len([unitforunitinvalues.keys() iflen(values[unit]) ==1])
stuck=after_solved_values==prev_solved_values#Gettingoutofloopisthenumberofsolvedcellisstillthesameasthepreviousiteration.
iflen([unitforunitinvalues.keys() iflen(values[unit]) ==0]):
returnFalse#ifthere's problems in the internal representation of the sudoku grid return False.return values# return the reduced grid values.

如果数独网格仍未通过约束满足问题解决,则部分解决方案将到达输出,其中一些单元格仍将分配给某些可能的值。在这种情况下,我们要做的是使用搜索树搜索那些位置中的最佳数字集。我们使用深度优先搜索(DFS)算法遍历搜索树。因此,基本上,使用DFS,我们用相同的网格创建了几个实例,并为每个尚未解决的单元尝试了不同的可能分配。我们递归地要求CSP算法根据搜索结果减少网格。我们以编程方式实现它,如下所示:

defsearch(values):
"""Recursive Depth-First Search: If the sudoku grid is not further reducible by constraint satisfactiona few of the cells will be left with different options and with DFS with search for the optimalvalues for those yet-unsolved cells.  """values=reduce_puzzle(values) #WecalltheReductionFunctiontoreducethepuzzlefurtherbasedonthesearchresultsacrossiterations.
ifvaluesisFalse:
returnFalseifall(len(values[b]) ==1forbinboxes):
print("Sudoku Problem Solved!")
returnvaluesm, n=min((len(values[b]), b) forbinboxesiflen(values[b]) >1)
forvalueinvalues[n]:
new_sudoku=values.copy()
new_sudoku[n] =valueattempted=search(new_sudoku)
ifattempted:
returnattempted

我们使用display sudoku函数将输入的字符串序列显示为二维9x9 Sudoku网格:

defdisplay(values):
"""Display the values as a 2-D grid.Input: The sudoku in dictionary form"""width=1+max(len(values[b]) forbinboxes)
line='+'.join(['-'* (width*3)] *3)
forrinrows:
print(''.join(values[r+c].center(width) + ('|'ifcin'36'else'')
forcincols))
ifrin'CF':
print(line)
return

为了解决数独序列,我们将上述函数调用如下:

if__name__=="__main__":
diag_sudoku_grid='2.............62....1....7...6..8...3...9...7...6..4...4....8....52.............3'values=grid_values(diag_sudoku_grid)
values=reduce_puzzle(values)
values=search(values)
display(values)

输出如下所示,其中一组算法已成功计算出答案。

640.png

解决数独作为约束满足问题的量子方法

现在,我们将尝试使用“量子模拟退火”解决简单的Sudoku网格。首先,什么是模拟退火?对于这样的优化问题,我们的想法是使用一些次优启发式算法,并获得最优的启发式算法集以获得最优解。我们在这里使用DWave AQC模型(糖尿病量子计算)来采样满足前面讨论的约束的最佳解决方案。...

使用DWave Kerberos混合采样器:

在本示例中,我们正在使用DWave随附的混合求解器。它通过运行并行搜索来找出最佳的启发式方法。它是一种混合求解器,因为它同时使用了量子计算和经典的计算特性。它也是一个分解采样器,在处理时使用异步工作流。它包含在DWave Systems的Ocean SDK软件包中。要开始本地开发,请确保您的系统上安装了Python 3.5+,然后发出以下命令。

python-mpipinstall--upgradepippipinstalldwave-ocean-sdk

使用二进制二次模型(BQM)进行计算

我们无法构造直接准备将其馈送到Quantum Computers的约束,我们需要一个中间表示来将其馈入。这就是为什么我们将使用BQM的原因,幸运的是,DWave Ocean SDK已经提供了一种称为“组合”的工具,可用于将约束满足问题归结为BQM。首先,顾名思义,二进制二次模型本身就是一个方程系统,它是二次的,用二进制表示。由于计算的复杂性更高,Quantum计算机使用这些计算可以大大加快开发过程。因此,在游戏中,我们决定使用dimod的组合工具,该工具将返回一个二进制二次模型,该模型对于其输入变量和内部变量的k个组合中的每一个均最小。

我们首先从dwave-ocean-sdk导入必要的软件包,并在实际读入Sudoku Grid之前进行一些完整性检查。

importdimodimportmathimportsysimportdimod.generators.constraintsimportcombinationsfromhybrid.referenceimportKerberosSamplerdefprettify(row, col, digit):
return"{row}, {col}_{digit}".format(row, col, digit)
defread_matrix(filename):
withopen(filename, 'r') asf:
all_lines=f.read()
lines= []
forlineinall_lines:
new_line=line.rstrip()
ifnew_line:
new_line=list(map(int, new_line.split(' ')))
lines.append(new_line)
returnlinesdefsanity_check(matrix):
n=len(matrix)
m=int(math.sqrt(n))
unique_digits=set(range(1, 1+n))
forrowinmatrix:
ifset(row) !=unique_digits:
print("Error in row", row)
returnfalseforjinrange(n):
col= [matrix[i][j] foriinrange(n)]
ifset(col) !=unique_digits:
print("Error in column", col)
subsquare_idx= [(i, j) foriinrange(m) forjinrange(m)]
forr_scalarinrange(m):
forc_scalarinrange(m):
subsquare= [matrix[i+r_scalar*m ][j+c_scalar*m] fori, jinsubsquare_idx]
ifset(subsquare) !=unique_digits:
print('Error in sub-square', subsquare)
returnTruereturnTrue

现在,我们使用Sudoku Grid的行,列和子正方形索引的所有可用变量组合,使用组合工具来创建二进制二次模型。

defmain():
iflen(sys.argv) >1:
filename=sys.argv[1]
matrix=read_matrix(filename)
n=len(matrix)
m=int(math.sqrt(n))
digits=range(1, n+1)
bqm=dimod.BinaryQuadraticModel({}, {}, 0.0, dimod.SPIN)
forrowinrange(n):
forcolinrange(n):
node_digits= [prettify(row, col, digit) fordigitindigits]
one_digit_bqm=combinations(node_digits, 1)
bqm.update(one_digit_bqm)
forrowinrange(n):
fordigitindigits:
row_nodes= [prettify(row, col, digit) forcolinrange(n)]
row_bqm=combinations(row_nodes, 1)
bqm.update(row_bqm)
forcolinrange(n):
fordigitindigits:
col_nodes= [prettify(row, col, digit) forrowinrange(n)]
col_bqm=combinations(col_nodes, 1)
bqm.update(col_bqm)
if__name__=="__main__":
main()

就是这样。我们已经成功实现了两种智能解决方案,其中一种使用经典计算,并且使用了功能非常强大的人工智能启发式算法,甚至可以解决对角数独网格。第二种方法使用异步混合启发式采样器,该采样器也恰好使用绝热量子计算模型的模拟退火来将约束满足问题转换为二进制二次模型以对其进行采样,从而获得最佳采样解。

目录
相关文章
|
2天前
|
机器学习/深度学习 人工智能 自然语言处理
人工智能与情感计算:AI如何理解人类情感
人工智能与情感计算:AI如何理解人类情感
38 20
|
2月前
|
机器学习/深度学习 人工智能 并行计算
量子计算与人工智能:智能革命的新动力
量子计算与人工智能的结合正成为推动社会进步和产业升级的重要力量。量子计算利用量子比特实现高效并行计算,而人工智能则在语音、图像识别等领域取得显著成果。两者结合可加速模型训练、提高计算效率和优化算法,为医疗、智能制造等领域带来深远影响。尽管面临技术成熟度和跨学科人才培养等挑战,但其巨大潜力预示着未来的智能革命。
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
探索量子计算在人工智能领域的应用####
本文深入探讨了量子计算技术在人工智能领域的潜在应用及其革命性影响。文章首先概述了量子计算的基本原理,随后分析了其在机器学习、优化算法及模式识别等AI子领域中的具体应用实例,最后讨论了当前面临的挑战与未来发展趋势。通过对比经典计算与量子计算在处理复杂问题上的差异,揭示了量子计算加速AI进程的可能性。 ####
|
3月前
|
机器学习/深度学习 人工智能 并行计算
量子计算与人工智能:智能革命的新动力
【10月更文挑战第31天】量子计算与人工智能的结合,正成为推动科技进步的重要力量。本文探讨了量子计算的基本原理与优势,分析了人工智能的发展现状与挑战,并展望了两者结合在医疗、金融、交通和智能制造等领域的应用前景。尽管面临技术成熟度和算法设计等挑战,但这场智能革命将为人类社会带来前所未有的变革和机遇。
|
3月前
|
机器学习/深度学习 人工智能 自然语言处理
探索AIGC的底层技术:人工智能通用计算架构
探索AIGC的底层技术:人工智能通用计算架构
259 3
|
3月前
|
机器学习/深度学习 人工智能 并行计算
探索未来科技的边界:人工智能与量子计算的融合之路
【10月更文挑战第14天】 探索未来科技的边界:人工智能与量子计算的融合之路
76 0
|
4月前
|
机器学习/深度学习 人工智能 算法
量子计算与人工智能的融合:智能计算的新篇章
【9月更文挑战第22天】量子计算与人工智能的融合正开启智能计算的新篇章。通过利用量子计算的独特优势,人工智能领域将迎来前所未有的性能提升和全新可能性。随着技术的不断进步和应用场景的不断拓展,我们有理由相信,量子计算与人工智能的融合将引领一场科技革命,为人类社会的发展和进步做出更大贡献。
|
4月前
|
人工智能 算法 网络安全
探索未来:量子计算与人工智能的融合之路
本文将探讨量子计算和人工智能的结合可能性,以及这一结合如何改变我们的未来。我们将深入了解这两个领域的基础知识,分析它们如何相互影响,以及面临的挑战和未来的发展趋势。最后,我们将讨论这一技术革命对个人和社会可能产生的影响。
87 9
|
4月前
|
人工智能 供应链 算法
揭秘未来科技:量子计算与人工智能的融合之路
在探索未知的道路上,科技总是以令人惊叹的速度前进。本文将带你穿越至技术的前沿,解锁量子计算和人工智能这两大科技巨人的秘密联盟。我们将一窥它们如何共同塑造未来,以及这一结合将如何影响我们的生活和工作。准备好了吗?让我们开始这场思想的旅程!
82 3
|
5月前
|
机器学习/深度学习 人工智能 算法
探索未来:量子计算与人工智能的融合之路
在科技飞速发展的今日,量子计算与人工智能的结合被视为开启新时代的钥匙。本文将探讨量子计算的原理、挑战以及其与人工智能结合的可能性和前景。我们将通过案例分析和最新研究数据来揭示这一跨学科领域如何推动技术革新,并讨论其对社会发展的潜在影响。读者将获得对这一激动人心领域的深刻理解,同时引发对未来技术趋势的思考。
117 3