《算法设计编程实验:大学程序设计课程与竞赛训练教材》——1.1 机理分析法的实验范例

简介: 本节书摘来自华章计算机《算法设计编程实验:大学程序设计课程与竞赛训练教材》一书中的第1章,第1.1节,作者:吴永辉,王建德著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

1.1 机理分析法的实验范例

根据客观事物的特性,分析其内部的机理,弄清关系,在适当抽象的条件下,得到可以描述事物属性的数学工具。

image


经过数学分析,如果能够抽象出Ad Hoc类问题的内在规律,则可以采用机理分析法建立数学模型,然后根据模型的原理对应到算法,编程实现,通过执行算法得到问题解,如图1.1-1所示。
机理分析法的核心是数学建模,即使用适当的数学思想建立起模型;或者提取问题中的有效信息,用简明的方式表达其规律。需要注意的是:
1)选择的模型必须尽量多地体现问题的本质特征。但这并不意味着模型越复杂越好,累赘的信息会影响算法效率。
2)模型的建立不是一个一蹴而就的过程,而是要经过反复地检验和修改,在实践中不断完善。
3)数学模型通常有严格的格式,但程序编写形式可不拘一格。
机理分析法是一个复杂的数据抽象过程。我们要善于透视问题的本质,寻找突破口,进而选择适当的模型。模型的构造过程可以帮助我们认识问题,不同的模型从不同的角度反映问题,可以引发不同的思路,起到引导发散思维的作用。但认识问题的最终目的是解决问题,模型的固有性质虽可帮我们建立算法,其优劣也可通过时空复杂度等指标来分析和衡量,但最终还是以程序的运行结果为标准。所以模型不是一成不变的,同样要通过各种技术不断优化。模型的产生虽然是人脑思维的产物,但它仍然是客观事物在人脑中的反映。所以要培养良好的建模能力,还必须依靠在平时的学习中积累丰富的知识和经验。
下面举两个实验范例。
【1.1.1 Factstone Benchmark】
【问题描述】
Amtel已经宣布,到2010年,它将发行128位计算机芯片;到2020年,它将发行256位计算机;等等,Amtel坚持每持续十年将其字大小翻一番的战略。(Amtel于2000年发行了64位计算机,在1990年发行了32位计算机,在1980年发行了16位计算机,在1970年发行了8位计算机,并首先在1960年发行了4位计算机。)
Amtel将使用新的标准检查等级(Factstone)来宣传其新芯片大大提高的能力。Factstone等级被定义为这样的最大整数n,使得n!可以表示为一个计算机字中的无符号整数(比如1960年的4位计算机可表示3!=6,而不能表示4!=24)。
给出一个年份1960≤y≤2160,Amtel最近发布的芯片的Factstone等级是什么?
输入:
给出若干测试用例。每个测试用例一行,给出年份y。在最后一个测试用例后,给出包含0的一行。
输出:
对于每个测试用例,输出一行,给出Factstone等级。
image

试题来源:Waterloo local 2005.09.24
在线测试地址:POJ 2661,UVA 10916
试题解析
对于给定的年份,先求出当年Amtel处理器的字大小,然后计算出最大的n的值,使得n!成为一个符合字的大小的无符号整数。
在1960年,字的大小是4位,以后每十年字的大小翻一番,这就意味着,y年字的位数为k=22+y-196010。能放在k位中最大的无符号整数是2k-1。如果n!为不大于2k-1的最大正整数,则n为y年芯片的Factstone等级。计算方法有两种:
方法1:直接求不大于2k-1的最大正整数n!,这种方法极容易溢出且速度慢。
方法2:采用对数计算,即根据log2n!=log2n+log2(n-1)+…+log21≤log2(2k-1)计算y年字的位数k,累加log2i(i从1出发,每次加1),直到数字超过k为止。此时,i-1即为Factstone等级。
程序清单

#include <stdio.h>
#include <math.h>

int y,Y,i,j,m;             // 年份为y
double f,w;// y年字的位数为w,log2i的累加值为f

main(){
while (1 == scanf("%d",&y) && y){// 输入年份y
w = log(4);// 按照每十年字的大小翻一番的规律,计算y年字的位数w
for (Y=1960; Y<=y; Y+=10){
w *= 2;
}
i = 1;// 累加log2i(每次i加1),直到数字超过w
f = 0;
while (f < w) {
f += log((double)++i);
}
printf("%d\n",i-1) ;// 输出Factstone等级
}
if (y) printf("fishy ending %d\n",y);
}

