算法设计与分析:动态规划法求扔鸡蛋问题 C++

目录

一、实验目的

二、问题描述

三、实验要求

四、算法思想和实验结果

1、动态规划法原理: 

2、解决方法:

2.1 方法一:常规动态规划

2.1.1 算法思想:

2.1.2 时间复杂度分析

2.1.3 时间效率分析

2.2 方法二:动态规划加二分查找最优x

2.2.1 算法思想:

2.2.2 时间复杂度分析

2.2.3 时间效率分析

2.3 方法三:动态规划加逆向求解

2.3.1 算法思想

2.3.2 时间复杂度分析

2.3.3 时间效率分析

3、结果输出示例


实验目的

1. 掌握动态规划算法设计思想。

2. 掌握扔鸡蛋问题的动态规划法。

问题描述

扔鸡蛋问题是计算机程序设计中的一个经典问题。从一幢楼房的不同楼层往下扔鸡蛋,用最少的最坏情况试验次数,确定鸡蛋不会摔碎的最高安全楼层。仅有一个鸡蛋供试验时,只能采用顺序查找法。有足够多的鸡蛋时,可以采用二分查找法。有多于一个但数量有限的鸡蛋时,采用动态规划方法求解。双蛋问题(two-egg problem)是本问题的一个特例,曾出现于谷歌的程序员面试题中。

有一幢楼房高层。某人准备了N个鸡蛋供试验。他想知道鸡蛋从几层扔下不会摔碎,并确定出最高安全楼层。试验过程中,鸡蛋没有摔碎则可以继续使用,摔碎了则需要换一个鸡蛋继续试验。为保证试验成功,此人要设计一个程序,以最小化最坏情况的试验次数F(M, N)。作为一个数学抽象,本问题采用一些理想化假设:所有鸡蛋抗摔能力相同,不计重复坠地的累积损伤,且忽略试验结果的偶然性。试验成功的标准是在N个鸡蛋用完之前,精确确定最高安全楼层是哪一层。允许有鸡蛋剩余。

如果只有N=1个鸡蛋供试验,则为了保证试验成功,只能从一层开始逐层往上试验。这相当于采用顺序查找算法,最坏试验次数F(M, 1)=M。如果一层就碎了,则最高安全楼层为0。如果M层还不碎,则最高安全楼层为M。

实验要求

1. 给出解决问题的动态规划方程;

2. 理论分析该算法的时间复杂度;

3. 分别测试M=10000, 20000, …, 100000, N=20时以及M=50000, N=11, 12, …, 20时的算法运行时间,并分析实验结果;

4. 依次在终端输出M=10000, N=1~20时的F(M, N)值,实验课时检查该代码,限用C或C++语言编写。

、算法思想和实验结果

1、动态规划法原理: 

        将复杂问题划分为更小的子问题,通过子问题的最优解来重构原问题的最优解。求解过程中,保存子问题的解,以便在需要时直接查表而不是重复计算,从而减少计算量。

2、解决方法:

2.1 方法一:常规动态规划
2.1.1 算法思想:

        用二维数组k[m][n]表示从第m层扔n个鸡蛋的动态规划法最优解,即该实验所要求的最少的最坏情况试验次数。

        对于m层、n个鸡蛋的求解,当尝试从第x层扔下一个鸡蛋时,有两种情况:

        1)鸡蛋破碎,剩余n-1个鸡蛋,则在第1层至第x-1层(共x-1层)继续尝试,即k[x-1][n-1]。

        2)鸡蛋未破碎,仍剩余n个鸡蛋,则在第x+1至m层(共m-x层)继续尝试,即k[m-x][n];

        因为题目要求是最坏情况,所以应该取上述两种情况的较大者。又由于要最少实验次数,所以x应该取让前面的较大者尽量小些的值。

        得动态规划方程如下:

 k[m][n]=1+min{max{k[x-1][n-1],k[m-x][n]}}(x=1,2,3……,m)

        实验时

        1)对于m=0、m=1或n=1的情况取值依次为0、1、m(在创建**k时就赋值完成)。

        2)对于其他值,用三层for循环自底向上依次确定k[i][j]的值。伪代码如下:

F1(m,n)for i=2 to mfor j=2 to nfor x=1 to ik[i][j]=1+max{k[x-1][j-1],k[i-x][j]}return k[m][n]
2.1.2 时间复杂度分析

        由前面伪代码可知,最外层m-1次,中间层n-1次,最里层i(i=2,3,……,m)次,则时间复杂度为O(n*m^2)。

