(算法设计与分析)第七章随机化算法概述

news2024/11/20 20:24:11

文章目录

  • 一:概述
    • (1)什么是随机化算法
    • (2)随机化算法的特点
    • (3)随机化算法分类
    • (4)随机数
  • 二:数值随机化算法(以计算 π π π值为例)
  • 三:舍伍德算法(以快速排序为例)
  • 四:拉斯维加斯算法(以N皇后问题为例)
    • (1)拉斯维加斯算法概述
    • (2)N皇后问题
      • A:问题概述
      • B:回溯算法解决N皇后问题
      • C:拉斯维加斯算法解决N皇后问题
  • 五:蒙特卡罗算法(以主元素问题为例)

一:概述

(1)什么是随机化算法

随机化算法:随机化算法把“对于所有合理的输入都必须给出正确的输出”这一求解问题的条件放宽,将随机性选择注入到算法中,在算法执行某些步骤时,可以随机地选择下一步该如何进行,同时允许结果是近似解或以较小概率出现错误,并以此为代价,获得算法运行时间的大幅度减少。随机化算法执行中注入的随机性依赖于随机数发生器所产生的随机数

(2)随机化算法的特点

随机化特点

  • 允许算法执行过程中随机选择下一计算步骤。
  • 当算法执行过程中面临选择时,随机化算法通常比最优选择算法省时。可在很大程度上降低算法复杂性
  • 对所求问题的同一实 用同一随机化算法求解两次,两次求解所需的时间甚至所得的结果可能有相当大的差别
  • 设计思想简单,易于实现

(3)随机化算法分类

随机化算法分类

  • 数值随机算法
  • 舍伍德算法
  • 拉斯维加斯算法
  • 蒙特卡罗算法

(4)随机数

随机数:随机数在随机化算法设计中扮演着十分重要的角色,但计算机无法产生真正的随机数,在随机化算法中使用的随机数都是伪随机数,其中线性同余法是产生伪随机数的最常用的方法

二:数值随机化算法(以计算 π π π值为例)

数值随机化算法:将一个问题的计算与某已经确定的概率分布的事件联系起来,算法求解结果一般为近似解,注意

  • 解精度随计算时间的增加而提高
  • 某些情况下计算问题的精确解是不可能或没必要

以计算 π π π值问题为例:设有一半径为 r r r的圆及其外切四边形。向该正方形随机地投掷 n n n个点。设落入圆内的点数为 k k k。若所投入的点在正方形上满足均匀分布,则所投入的点落入圆内的概率可由下式计算:

π r 2 4 r 2 = π 4 ≈ k n \frac{πr^{2}}{4r^{2}}=\frac{π}{4}\approx \frac{k}{n} 4r2πr2=4πnk

n n n足够大时, k k k n n n之比近似逼近这一概率,从而有:

π ≈ 4 k n π\approx\frac{4k}{n} πn4k

代码如下

#include <iostream>
#include <cstdlib>
using namespace std;

// 获得0-1之间的随机数
double get_random_num ()
{
    return (double)rand() / RAND_MAX ;
}
// 用随机投点法计算 PI
double darts (int n)
{
    int k = 0 ;
    for (int i = 0; i < n; ++ i) {
        // 投掷n个点,随机生成点(x, y)
        double x = get_random_num() ;
        double y = get_random_num() ;
        //如果该点落在圆内,则k++
        if ((x * x + y * y) <= 1.0) {
            ++ k ;
        }
    }
    //计算公式
    return (4 * k) / (double)n ;
}
int main()
{
    cout << darts (200000000) << endl ;
}

在这里插入图片描述

三:舍伍德算法(以快速排序为例)

舍伍德算法:当一个确定性算法在最坏情况下的计算复杂度与其在平均情况下的计算复杂度相差较大时,可以在该确定性算法中引入随机性将它改造为一个舍伍德算法,用来消除或减少问题不同实例之间在计算时间上的差别,舍伍德算法总能求得问题的正确解,具体方法为数据洗牌+确定算法

