前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何用模拟退火算法解数独

如何用模拟退火算法解数独

作者头像
HuangWeiAI
发布于 2019-10-15 15:22:58
发布于 2019-10-15 15:22:58
1.8K00
代码可运行
举报
文章被收录于专栏:浊酒清味浊酒清味
运行总次数:0
代码可运行

数独介绍

想必大家都看过或者玩过数独游戏吧。数独游戏是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。

如上图所示,玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复:

这种游戏只需要逻辑思维能力,与数字运算无关。虽然玩法简单,但提供的数字却千变万化,所以不少教育者认为数独是锻炼脑筋的好方法。

数独解法和比赛

关于数独的解法有很多种,直观法解题技巧主要有:唯一解法、基础摒除法、区块摒除法、唯余解法、矩形摒除法、单元摒除法、余数测试法等。

  • 基础摒除法:基础摒除法就是利用1~9的数字在每一行、每一列、每一个九宫格都只能出现一次的规则进行解题的方法。
  • 唯一解法:当某行已填数字的宫格达到8个,那么该行剩余宫格能填的数字就只剩下那个还没出现过的数字了。成为行唯一解。
  • 唯余解法:唯余解法就是某宫格可以添入的数已经排除了8个,那么这个宫格的数字就只能添入那个没有出现的数字。

随着数独发展,各种解法也是层出不穷,可谓是百花齐放。数独游戏也有专业的比赛,比如数独世锦赛是一种世界性的数独比赛,因为参赛选手、国家之多,是目前世界上规模最大的数独比赛。每年举办一届,比赛可谓是云集了各个国家的数独高手!比赛通过层层淘汰,最后决出冠军,冠军将会被授予“数独之王”的荣誉称号!

《最强大脑》节目也引入了数独比赛:

如何用程序解数独

但是今天,我们并不打算给大家详细介绍如何给计算机设计算法来让程序自己解数独。

我们要介绍的这个算法只需要知道数独最基本的规则:并满足每一行、每一列、每一个粗线宫内的数字均含1-9,不重复。除此之外,我们并不会人为给程序设计任何“技巧”,有种“重剑无锋,大巧不工”的感觉。有种那么如此神奇叫什么呢?它就是著名的“模拟退火(simulated annealing)”算法。

模拟退火算法是寻找一个最优解的算法。用形象的话来讲,我们有一座绵延不绝的山,最优解(global minimum)在山的最低谷:

现在有一个难题,就是我们下坡很容易,上坡很难。这样的话,我们容易陷在一个位置较高的谷底,而爬不出来,也就没办法找到最低谷了。

模拟退火可以解决上面的难题,它通过模拟物质中晶体结构的形成:物质 (例如金属) 晶格中的原子可以进入具有较低能级的状态, 或者随着温度降低而保持原位。我们从一个高温出发,这时候爬坡时一件比较容易的事情,可以避免一直限在位置较高的低谷。随着不断降温,我们越来越会走到一些低的位置,最终有一定概率可以找到最低谷。

现在还缺的是,如何把数独看成一个优化问题。其实做法比较简单。我们赋予数独一个“能量”,然后能量计算的规则便是:同一个九宫格,同一行,同一列任何两个数字如果一样那么能量就是1,如果不一样那么能量就是0。只能当总能量为0的时候,此时能量最低,而且满足数独完成条件。所以通过给与数独一个能量的概念和计算规则,我们将数独问题转换成一个寻找最低能量问题。

程序解数独

我们把上面的思路用Python实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import numpy as npimport randomimport timeimport matplotlib.pyplot as plt
def compute_energy(snx, pro, i, j):    en=0; en1=0    for ro in range(9):        if snx[i,j]==snx[ro,j]:            en = en  + 1        if pro==snx[ro,j]:            en1 = en1 + 1        if snx[i,j]==snx[i,ro]:            en  = en  + 1        if pro==snx[i,ro]:            en1 = en1 + 1                 for ro in range(3):        for co in range(3):            if  i<3:                x0 = 0            elif i<6:                x0 = 3            else:                x0 = 6
            if  j<3:                y0 = 0            elif j<6:                y0 = 3            else:                y0 = 6 
            if sn1[i,j]==sn1[ro+x0,co+y0]:                en = en + 1            if pro==sn1[ro+x0,co+y0]:                en1 = en1 + 1    return en, en1             