2.1.3 时间效率分析

        由于效率较低,这里降低数据规模,分别测试M=1000, 2000, …, 10000, N=20时以及M=10000,N=11, 12, …, 20时的算法运行时间(对每种情况都运行5次取平均值)。

        1)N=20时,以M=1000为基准

M

1000

2000

3000

4000

5000

6000

7000

8000

9000

10000

平均运行时间/ms

47

190

434

814

1310

1964

2750

3706.2

4722.4

5802

理论时间/ms

47

188

423

752

1175

1692

2303

3008

3807

4700

        得下图:N=20时的实际效率曲线和理论效率曲线图。

        两条曲线一开始较贴合,但随着M的增大,实际运行时间与理论运行时间的差(非负)呈现递增趋势。

        2)M等于10000时,以N=11为基准:

N

11

12

13

14

15

16

17

18

19

20

平均运行时间/ms

2890.5

3152.25

3443

3832

4092.5

4408

4704

5180

5478

5825

理论时间/ms

2890.5

3153.27

3416.05

3678.82

3941.59

4204.36

4467.17

4729.91

4992.68

5255.45

        得下图:M=10000时的实际效率曲线和理论效率曲线图。

        与上一个图一样,两条曲线一开始较贴合,但随着N的增大,实际运行时间与理论运行时间的差(非负)呈现递增趋势。

        可能原因是

        k[i][j]=1+max{k[x-1][j-1],k[i-x][j]}中的数据访问的耗时实际是随着规模的增大而增大的,但分析时间复杂度和理论时间时忽略这个。(实际效率与理论效率之间差某些常数因子)

2.2 方法二:动态规划加二分查找最优x
2.2.1 算法思想:

        对于

            for x=1 to mk[i][j]=1+max{k[x-1][j-1],k[i-x][j]}

        当x增大时,k[x-1][j-1]是x相关的非递减函数,而k[i-x][j]是x相关的非递增函数。所以可以在前面的基础上,用二分法求出最优的x,而不是从1到m逐个的尝试、比较。

        由于层数为整数,二分到最后有两种情况:

        1)二分到最后low!=high,如下图,则应该取点1和点3中次数较小的那个(最坏情况下的最少测试次数),即

 k[i][j]=min{k[i-low][j],k[high-1][j-1]}

        2)分到最后刚好low=high,如下图:此时x=low=high,k[x-1][j-1=,k[i-x][j]。

        伪代码如下:

F2(m,n)for i=2 to mfor j=2 to nl=1;h=i;while l+1<hx=(l+h)/2if  k[x-1][j-1]<k[i-x][j]l=xelse if  k[x-1][j-1]>k[i-x][j]h=xelse  low=high=xk=1+min{k[i-l][j],k[h-1][j-1]}return k[m][n]
2.2.2 时间复杂度分析

        原来求最优x需m次循环,现在为log m,所以时间复杂度变为O(n*m*log m)

2.2.3 时间效率分析

        分别测试M=10000, 20000, …, 100000, N=20时以及M=50000, N=11, 12, …, 20时的算法运行时间(对每种情况都运行20次取平均值)。

        1)N=20时,以M=10000为基准

M

10000

20000

30000

40000

50000

60000

70000

80000

90000

100000

平均运行时间/ms

5

9.5

14.5

19.05

29.95

34.45

41.5

47.45

57.5

62.93

理论时间/ms

5

10.75

16.79

23.01

29.37

35.84

42.39

49.03

55.74

62.5

        得下图:N=20时的实际效率曲线和理论效率曲线图。

        两条曲线贴合度较高,说明算法效率基本符合关于m的mlog m级关系。

        2)M等于50000时,以N=11为基准

N

11

12

13

14

15

16

17

18

19

20

平均运行时间/ms

17

18.5

19.45

20

22.5

24

26.25

27.25

28.25

29

理论时间/ms

17

18.55

20.09

21.64

23.18

24.73

26.27

27.83

29.36

30.91

        得下图:M=50000时的实际效率曲线和理论效率曲线图。

        两条曲线较贴合,说明算法效率基本符合关于N的线性关系。

