Las Vegas 与回溯组合法解八皇后问题

news2025/1/15 6:44:47

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

在这里插入图片描述

回溯法的原理用伪代码表示如下:

// 回溯法原理
QueenBacktrace{
	while(i < 8){
		检查当前行i,从当前列j开始向后找到一个可以放置的安全列号
		if 找到安全的列号{
			放置皇后,将列号入栈,进入下一行i++
			j = 0; // 下一行将从第0列开始搜索
		}
		else 找不到安全的列号 回溯{
			弹栈回到上一行i--
			移去放置的皇后,弹栈出来的j,j++继续向后搜索
		}
	}
}

然而当八皇后变成n皇后时,回溯法将爆炸性增长。这里将使用Las Vegas的概率算法与回溯法组合求解。
首先是贪心的Las Vegas算法原理:

  • 遍历n行,每一行尝试随机放置一个可以放置的位置,如可放置皇后的位置有{1,3,5,7},从其中随机挑一个

  • 每一次判断是否可以放置使用三种不冲突判断:

    • 无列冲突,任意两个行的列号不同
    • 无45°对角线冲突,即任意两个皇后(x1,y1) (x2,y2),x1-y1 != x2-y2
    • 无135°对角线冲突,即任意两个皇后(x1,y1) (x2,y2),x1+y1 != x2+y2
  • 在第stepVegas行开始,进入回溯法求解

如此是在前stepVegas行的皇后是概率的选取皇后位置,而第stepVegas行后是有回溯法确定的选择。

伪代码如下:

LVQueens(){ 
	col[8] // 存每行的皇后放置的列位置
	diag45 // 存已放置皇后的x-y值
	diag135 // 存以防止的皇后x+y值
	k = 0 // 行号
	repeat{
		nb = 0 // 计数器
		for i <- 0 to 8{ // 遍历8列
			if(i 不属于 col  and  (i-k) 不属于 diag45 and (i+k) 不属于diag135){
				nb += 1
				if random(1,nb)随机数从1~nb中选择出的值==1{
					j = i // 注意这里第一次可行的列号random一定选中了j = i
				}
			}
		}
		if(nb >0){ // 说明找到过可以放置的解
			# 此时第k行的皇后将放置在j位置上
            col.append(j)
            diag45.append(j-k)
            diag135.append(j+k)
            k += 1
		}
		if(nb == 0) return false // 说明没找到解
		if(k == stepVegas):  // 在第stepVegas行开始使用回溯法求解
            return QueenBacktrace(n, k, col, diag45, diag135, ans, count)
	}
}

回溯法和LV算法对比:

  • 回溯法是一定有精确解的
  • LV算法不一定有结果,可能是成功,也有概率失败
  • LV组合回溯算法也是有可能成功,有可能失败

为了衡量不同stepVegas从哪一行开始进行回溯效果最好,引入一个遍历节点消耗的概念:即放置一次皇后作为一次搜索的节点。显然LV算法中每一层只会放置一次皇后,而回溯法因为失败回溯会放置导致进入过错误的节点而节点数更多。

测试时候重复如repeat = 1000次,然后查看这1000次成功的概率为p,成功时候遍历节点的平均数为s,失败遍历的节点数平均为e,那么总的平均次数t为: t = s + ( 1 − p ) e p t = s+ \frac{(1-p)e}p t=s+p(1p)e

由以上的算法去测试结果如下

n = 8, stepVegas = 0 : p = 1.0 s = 114.0 e = 0 t = 114.0
n = 8, stepVegas = 1 : p = 1.0 s = 40.042 e = 0 t = 40.042
n = 8, stepVegas = 2 : p = 0.858 s = 22.884615384615383 e = 39.556338028169016 t = 29.43123543123543
n = 8, stepVegas = 3 : p = 0.518 s = 13.432432432432432 e = 15.236514522821576 t = 27.610038610038607
n = 8, stepVegas = 4 : p = 0.247 s = 10.48582995951417 e = 8.731739707835326 t = 37.10526315789474
n = 8, stepVegas = 5 : p = 0.157 s = 9.178343949044587 e = 7.290628706998814 t = 48.32484076433121
n = 8, stepVegas = 6 : p = 0.145 s = 9.027586206896551 e = 6.990643274853801 t = 50.248275862068965
n = 8, stepVegas = 7 : p = 0.141 s = 9.0 e = 6.933643771827707 t = 51.241134751773046
n = 8, stepVegas = 8 : p = 0.127 s = 9.0 e = 6.953035509736541 t = 56.79527559055118
n = 8, bestStepVegas = 3