以快速排序为例:快速排序是基于分治策略的一个典型代表算法,其基本思想是对于输入的子数组a[p:r],按以下三个步骤进行排序

  • 分解:以a[p]为基准元素将a[p:r]划分成3段a[p:q-1]a[q]a[q+1:r],使a[p:q-1]中任一元素小于等于a[q],而a[q+1:r]中任一元素大于等于a[q]。下标q在划分过程中确定
  • 递归求解:通过递归调用快速排序算法,分别对a[p:q-1]a[q+1:r]进行排序
  • 合并:由于对a[p:q-1]a[q+1:r]的排序是就地进行的,因此在a[p:q-1]a[q+1:r]都已排好序后,不需要执行任何计算,a[p:r]就已经排好序了

代码如下

#include <iostream>
#include <vector>
#include <algorithm>
using  namespace std;



int Partition(vector<int>& data, int begin, int end){
    int key = data[begin];//以左侧元素为基准元素
    int save = begin;
    while(begin < end){
        while(begin < end && data[end] >= key){
            //end处元素应该大于等于key,一旦找到小于key的就停止
            --end;
        }
        while(begin < end && data[begin] <= key){
            //end处元素应该小于等于key,一旦找到大于key的就停止
            ++begin;
        }
        swap(data[begin], data[end]);//交换
    }
    swap(data[save], data[end]);//让基准元素到"中间";
    return begin;
}

void quickSort(vector<int>& data, int left, int right){
    if(left < right){
        int div = Partition(data, left, right);
        quickSort(data, left, div-1);
        quickSort(data, div+1, right);
    }else{
        return;
    }
}

void print(vector<int>& data){
    for(int i = 0; i < data.size(); i++){
        cout << data[i] << "  ";
    }
    cout << endl;
}
int main(){
    vector<int> test = {21, 43, 32, 18, 29, 30, 21, 55, 43, 17, 83};
    print(test);
    quickSort(test, 0, test.size());
    print(test);

    return 0;
}

在这里插入图片描述

快速排序算法的核心在于合适的划分基准,这个划分基准将直接导致划分的对称性,继而影响算法的性能。一旦基准挑选的不合理,如下图,那么将导致算法时间复杂度非常高

在这里插入图片描述

因此,舍伍德算法通过采用随机选择策略来随机选取出一个元素作为划分基准,这样可以使划分基准是随机的,从而可以期望划分是较为对称的

代码如下

#include <iostream>
#include <vector>
#include <algorithm>
using  namespace std;


int Partition(vector<int>& data, int begin, int end){
    // 随机挑选
    int rand_idx = (rand() % (end - begin +1)) + begin;
    swap(data[begin], data[rand_idx]);

    int key = data[begin];//以左侧元素为基准元素
    int save = begin;
    while(begin < end){
        while(begin < end && data[end] >= key){
            //end处元素应该大于等于key,一旦找到小于key的就停止
            --end;
        }
        while(begin < end && data[begin] <= key){
            //end处元素应该小于等于key,一旦找到大于key的就停止
            ++begin;
        }
        swap(data[begin], data[end]);//交换
    }
    swap(data[save], data[end]);//让基准元素到"中间";
    return begin;
}

void quickSort(vector<int>& data, int left, int right){
    if(left < right){
        int div = Partition(data, left, right);
        quickSort(data, left, div-1);
        quickSort(data, div+1, right);
    }else{
        return;
    }
}

void print(vector<int>& data){
    for(int i = 0; i < data.size(); i++){
        cout << data[i] << "  ";
    }
    cout << endl;
}
int main(){
    vector<int> test = {21, 43, 32, 18, 29, 30, 21, 55, 43, 17, 83};
    print(test);
    quickSort(test, 0, test.size());
    print(test);

    return 0;
}

在这里插入图片描述

四:拉斯维加斯算法(以N皇后问题为例)

(1)拉斯维加斯算法概述