2.3 方法三:动态规划加逆向求解
2.3.1 算法思想

        不直接按照在m层、n个鸡蛋的情况下求解最少的最坏情况试验次数,而是逆向求解在n个鸡蛋、r次试验次数且最坏情况下最多能测多少层。找到层数大于m时的最小r。

        此时扔鸡蛋,碎了,就继续测楼下;没碎,就继续测楼上。

        总的可测楼层数 = 楼上的可测楼层数 + 楼下的可测楼层数 + 1(当前这层楼)。即

             k[n][r]=k[n][r-1]+k[n-1][r-1]+1

        此时的**k和前面的两种方法的不同,分配的空间不再是k[m+1][n+1],而是为k[n][max],max为一个较大的值(由于实验要求的最大规模M=100000、N=20的解为447,所以这里max取1000,实际未知该解时应取更大些)。

        伪代码如下:

F3(m,n)r=0while k[n][r]<m //r不超过mr++for i=1 to nk[i][r]=k[i][r-1]+k[i-1][r-1]+1return r
2.3.2 时间复杂度分析

        外层while循环(不大于)m次,内层for循环n次,所以时间复杂度为O(n*m)。

2.3.3 时间效率分析

        效率较高,m=2000000000,n=20时运行时间仍然小于1ms。

3、结果输出示例

​​​​​​​           

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

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

相关文章

Java面试题:解释Java的类加载过程,包括加载、链接和初始化阶段

Java的类加载过程是将类从其二进制表示&#xff08;通常是一个 .class 文件&#xff09;加载到JVM中并准备使用的过程。这个过程分为三个主要阶段&#xff1a;加载&#xff08;Loading&#xff09;、链接&#xff08;Linking&#xff09;和初始化&#xff08;Initialization&am…

python数据分析-糖尿病数据集数据分析预测

一、研究背景和意义 糖尿病是美国最普遍的慢性病之一&#xff0c;每年影响数百万美国人&#xff0c;并对经济造成重大的经济负担。糖尿病是一种严重的慢性疾病&#xff0c;其中个体失去有效调节血液中葡萄糖水平的能力&#xff0c;并可能导致生活质量和预期寿命下降。。。。 …

若依框架集成微信支付

1. 添加微信支付相关依赖 <!-- 微信支付 --> <dependency><groupId>com.github.wxpay</groupId><artifactId>wxpay-sdk</artifactId><version>0.0.3</version> </dependency> <dependency><groupId>com.gi…

微信小程序开发---自定义底部tabBar

自定义tabBar注意事项&#xff1a; 在自定义 tabBar 模式下 &#xff0c;为了保证低版本兼容以及区分哪些页面是 tab 页&#xff0c;app.json文件中 tabBar 的相关配置项需完整声明&#xff0c;但这些字段不会作用于自定义 tabBar 的渲染。所有 tabBar 的样式都由该自定义组件…

x86 汇编中的 “lock“ 指令详解

在深入理解 “lock” 指令之前&#xff0c;我们先来看一下 Qt 源代码中的一段 x86 汇编代码&#xff1a; q_atomic_increment:movl 4(%esp), %ecxlock incl (%ecx)mov $0,%eaxsetne %alret.align 4,0x90.type q_atomic_increment,function.size q_atomic_increment,.-q_atom…

网工常见面试题

1-10题 1.介绍TCP/IP四层、五层模型作用及每层包含的协议 TCP/IP四层模型 应用层&#xff1a; 作用&#xff1a;直接与应用程序交互&#xff0c;定义了应用程序如何通过网络发送数据。包含协议&#xff1a;HTTP&#xff08;网页浏览&#xff09;、FTP&#xff08;文件传输&…

Linux学习笔记:前言与操作系统的初识【1】

前言 为什么学习Linux 作为当下最流行的操作系统之一&#xff0c;学会如何使用和操作Linux操作系统也就是每位计算机学者的看家必备技能了。其次呢&#xff0c;本人受Linux的创始人林纳斯的影响太深了&#xff0c;觉得这个人太了不起了&#xff0c;而且人家大学里就自研开发出…

javascript的toFixed()以及使用

toFixed() 是 JavaScript 中数字类型&#xff08;Number&#xff09;的一个方法&#xff0c;用来将数字转换为指定小数位数的字符串表示形式。 使用方式和示例&#xff1a; let num 123.45678; let fixedNum num.toFixed(2); console.log(fixedNum); // 输出 "123.46&qu…

【Gradio】构建自定义多模态聊天机器人

这是我们构建自定义多模态聊天机器人组件两部分系列的第一部分。在第一部分中&#xff0c;我们将修改 Gradio 聊天机器人组件&#xff0c;使其能够在同一消息中显示文本和媒体文件&#xff08;视频、音频、图片&#xff09;。在第二部分中&#xff0c;我们将构建一个自定义的文…