【1.1.2 Bridge】
【问题描述】
n个人要在晚上过桥,在任何时候最多两人一组过桥,每组要有一只手电筒。在这n个人中只有一只手电筒可以用,因此要安排以某种往返的方式来返还手电筒,使得更多的人可以过桥。
每个人的过桥速度不同,每组的速度由速度较慢的成员所决定。请确定一个策略,让n个人用最少的时间过桥。
输入:
输入的第一行给出n,接下来的n行给出每个人的过桥时间,不会超过1000人,且没有人的过桥时间超过100秒。
输出:
输出的第一行给出所有n个人过桥的总的秒数,接下来的若干行给出实现策略。每行包含一个或两个整数,表示组成一组过桥的一个人或两个人(每个人用其在输入中给出的过桥所用的时间来标识。虽然许多人的过桥时间相同,但即使有混淆,对结果也没有影响)。这里要注意的是过桥也有方向性,因为要返还手电筒让更多的人通过。如果用时最少的策略有多个,则任意一个都可以。
image

试题来源:Waterloo local 2000.09.30
在线测试地址:POJ 2573,ZOJ 1877,UVA 10037
试题解析
稍动脑筋,便可以得出一个简单的逻辑:要使得n个人用最少的时间过桥,慢的成员必须借助快的成员传递手电筒。
由于一次过桥最多两人且手电筒需要往返传递,因此以两个成员过桥为一个分析单位,计算过桥时间。我们按过桥时间递增的顺序将n个成员排序。设当前序列中,
A是最快的人,B是次快的人,A和B是序列首部的两个元素。
a是最慢的人,b是次慢的人,a和b是序列尾部的两个元素。
有两种过桥方案:
方案1:用最快的成员A传递手电筒帮助最慢的a和b过桥。
如果带一个最慢的成员,则所用的时间是a+A(a表示最快和最慢的两个成员到对岸所需的时间,而A是最快的成员返回所需的时间)。显然带两个最慢的成员所用的时间是2*A+a+b。
方案2:用最快的成员A和次快的成员B传递手电筒帮助最慢的a和b过桥。
步骤1:A和B到对岸,所用时间为B。
步骤2:A返回,将手电筒给最慢的a和b,所用时间为A。
步骤3:a和b到对岸,所用时间为a;到对岸后,他们将手电筒交给B。
步骤4:B需要返回原来的岸边,因为要交还手电筒,所需时间为B。
所以,需要的总时间为2*B+A+a。
显然,最慢的a和b要用最少的时间过桥,只能借助A或者A和B传递手电筒过桥,其他方法都会增加过桥时间。哪一种过桥方案更有效?比较一下就行了:
如果(2A+a+b<2B+A+a),则采用第1种方案,即用最快的成员A传递手电筒;否则采用第2种方案,即用最快的成员A和次快的成员B传递手电筒(2A+a+b<2B+A+a等价于b+A<2*B)。
我们每次帮助最慢的两个成员过桥(n-=2),累计每个最佳过桥方案的时间。最后产生两种可能情况:
1)对岸剩下2个队员(n==2),全部过桥,即累计时间为B。
2)对岸剩下3个队员(n==3),用最快的成员传递手电筒,帮助最慢的成员过桥,然后与次慢的成员一起过桥,即累计时间为a+A+b。
程序清单

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<string>
using namespace std;
int n,i,j,k,a[111111];            // 人数为n,每个人的速度存储于序列a[]
int ans=0;// n个人过桥的总时间初始化
int main () {
scanf("%d",&n);// 输入每个人的速度
for(i=1;i<=n;i++)scanf("%d",a+i);
if(n==1){// 输出1个人的过桥方案
printf("%d\n%d\n",a[1],a[1]);return 0;
}
int nn=n;
sort(a+1,a+n+1);// 按照速度递增顺序排序
while(n>3){// 统计n个人过桥的总时间
if(a[1]+a[n-1]<2*a[2]){// 累计用a[1]传递手电筒帮助最慢的2个成员过桥
// 所需的时间
ans+=a[n]+a[1]*2+a[n-1];
}else{// 累计用a[1]a[2]传递手电筒帮助最慢的2个成员
// 过桥所需的时间
ans+=a[2]+a[1]+a[2]+a[n];
}
n-=2;// 两个最慢的成员过桥
}
if(n==2)ans+=a[2];// 对岸剩下2个成员,累计其过桥的时间
else ans+=a[1]+a[2]+a[3];// 对岸剩下3个成员,累计其过桥的时间
printf("%d\n",ans);// 输出n个人过桥的总时间
n=nn;
while(n>3){// 输出每组人过桥所用的时间

if(a[1]+a[n-1]<2*a[2])// 输出用a[1]传递手电筒的过桥方案
printf("%d %d\n%d\n%d %d\n%d\n",a[1],a[n],a[1],a[1],a[n-1],a[1]);
else// 输出用a[1]和a[2]传递手电筒的过桥方案
printf("%d %d\n%d\n%d %d\n%d\n",a[1],a[2],a[1],a[n-1],a[n],a[2]);
n-=2;// 两个最慢的成员过桥
}
if(n==2)printf("%d %d\n",a[1],a[2]);// 剩下2个队员过桥,输出过桥方案
else// 剩下3个队员过桥,输出过桥方案
printf("%d %d\n%d\n%d %d\n",a[1],a[3],a[1],a[1],a[2]);
return 0;
}
相关文章
|
11天前
|
算法 搜索推荐 开发者
别再让复杂度拖你后腿!Python 算法设计与分析实战,教你如何精准评估与优化!
在 Python 编程中,算法的性能至关重要。本文将带您深入了解算法复杂度的概念,包括时间复杂度和空间复杂度。通过具体的例子,如冒泡排序算法 (`O(n^2)` 时间复杂度,`O(1)` 空间复杂度),我们将展示如何评估算法的性能。同时,我们还会介绍如何优化算法,例如使用 Python 的内置函数 `max` 来提高查找最大值的效率,或利用哈希表将查找时间从 `O(n)` 降至 `O(1)`。此外,还将介绍使用 `timeit` 模块等工具来评估算法性能的方法。通过不断实践,您将能更高效地优化 Python 程序。
28 4
|
9天前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
20 1
|
28天前
|
算法 数据可视化
基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
奇异谱分析(SSA)是一种基于奇异值分解(SVD)和轨迹矩阵的非线性、非参数时间序列分析方法,适用于提取趋势、周期性和噪声成分。本项目使用MATLAB 2022a版本实现从强干扰序列中提取趋势线,并通过可视化展示了原时间序列与提取的趋势分量。代码实现了滑动窗口下的奇异值分解和分组重构,适用于非线性和非平稳时间序列分析。此方法在气候变化、金融市场和生物医学信号处理等领域有广泛应用。
|
15天前
|
机器学习/深度学习 存储 人工智能
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
使用Python作为开发语言,基于文本数据集(一个积极的xls文本格式和一个消极的xls文本格式文件),使用Word2vec对文本进行处理。通过支持向量机SVM算法训练情绪分类模型。实现对文本消极情感和文本积极情感的识别。并基于Django框架开发网页平台实现对用户的可视化操作和数据存储。
20 0
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
|
2月前
|
算法
算法设计与分析作业
这篇文章是关于算法设计与分析的作业,其中包含了两个算法实现:一个是使用分治算法实现的十进制大整数相乘(包括加法、减法和乘法函数),并进行了正确性和健壮性测试;另一个是使用快速排序思想实现的分治查找第K小元素的程序,并分析了其平均和最坏时间复杂度。
算法设计与分析作业
|
1月前
|
编解码 算法 图形学
同一路RTSP|RTMP流如何同时回调YUV和RGB数据实现渲染和算法分析
我们播放RTSP|RTMP流,如果需要同时做渲染和算法分析的话,特别是渲染在上层实现(比如Unity),算法是python这种情况,拉两路流,更耗费带宽和性能,拉一路流,同时回调YUV和RGB数据也可以,但是更灵活的是本文提到的按需转算法期望的RGB数据,然后做算法处理
|
2月前
|
人工智能 算法
第一周算法设计与分析 G : 排队援救
这篇文章介绍了解决算法问题"排队援救"的方法,通过使用队列和映射来模拟救援点的排队过程,并确定最终得到救援的人的顺序和编号。
|
2月前
|
算法 C++
第一周算法设计与分析 E : 构造回文串
这篇文章介绍了解决算法问题"构造回文串"的方法,即判断给定的整数N(视为字符串)是否可以通过在前面添加任意个0(或不添加)来构造一个回文串,并给出了相应的C++代码实现。
|
2月前
|
算法 C++
第一周算法设计与分析 H : 括号匹配
这篇文章介绍了解决算法问题"括号匹配"的方法,通过使用栈来检查给定字符串中的括号是否合法匹配,并提供了相应的C++代码实现。
|
2月前
|
算法 C++
第一周算法设计与分析 F : 模拟计算器
该文章 "第一周算法设计与分析 F : 模拟计算器" 的摘要或讨论。这篇文章介绍了如何设计一个程序来模拟一个基本的计算器,处理包含加、减、乘运算的表达式,并给出了相应的C++代码实现
下一篇
无影云桌面