所以看到当bestStepVegas =3时候效果最好,遍历的节点数最小,比单纯回溯法114次遍历节点数降低到27.6。

这里给出对于n皇后的组合算法求解n=8~20的对比,如下代码:

import random
random.seed(0)
def QueenBacktrace(n, k, col, diag45, diag135, ans, count):
    """
    从第k行开始使用回溯法求n皇后
    """
    i = k # 行
    j = 0 # 列
    while(i < n and i >= k):
        # 找到当前行i的一个安全列,除非找不到了
        if(j < n):
            if (j not in col) and ((j-i) not in diag45) and ((j+i) not in diag135):
                count += 1
                # 找到了安全列,放置皇后,入栈,进入下一行
                ans[i] = j # 入栈结果
                col.append(j)
                diag45.append(j-i)
                diag135.append(j+i)
                i += 1 # 下一行
                j = 0
            else:
                j += 1
        # 找不到安全的列号,弹栈回溯 
        else:
            i -= 1 
            j = ans[i] + 1
            col.pop()
            diag135.pop()
            diag45.pop()
    if(i == n):
        return True,ans,count
    else:
        return False,ans,count


    # for j in range(n): 
    #     # 找到起始第k行的安全的行号进入回溯法
    #     if (j not in col) and ((j-k+1) not in diag45) and ((j+k+1) not in diag135):
    #         for i in range(k+1, n): 


def QueensLV(n, stepVegas):
    """ LV 混合 回溯的Queen算法 """
    count = 1 # 计数通过的节点数
    col = [] # 已使用列集合
    diag45 = [] # 45度对角线冲突集合
    diag135 = [] # 135度对角线冲突集合
    ans = [0 for i in range(n)] # 皇后放在第index行的第ans[index]列
    k = 0 # 行号
    j = 0 # 选择的安全行号
    while(True):
        nb = 0 # 计数器
        if(k == stepVegas):
            break
        for i in range(n):
            # 循环随机找一个开放的位置
            if (i not in col) and ((i-k) not in diag45) and ((i+k) not in diag135):
                nb += 1
                if(random.randint(1,nb) == 1):
                    j = i
        if(nb > 0):
            # 此时第k+1的皇后将放置在j位置上
            count+=1
            ans[k] = j
            col.append(j)
            diag45.append(j-k)
            diag135.append(j+k)
            k += 1
        if(nb == 0):
            return (False,ans,count)
        
    # 回溯法继续执行
    return QueenBacktrace(n, k, col, diag45, diag135, ans, count)

repeat = 1000
for i in range(8,21):
    minT = 100000000000 # 找到最小的node次数
    bestStepVegas = 0
    for stepVegas in range(0,i+1):
        s = 0
        s_count = 0
        e = 0
        e_count = 0
        for j in range(repeat):
            flag,ans,count = QueensLV(i,stepVegas)
            if(flag):
                s += count
                s_count += 1
            else:
                e += count
                e_count += 1
        if(s_count == 0):
            s = 0
        else:
            s = 1.0*s/s_count
        if(e_count == 0):
            e = 0
        else:
            e = 1.0*e/e_count
        p = s_count/repeat
        t = s + e*e_count/s_count
        print("n = "+str(i)+", stepVegas = "+str(stepVegas)+ " : p = "+str(p) +" s = "+str(s)+ " e = "+str(e)+" t = "+str(t))
        if(minT > t):
            minT = t
            bestStepVegas = stepVegas
    print("n = "+str(i)+", bestStepVegas = "+str(bestStepVegas) + "\n")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/45112.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