深度解析RocketMq源码-持久化组件(一) MappedFile

1. 绪论 rocketmq之所以能够有如此大的吞吐量&#xff0c;离不开两个组件&#xff0c;一个是利用netty实现的高性能网络通信组件&#xff1b;另一个就是利用mmap技术实现的存储组件。而在rocketmq的存储组件中主要有三个组件&#xff0c;分别是持久化文件commitLog&#xff0c…

音樂大模型的崛起:技術革新與行業變革

音樂大模型的崛起&#xff1a;技術革新與行業變革 在過去的一個月中&#xff0c;隨著多個音樂大模型的輪番上線&#xff0c;音樂創作的門檻驟然降低&#xff0c;使得普通人也能輕鬆創作出高質量的音樂作品。這一技術進步引發了廣泛的討論&#xff0c;尤其是在音樂圈內&#xf…

多模态MLLM都是怎么实现的(10)-Chameleon和Florence-2如果你想玩多模态就不能不了解

这个也是一个补充文&#xff0c;前9章基本把该讲的讲了&#xff0c;今天这个内容主要是因为Meta出了一个Chameleon&#xff0c;这个以后可能会成为LLaMA的一个很好的补充&#xff0c;或者说都有可能统一起来&#xff0c;叫LLaMA或者Chamleon或者什么别的&#xff0c;另外我司把…

【图解IO与Netty系列】Netty源码解析——事件循环

Netty源码解析——事件循环 Netty事件循环源码解析select()processSelectedKeys()NioMessageUnsafe#read()NioByteUnsafe#read() runAllTasks() Netty事件循环 当Netty服务端启动起来以后&#xff0c;就可以接受客户端发送的请求&#xff0c;接收到客户端发来的请求后就会有事…

计算机网络 交换机的VLAN配置

一、理论知识 1.VLAN的定义 ①VLAN虚拟局域网&#xff0c;是一种通过将局域网内的设备逻辑地而不是物理地划分成一个个网段从而实现虚拟工作组的技术。 ②IEEE于1999年颁布了用以标准化VLAN实现方案的802.1Q协议标准草案。 ③VLAN技术允许网络管理者将一个物理的LAN逻辑地划…

【Ruby简单脚本02】双色球系统

# frozen_string_literal: true require date # 生成中奖号码的工具 # 红球 1-32 篮球 1-15 def create_num nums [] 6.times do while true num rand(1..32) unless nums.include?(num) nums << num break end end end blue rand(1..15) nums…

MySQL存储管理(一):删数据

从表中删除数据 从表中删除数据&#xff0c;也即是delete过程。 什么是表空间 表空间可以看做是InnoDB存储引擎逻辑结构的最高层&#xff0c;所有的数据都存放在表空间中。默认情况下&#xff0c;InnoDB存储引擎有一个共享表空间idbdata1&#xff0c;即所有数据都存放在这个表…

无限滚动表格

纵向无限滚动 单元格内部横向滚动 <!--* Description: 横向、纵向滚动表格* Author: liyanfeng liyanfenghopewind.com* Date: 2024-06-15 16:06:57* LastEditors: liyanfeng liyanfenghopewind.com* LastEditTime: 2024-06-20 17:15:37* FilePath: \plus-ui\src\componen…

Gone框架介绍33 - HTTP 注入说明

gone是可以高效开发Web服务的Golang依赖注入框架 github地址&#xff1a;https://github.com/gone-io/gone 文档地址&#xff1a;https://goner.fun/zh/ 文章目录 HTTP 注入说明HTTP 依赖注入标签的格式支持注入的类型和响应标签Query参数注入属性类型为简单类型[1]属性类型为简…

SEO是什么?SEO相关发展历史

一、SEO是什么意思&#xff1f; SEO&#xff08;Search Engine Optimization&#xff09;&#xff0c;翻译成中文就是“搜索引擎优化”。简单来讲&#xff0c;seo是指自然搜索结果下获得的网站流量的技术&#xff0c;是可以不用花钱就可以让自己的网站有好的排名&#xff0c;也…

关于Redis知识的理解

系列文章 关于时间复杂度o(1), o(n), o(logn), o(nlogn)的理解 关于HashMap的哈希碰撞、拉链法和key的哈希函数设计 关于JVM内存模型和堆内存模型的理解 关于代理模式的理解 关于Mysql基本概念的理解 关于软件设计模式的理解 文章目录 Redis的由来一、Redis数据类型的发展…