start = time.time() #------------read file----------------filename = 'exam2.txt'question = open(filename, "r")
#----------initialization--------------sn = np.zeros((9,9)) for i in range(9):    line =question.readline()    sp = line.split()    for j in range(9):        sn[i,j] = float(sp[j]) sn1 = sn.copy()sn1[sn1==0]=1
#----------------program----------------for n in range(300):    print (n)    temp = 1-n/299+0.00001    beta  = 1.0/temp#----------metroplish at T-----------       for imetro in range(200):        for i in range(9):            for j in range(9):                if sn[i,j]!=0:                    continue                en = 0; en1 = 0                pro = random.randint(1,9)                if pro==sn1[i,j]:                    continue                en, en1 = compute_energy(sn1, pro, i, j)                              if (en-3)>=en1:                    sn1[i,j] = pro                elif random.random()<np.exp((en-3-en1)*beta):                     sn1[i,j] = pro total_en = 0 for i in range(9):            for j in range(9):                en, en1 =  compute_energy(sn1, pro, i, j)                total_en = total_en + en  
if total_en-3*81 == 0:    print ('done!')    print (sn1)else:    print ('Have not found the solution.')    
end = time.time()print (str(end-start))

我们找到一个数独题目:

其中0表示待填入的数字,经过程序运行获得了结果:

参考资料

https://baike.baidu.com/item/%E6%95%B0%E7%8B%AC/74847

https://en.wikipedia.org/wiki/Sudoku_solving_algorithms

https://baike.baidu.com/item/%E6%95%B0%E7%8B%AC%E6%8A%80%E5%B7%A7/3533466#2

https://baike.baidu.com/item/%E6%95%B0%E7%8B%AC%E9%94%A6%E6%A0%87%E8%B5%9B