刷爆力扣之卡牌分组

刷爆力扣之卡牌分组 HELLO&#xff0c;各位看官大大好&#xff0c;我是阿呆 &#x1f648;&#x1f648;&#x1f648; 今天阿呆继续记录下力扣刷题过程&#xff0c;收录在专栏算法中 &#x1f61c;&#x1f61c;&#x1f61c; 该专栏按照不同类别标签进行刷题&#xff0c;每个…

DDOS防护如何建设?

数字化转型发展也推动了云计算、人工智能、大数据、物联网等新一代信息技术应用普及&#xff0c;与此同时&#xff0c;新时代的发展也带来了新的网络威胁和新的安全需求。我们不难发现&#xff0c;近年网络攻击时间层出不穷&#xff0c;全球范围来看&#xff0c;企业因遭受网络…

quilt基本使用

一、简介 quilt是制作补丁和管理的工具。比如想用开源代码做一些项目&#xff0c;我们如果直接在开源代码里进行修改&#xff0c;等到下次开源代码升级后&#xff0c;我们再将我们修改的部分转移到新版本的开源代码中就需要进行大量的新旧版本对比工作。那如果我们将这些改动使…

3D视觉应用案例:引导板件定位抓取

3D引导板件定位抓取 某知名模具钢材集团 项目背景 广州某知名模具钢材集团&#xff0c;需求3D引导板件定位抓取和2D识别信息获取功能。原场景用的桁车设备加人工搬运安全系数极低。 作业流程 • 3D相机视觉识别产品位置后&#xff0c;通过机器人电磁铁完成产品的抓取。 •…

大数据 安装配置centOS

安装vmware workstation 启动vmware workstation 启动【编辑】菜单下的“虚拟网络编辑器” 更改VMnet8 查看DHCP设置 查看NAT设置 安装centos7 链接&#xff1a;https://howard2005.blog.csdn.net/article/details/126830182?spm1001.2014.3001.5502 启动虚拟机 虚拟机 查…

代码调优?从Fibonacci数列的7种写法说起,看完coding能力上一个台阶

开启掘金成长之旅&#xff01;这是我参与「掘金日新计划 12 月更文挑战」的第2天&#xff0c;点击查看活动详情 引子 楼梯有 N阶&#xff0c;上楼可以一步上一阶&#xff0c;也可以一步上二阶。 编一个程序&#xff0c;计算共有多少种不同的走法。 例&#xff1a; 0层&…

【毕业设计】28-基于单片机的音乐播放器简易音乐播放器设计(原理图+源代码+仿真工程+答辩PPT+答辩论文)

typora-root-url: ./ 【毕业设计】28-基于单片机的音乐播放器简易音乐播放器设计&#xff08;原理图源代码仿真工程答辩PPT答辩论文&#xff09; 文章目录typora-root-url: ./【毕业设计】28-基于单片机的音乐播放器简易音乐播放器设计&#xff08;原理图源代码仿真工程答辩PP…

麦芽糖-聚乙二醇-甲氨蝶呤 MTX-PEG-maltose

麦芽糖-聚乙二醇-甲氨蝶呤 MTX-PEG-maltose 中文名称&#xff1a;麦芽糖-甲氨蝶呤 英文名称&#xff1a;maltose-MTX 别称&#xff1a;甲氨蝶呤修饰麦芽糖&#xff0c;甲氨蝶呤-麦芽糖 PEG接枝修饰麦芽糖&#xff0c;麦芽糖-聚乙二醇-甲氨蝶呤&#xff0c;MTX-PEG-maltose&…

【visual studio】visual studio 2022 无法 复制黏贴

visual studio 2022 cannot copy paste 其他网友也有反馈到微软&#xff1a;VS 2022 Copy and Paste form feature Broken?Copy paste still not fixed in Visual studio 2022表现是突然就无法复制和黏贴了其他的app 就没有这个问题每次都是重启电脑解决。 2022年11月fix 今…