拉斯维加斯算法:舍伍德型算法的优点是,其计算时间复杂性对所有实例而言相对均匀,但与其相应的确定性算法相比,其平均时间复杂性没有改进。拉斯维加斯算法则不然,它能显著地改进算法的有效性,甚至对某些迄今为止找不到有效算法的问题,也能得到满意的结果。具体来说

  • 拉斯维加斯算法求得的解总是正确的,求解中算法所作的随机性决策有可能导致算法找不到所需的解

  • 一般情况下,求得正确解的概率随计算时间增加而增大

  • 对同一实例,重复执行一个拉斯维加斯算法,可使求解失败概率任意小

  • 由于拉斯维加斯型算法有时运行成功,有时运行失败,故通常拉斯维加斯型算法的返回类型为bool

  • 拉斯维加斯算法运行一次,或者得到一个正确的解,或者无解。因此,需要对同一输入实例反复多次运行算法,直到成功地获得问题的解

算法有两个参数

  • 算法输入
  • 当算法运行成功时保存问题的解

拉斯维加斯算法典型调用形式如下

void obstinate(Object x, Object y){
	//反复调用拉斯维加斯算法直到找到问题的一个解y
	bool success = false;
	while(!success) success = lv(x, y)
}

(2)N皇后问题

A:问题概述

N皇后问题:在 n × n n×n n×n格的棋盘上放置彼此不相互攻击的 N N N个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。N皇后问题等价于在 n × n n×n n×n格的棋盘上放置 N N N个皇后,任何2个皇后不能放在同一行或同一列或同一斜线上

在这里插入图片描述

B:回溯算法解决N皇后问题

  • 1

C:拉斯维加斯算法解决N皇后问题

在棋盘上相继各行中随机地放置皇后,使新放置皇后与已放置皇后互不攻击,直至 n n n个皇后均已相容地放置好,或没有下一个皇后的可放置位置时为止

  • 注意:完全使用拉斯维加斯算法可能复杂度太高,长时间得不到解,所以下面的示例中前 t t t个皇后的位置由拉斯维加斯算法产生,后 N − t N-t Nt个皇后位置由回溯法产生
#include <iostream>
#include "Random.h"
#include <cmath>
#define MAX_V 100
using namespace std;

/* 判断某行放置皇后是否合法
 * i : 即将放置的行数
 * j : 即将放置的列数
 * */
bool place(int map[MAX_V][MAX_V], int n, int i, int j){
	for(int a = i - 1, b = 1; a >= 0; a--, b++){
		//判断直线方向
		if(map[a][j] == 1){
			return false;
		}
		int l = j - b;
		int r = j + b;
		//判断两个斜线方向
		if(l >= 0 && map[a][l] == 1){
			return false;
		}
		if(r < n && map[a][r] == 1){
			return false;
		}
	}

	return true;
}

/* 回溯剩下的皇后位置
 * t : 回溯开始的位置
 * */
bool recall(int map[MAX_V][MAX_V], int n, int t){
	if(t == n){
		return true;
	}

	for(int i = 0; i < n; i++){
		if(!place(map, n, t, i)){
			continue;
		}

		map[t][i] = 1;
		if(recall(map, n, t+1)){
			return true;
		}
		map[t][i] = 0;
	}

	return false;
}

/* n 皇后问题
 * map : 棋盘
 * n   : 棋盘大小
 * t   : 使用拉斯维加斯算法的层次数
 * */
void nQueen(int map[MAX_V][MAX_V], int n, int t){
	Random rand;
	int i;
	while(i != n){                       //循环调用,直到产生解
		for(i = 0; i < t; i++){
			int randPos = rand.rand(n);  //产生[0, n)的随机数
			if(!place(map, n, i, randPos)){
				break;
			}
			for(int j = 0; j < n; j++){
				map[i][j] = 0;
			}
			map[i][randPos] = 1;
		}

		if(i == t && recall(map, n, t)){
			i = n;
		}

	}
}