https://en.wikipedia.org/wiki/Sudoku_solving_algorithms

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python与机器学习之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
CompletableFuture:异步编程没那么难
我们在日常开发中经常用多线程优化性能,其实不过就是将串行操作变成并行操作。如果仔细观察,你还会发现在串行转换成并行的过程中,一定会涉及到异步化,例如下面的示例代码,现在是串行的,为了提升性能,我们得把它们并行化,那具体实施起来该怎么做呢?
码农架构
2020/10/26
7470
CompletableFuture:异步编程没那么难
并发编程系列-CompletableFuture
利用多线程来提升性能,实质上是将顺序执行的操作转化为并行执行。仔细观察后,你还会发现在顺序转并行的过程中,一定会牵扯到异步化。举个例子,现在下面这段示例代码是按顺序执行的,为了优化性能,我们需要将其改为并行执行。那具体的实施方法是什么呢?
架构狂人
2023/08/16
2040
并发编程系列-CompletableFuture
阅读 Flink 源码前必会的知识 - Java 8 异步编程 CompletableFuture 全解析
通常来说,程序都是顺序执行,同一时刻只会发生一件事情。如果一个函数依赖于另一个函数的结果,它只能等待那个函数结束才能继续执行,从用户角度来说,整个程序才算执行完毕。
kk大数据
2021/03/13
1.1K0
手把手教学妹CompletableFuture异步化,性能关系直接起飞!
由于 JDK1.5 Futrure 的 get 方法获取任务结果必须阻塞等待,Google 看不下去了,开发了 Guava 库
JavaEdge
2021/04/19
1.3K0
手把手教学妹CompletableFuture异步化,性能关系直接起飞!
CompletableFuture使用详解
在上一篇文章《CompletionService使用与源码分析》中,已经介绍过了Future的局限性,它没法直接对多个任务进行链式、组合等处理,需要借助并发工具类才能完成,实现逻辑比较复杂。
全栈程序员站长
2022/09/07
9440
CompletableFuture使用详解
Future和Callable学习
我们知道使用多线程时,最初的Thread到线程池,此时对于线程的使用,提供了其使用的复用率。而实现多线程的三种方式:继承Thread;实现Runnable接口,重写run方法;实现Callable接口,同时重写call方法,同时通过Future获取执行的返回值。也就是说callable执行任务,而Future拿到执行的结果。Future具有阻塞性在于其get()方法具有阻塞性,而isDone()是不具有阻塞性的。
路行的亚洲
2020/07/16
5050
聊聊异步编程的 7 种实现方式
当用户创建一笔电商交易订单时,要经历的业务逻辑流程还是很长的,每一步都要耗费一定的时间,那么整体的RT就会比较长。
微观技术
2022/09/28
6260
聊聊异步编程的 7 种实现方式
CompletableFuture 异步多线程,那叫一个优雅
虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,我们必须使用Future.get()的方式阻塞调用线程,或者使用轮询方式判断 Future.isDone 任务是否结束,再获取结果。
程序员大彬
2023/03/01
1.7K0
CompletableFuture 异步多线程,那叫一个优雅
大厂常用工具类Completablefuture的面试题都有哪些?这次我都替你收集好了。
最近在复习自己的简历,发现Completablefuture这个工具类怎么突然这么爱考了,牛客上的面经基本都在问这个工具类。因此今天我们就来专门总结一下Completablefuture常见的面试题。
程序员牛肉
2025/03/17
2920
大厂常用工具类Completablefuture的面试题都有哪些?这次我都替你收集好了。
有了Future为什么还要CompletableFuture?
cheese
2024/02/06
2510
有了Future为什么还要CompletableFuture?
不会用Java Future,我怀疑你泡茶没我快, 又是超长图文!!
现陆续将Demo代码和技术文章整理在一起 Github实践精选 ,方便大家阅读查看,本文同样收录在此,觉得不错,还请Star
用户4172423
2020/07/14
5740
理解Java8里面CompletableFuture异步编程
其中第三个特性,就是今天我们想要聊的话题,正是因为CompletableFuture的出现,才使得使用Java进行异步编程提供了可能。
我是攻城师
2018/11/23
16.7K0
CompletableFuture 使用详解
没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同。
java404
2018/10/10
4.1K1
CompletableFuture详解
因为CompletableFuture实现了Future接口所以先看一下Future
用户10136162
2022/11/15
1.1K0
CompletableFuture详解
异步编程利器:CompletableFuture详解
最近刚好使用CompeletableFuture优化了项目中的代码,所以跟大家一起学习CompletableFuture。
捡田螺的小男孩
2021/06/15
7K0
异步编程利器:CompletableFuture详解
一网打尽:异步神器 CompletableFuture 万字详解!
CompletableFuture是jdk8的新特性。CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步会点、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。
搜云库技术团队
2023/09/18
2.7K0
一网打尽:异步神器 CompletableFuture 万字详解!
【并发编程】异步编程CompletableFuture实战
在JDK8之前,我们使用的Java多线程变成,主要是 Thread+Runnable 来完成,但是这种方式有个弊端就是没有返回值。如果想要返回值怎么办呢,大多数人就会想到 Callable + Thread 的方式来获取到返回值。
互联网小阿祥
2023/05/28
1.1K0
【并发编程】异步编程CompletableFuture实战
CompletableFuture:supplyAsync与runAsync
CompletableFuture是Java 8中引入的一个类,用于简化异步编程和并发操作。它提供了一种方便的方式来处理异步任务的结果,以及将多个异步任务组合在一起执行。CompletableFuture支持链式操作,使得异步编程更加直观和灵活。
不惑
2023/11/14
1.3K0
聊聊Java中CompletableFuture的使用
CompletableFuture是java8引入的一个异步类,它最大的优势是可以在创建的对象中传入一个回调对象,在任务结束后(done或throw exception),自动调用回调对象的回调方法,而不用让主线程阻塞。
jinjunzhu
2020/08/20
9090
Java8异步利器CompletableFuture的骚操作
这篇关于CompletableFuture的文章在前一个月就写了一部分,后面没有时间去写,今天周末,所以就抽时间把它写完,因为CompletableFuture中的函数确实很多,也没必要一个一个的去写完,只是抽出大致的函数来说,因为CompletableFuture很像ES6中的Promise()函数,所以我们在学习的时候可以带着Promise()的思想去学习,异步编程不但能够提升我们的相应速度,也能使我们的代码更加简洁,但是我们是在用异步编程的时候也要充分考虑业务和方法是否合适异步操作,不然将会带来一些问题。
小四的技术之旅
2022/07/26
1.7K0
Java8异步利器CompletableFuture的骚操作
推荐阅读
相关推荐
CompletableFuture:异步编程没那么难
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验