java中csv导出-追加-列转行

1、问题描述 业务数据量比较大&#xff0c;业务上查询条件写入数据库&#xff0c;java定时去读&#xff0c;然后导出csv&#xff0c;供用户下载&#xff0c;因为有模板要求&#xff0c;前一部分是统计信息&#xff0c;后一部分是明细信息&#xff1b;首先csv中写入统计信息&am…

gradle安装配置

Gradle和Maven都是当前热门的自动化构建工具。 这里以安装6.8版本为例 下载地址 https://services.gradle.org/distributions/ 环境配置 新建系统环境 GRADLE_HOME D:\software\gradle-6.8 新建系统环境 GRADLE_USER_HOME D:\gradle\repository 找到path变量,后面添加 %…

uniapp 短信监听(验证码)插件 Ba-Sms

简介&#xff08;下载地址&#xff09; Ba-Sms 是一款用于拦截实时短信的插件&#xff0c;可以进行短信过滤&#xff0c;得到自己想要的内容&#xff0c;可以用于需要自动填写短信验证码的项目 支持监听当前接收到的短信信息支持过滤接收到的短信&#xff0c;默认过滤4~8位的…

Python学习笔记之进程池pool

平时很多操作都会用到多进程&#xff0c;比如爬虫、数据处理。 下面介绍一下多进程的函数方法、参数及使用方法。 目录 一.进程池Pool介绍 1.apply() 2.apply_async 3.map() 4.map_async() 5.close() 6.terminal() 7.join() 二&#xff0e;进程池Pool使用 1.map用法…

【虚幻引擎UE】UE5 材质动态修改的2种方法(含工程源码)

演示效果&#xff1a; 示例工程源码 一、直接材质参数变量 1、贴图变量&#xff1a; 在材质蓝图中右键&#xff0c;创建变量TextureSampeParameter2D&#xff08;贴图变量&#xff09;。 输入RGB到基础颜色 2、单色变量&#xff1a; 在材质蓝图中右键&#xff0c;创建变量…

[附源码]计算机毕业设计springboot高校商铺管理系统论文

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

要想后期修改少,代码重构要趁早

摘要&#xff1a;在敏捷中&#xff0c;让设计简单化&#xff0c;必须让设计从简单开始&#xff0c;然后变得成熟。要做到这一点&#xff0c;重构是唯一的出路。本文分享自华为云社区《敏捷技术实践之重构》&#xff0c;作者&#xff1a;华为云PaaS服务小智 。 前言 极限编程&…

Flink Forward Asia 2022 主论坛概览

2022 年 11 月 26-27 日&#xff0c;Flink Forward Asia&#xff08;FFA&#xff09;峰会成功举行。Flink Forward Asia 是由 Apache 软件基金会官方授权、由阿里云承办的技术峰会&#xff0c;是目前国内最大的 Apache 顶级项目会议之一&#xff0c;也是 Flink 开发者和使用者的…

使用nohup命令 或者 代码创建守护进程

目录 一、什么是守护进程&#xff1f; 1、守护进程的概念 2、为什么需要守护进程 二、理解进程组、会话、终端 三、创建守护进程的两种方式 1、nohup命令创建守护进程 2、代码创建守护进程 (1) 创建子进程&#xff0c;父进程退出 (2) 子进程创建新的会话 (3) 更改守护…

jsp美食共享平台系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 美食共享平台系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统采用mvc开发结构 serlvetdaobean模式&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用 B/S模式开发。开发环境为TOMCAT7.0,Myecl…

二叉树链式结构-c语言实现

文章目录二叉树链式结构实现1. 链式二叉树结构2. 二叉树的遍历2.1 前序遍历2.2 中序遍历2.3 后序遍历2.4 层序遍历3. 常见功能3.1 二叉树结点个数3.2 二叉树叶子结点个数3.3 第K层结点的个数3.4 二叉树的深度3.5 判断是不是树是不是完全二叉树3.6 在二叉树中查找值为x的结点3.7…