int main(){

	int map[MAX_V][MAX_V] = {0};

	nQueen(map, 10, 7);

	for(int i = 0; i < 10; i++){
		for(int j = 0; j < 10; j++){
			cout << map[i][j] << " ";
		}
		cout << endl;
	}

	return 0;
}


五:蒙特卡罗算法(以主元素问题为例)

拉斯维加斯算法:蒙特卡罗方法又称随机抽样或统计试验方法。传统的经验方法由于不能逼近真实的物理过程,很难得到满意的结果,而蒙特卡罗方法由于能够真实地模拟实际物理过程,故解决问题与实际非常符合,可以得到很圆满的结果。 在实际应用中常会遇到一些问题,不论采用确定性算法或随机化算法都无法保证每次都能得到正确的解答。蒙特卡罗算法则在一般情况下可以保证对问题的所有实例都以高概率给出正确解,但是通常无法判定一个具体解是否正确。蒙特卡罗算法一般包含如下三个步骤

  • 构造随机的概率过程:对于本身就具有随机性质的问题,要正确描述和模拟这个概率过程。对于本来不是随机性质的确定性问题,比如计算定积分,就必须事先构造一个人为的概率过程了(例如最开始的圆周率 π \pi π的求解)
  • 从已知概率分布抽样:由于各种概率模型都可以看作是由各种各样的概率分布构成的,因此产生已知概率分布的随机变量,就成为实现蒙特卡罗方法模拟实验的基本手段
  • 求解估计量:实现模拟实验后,要确定一个随机变量,作为所要求问题的解,即无偏估计。建立估计量,相当于对实验结果进行考察,从而得到问题的解

以主元素问题为例:设 T [ n ] T[n] T[n]是含有 n n n个元素的数组, x x x是该数组的一个元素,如果数组中有一半以上的元素与 x x x相同,则称元素 x x x为数组 T T T的主元素

  • 例如:数组 T = { 3 , 2 , 3 , 2 , 3 , 3 , 5 } T=\{3, 2, 3, 2, 3, 3, 5\} T={3,2,3,2,3,3,5}中元素3的就是主元素

蒙特卡罗解决思路:随机选择数组中的一个元素 T [ i ] T[i] T[i]进行统计,如果该元素出现的次数大于 n 2 \frac{n}{2} 2n,则该元素就是数组的主元素,算法返回true,否则该元素不是主元素,返回false。如果数组中存在主元素,则非主元素个数小于 n 2 \frac{n}{2} 2n,因此该算法将会以大于 1 2 \frac{1}{2} 21的概率返回true,以小于 1 2 \frac{1}{2} 21的概率返回false,这说明算法出现找不到正确解的概率小于 1 2 \frac{1}{2} 21。如果连续调用算法 k k k次,算法返回false的概率将减少为 2 − k 2^{-k} 2k

代码如下

//随机化算法 蒙特卡罗算法 主元素问题
#include "stdafx.h"
#include "RandomNumber.h"
#include <cmath>
#include <iostream>
using namespace std;

//判定主元素的蒙特卡罗算法
template<class Type>
bool Majority(Type *T,int n)
{
    RandomNumber rnd;
    int i = rnd.Random(n);

    Type x = T[i];	//随机选择数组元素
    int k = 0;

    for(int j=0; j<n; j++)
    {
        if(T[j] == x)
        {
            k++;
        }
    }

    return (k>n/2);	//k>n/2时,T含有主元素
}

//重复k次调用算法Majority
template<class Type>
bool MajorityMC(Type *T,int n,double e)
{
    int k = ceil(log(1/e)/log((float)2));
    for(int i=1; i<=k; i++)
    {
        if(Majority(T,n))
        {
            return true;
        }
    }
    return false;
}

int main()
{
    int n = 10;
    float e = 0.001;
    int a[] = {5,5,5,5,5,5,1,3,4,6};
    cout<<"数组a的元素如下:"<<endl;
    for(int i=0; i<10; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    cout<<"调用MajorityMC判断数组是否含有主元素结果是:"<<MajorityMC(a,n,e)<<endl;
}

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

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

相关文章

分布式计算 MapReduce 究竟是怎么一回事?

前言 如果要对文件中的内容进行统计&#xff0c;大家觉得怎么做呢&#xff1f;一般的思路都是将不同地方的文件数据读取到内存中&#xff0c;最后集中进行统计。如果数据量少还好&#xff0c;但是面对海量数据、大数据的场景这样真的合适吗&#xff1f;不合适的话&#xff0c;…

操作系统装完之后,安装几个特别有用的经典软件,都是电脑必备,包含pdf编辑、图片编辑、wiki、压缩、影音等等

操作系统装完之后&#xff0c;安装几个特别有用的经典软件&#xff0c;都是电脑必备&#xff0c;包含pdf编辑、图片编辑、wiki、压缩、影音等等。 Gimp https://www.gimp.org/ Gimp 是一款小巧实用的图片编辑工具。 如果你不想用笨重的PS&#xff0c;那可以尝试一下Gimp&…

元胞自动机模拟病毒传染(SEIR模型)(Python代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

SSM整合-如何配置相关文件

下述操作都是在IDEA上进行 1.首先新建一个Maven工程。 2.在pom.xml中增加相关依赖 <properties><spring.version>5.3.1</spring.version></properties><dependencies><dependency><groupId>org.springframework</groupId>&l…

安卓玩机搞机技巧综合资源----手机各种代码 查询信息 开启端口 调试选项【十】

接上篇 安卓玩机搞机技巧综合资源------如何提取手机分区 小米机型代码分享等等 【一】 安卓玩机搞机技巧综合资源------开机英文提示解决dm-verity corruption your device is corrupt. 设备内部报错 AB分区等等【二】 安卓玩机搞机技巧综合资源------EROFS分区格式 小米红…

踩坑Xxljob本地部署后调度一半成功一半失败原因分析及解决方案记录

缘由 入门学习和本地部署Xxljob过程中&#xff0c;发现Xxljob任务一半调度成功&#xff0c;一半调度失败&#xff0c;给我邮箱发爆了&#xff0c;为啥呢&#xff1f;查了半天资料都没解决 成功比例图&#xff1a; 实际操作时&#xff0c;发现单次手动执行一定成功&#xff0…

Python之数据库编程

目录 一、MySQL数据库的使用 数据库相关操作 二、数据库增删改查 增加 修改 删除 三、数据库标准写法 一、MySQL数据库的使用 建表 CREATE TABLE py_student( id INTEGER primary key auto_increment, name INTEGER not null, gender varchar(11) default 男 , birthday d…

CMake中define_property的使用

CMake中的define_property命令用于定义和记录自定义属性&#xff0c;其格式如下&#xff1a; define_property(<GLOBAL | DIRECTORY | TARGET | SOURCE |TEST | VARIABLE | CACHED_VARIABLE>PROPERTY <name> [INHERITED][BRIEF_DOCS <brief-doc> [docs...]]…

php+vue基于微信小程序的在线挂号预约小程序

网络的广泛应用给生活带来了十分的便利。所以把在线挂号管理与现在网络相结合&#xff0c;利用ThinkPHP5技术建设在线挂号微信小程序&#xff0c;实现在线挂号的信息化。则对于进一步提高在线挂号管理发展&#xff0c;丰富在线挂号管理经验能起到不少的促进作用。 在线挂号微信…

在飞书搞了个机器人,我让ChatGPT帮忙写算法

一、前言 环境&#xff1a; 系统&#xff1a;Windows 11 64位 Python版本&#xff1a;Python 3.9 注&#xff1a;本文不讲怎么实现&#xff0c;只讲实现的效果和一些思考。大家感兴趣再考虑去配置相关机器人。 先来问问ChatGPT两个问题&#xff1a; 1、ChatGPT是什么&#xff…

Java面试题总结-面向对象

面试题总结第一篇面向对象面向对象和面向过程的区别面向对象三大特性Java是如何实现多态的面向对象 面向对象和面向过程的区别 面向过程&#xff1a; 优点&#xff1a;性能比面向对象高&#xff0c;因为类调用时需要实例化&#xff0c;开销比较大&#xff0c;比较消耗资源;比如…

非零基础自学计算机操作系统 第1章 操作系统概述 1.2 操作系统的历史 1.2.1 操作系统的产生

非零基础自学计算机操作系统 文章目录非零基础自学计算机操作系统第1章 操作系统概述1.2 操作系统的历史1.2.1 操作系统的产生第1章 操作系统概述 1.2 操作系统的历史 由于操作系统是直接建造于硬件层之上的&#xff0c;它的演变必然与计算机系统结构的演变有着密切的联系。 …

Java基于JSP的小区内部物业管理系统

对于规模较小的物业管理公司来说,传统的人工小区物业管理模式仍旧可以应付日常的工作需求。但是,随着该行业不断的发展,竞争日益激烈,提高物业管理的工作水平与效率、为业主提供更好的服务,已经成为物业管理公司当前必须考虑的问题。而将计算机引入到管理机制中来将是一个不错的…

2022-12- 05 网工进阶(三十七)MPLS--基本概念、转发过程、基本配置、配置静态LSR

MPLS概述 基本概念 MPLS&#xff08;Multi-Protocol Label Switching&#xff0c;多协议标签交换&#xff09;位于TCP/IP协议栈中的数据链路层和网络层之间&#xff0c;可以向所有网络层提供服务。 通过在数据链路层和网络层之间增加额外的MPLS头部&#xff0c;基于MPLS头部…

木字楠后台管理系统开发(3):Vue项目初始化并引入基础依赖

&#x1f3b6; 文章简介&#xff1a;木字楠后台管理系统开发(3)&#xff1a;Vue项目初始化并引入基础依赖 &#x1f4a1; 创作目的&#xff1a;为了带大家完整的体验木字楠后台管理系统模版的开发流程 ☀️ 今日天气&#xff1a;温度骤降&#xff0c;差点给爷送走。 &#x1f4…

Js逆向教程23-AST Babel插件最简单修改值示例

作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; AST Babel插件最简单修改值示例 一、文档地址和安装的方式 https://github.com/jamiebuilds/babel-handbook/blob/master/translation…

动态规划

什么是动态规划 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中&#xff0c;可能会有许多可行解。每一个解都对应于一个值&#xff0c;我们希望找到具有最优值的解。 动态规划的前提是什么&#xff1f; 存在最优解 拿出来任意一块物品&#xff0c;仍旧是最…

详解C++11

文章目录前言一、C11简介二、统一的列表初始化2.1 {}的初始化2.2 std::initializer_list三、声明3.1 auto3.2 decltype3.3 nullptr四、范围for五、智能指针六、STL中一些变化新容器容器中的一些新方法七、右值引用和移动语义7.1 左值引用和右值引用7.2 左值引用和右值引用比较左…

灵魂拷问,你真的了解DNS吗?

未来已来&#xff0c;只是不均衡地分布在当下 大家好&#xff0c;我是菜农&#xff0c;欢迎来到我的频道。 都说程序员是面向Google编程&#xff0c;殊不知当你输入 www.google.com 地址的时候&#xff0c;是否有想过&#xff0c;在回车的一瞬间浏览器如何将请求发送&#xff…

价值年薪70W的JAVA进阶学习路线!终于让我从阿里P8手里抠出来了

作为一个男人我感觉必须得做点什么来证明一下自己&#xff0c;现在我又回来了&#xff0c;准备把自己的节操准备补一下。另外给各位未来的Java程序员说一句&#xff0c;别的我不清楚&#xff0c;学习编程请从一而终 咱们学习编程就挺难的&#xff0c;有这些先驱者来带领咱们学习…