遗传算法求根
⑴ 人工智能学什么
作为一名计算机专业的教育工作者,我来回答一下这个问题。
首先,人工智能专业属于计算机大类专业之一,虽然是新兴专业,但是由于当前人工智能领域的发展前景比较广阔,同时一系列人工智能技术也进入到了落地应用的阶段,所以当前人工智能专业也是热点专业之一。
人工智能专业有三个特点,其一是多学科交叉,涉及到计算机、数学、控制学、经济学、神经学、语言学等诸多学科,因此整体的知识量还是比较大的,其二是学习难度较大,人工智能本身的知识体系尚处在完善当中,很多领域还有待突破,其三是实践场景要求高。
基于这三个特点,要想在本科阶段有较好的学习效果,要有针对性的解决方案。针对于多学科交叉的情况,在大一期间一定要多做加法,尤其要重视编程语言的学习,基于编程语言来打开计算机技术大门,进而学习机器学习,而机器学习则被称为是打开人工智能技术大门的钥匙。
其三是要重视为自己营造一个较好的交流和实践场景,这对于学习效果有较大的影响,建议在大一、大二期间积极参加人工智能相关的课题组。在选择课题组的时候,要考虑到自己的兴趣爱好、课题周期、实践资源等因素,从这个角度来看,学校的科研资源对于人工智能专业的同学有较大的影响。
如果有互联网、大数据、人工智能等方面的问题,或者是考研方面的问题,都可以私信我!
很荣幸曾经参加过一次江苏省人工智能论坛,论坛上认真聆听了行业大佬周志华教授的报告,受益匪浅,首先呢,如果你是在校大学生,想要以后从事人工智能专业相关工作,我这里给你分享下 南京大学人工智能学院院长周志华教授 曾经在论坛上分享的南京大学人工智能专业本科生教育培养大纲的相关课程。
首先是基础数学部分:
数学分析、高等数学、高等代数、概率论与数理统计、最优化方法、数理逻辑。
其次是学科基础课程:
人工智能导引、数据结构与算法分析、程序设计基础、人工智能程序设计、机器学习导论、知识表示与处理、模式识别与计算机视觉、自然语言处理、数字系统设计基础、操作系统。
专业方向课程:
泛函分析、数字信号处理、高级机器学习、计算方法、控制理论方法、机器人学导论、多智能体系统、分布式与并行计算。
专业选修课课程:
数学建模、矩阵计算、随机过程、组合数学。博弈论及其应用、时间序列分析、编译原理、随机算法、数据库概论。
这是南京大学人工智能学院本科生四年的课程安排,看起来课程非常多,但这是一个培养体系,现在国内只有南京大学针对人工智能专业开设了如此系统的培养方案,专业涉及人工智能的各个领域方向。学生可以根据自己的兴趣爱好,选择想要学习的领域方向。
如果你已经毕业,想要转行从事人工智能行业,那么下面这套课程可能比较适合你:
1.莫烦python教程(网络可搜): 莫烦python有很多专栏,可以学习到python基础、以及人工智能相关的软件框架教程,包括相关人工智能相关的一些实战小项目。
2.吴恩达机器学习(网易云课堂): 人工智能机器学习理论部分,非常适合零基础的小白学习
3.吴恩达卷积神经网络(网易云课堂): 人工智能深度学习理论部分,非常适合零基础的小白学习
4.李飞飞CS231n(网易云课堂): 人工智能深度学习和机器学习理论,适合有一定基础的学习者。
5.吴恩达cs229(blibli): 人工智能深度学习和机器学习理论,适合有一定基础的学习者。
这些基础课程学会了,可能就算是跨入了半个门槛,当然面试的时候还欠缺实战经验,于是你可以去kaggle或者天池参加一些比赛,有了这些比赛经验,简历上也算是多了一块实战经验,增加了你的面试成功率。最后,不要参加什么培训机构区培训,既花钱又学不到什么东西,最后毕业还会给你简历造假,得不偿失,我给你推荐的这些课程绝对比市面上99.99%的培训机构课程靠谱!
接下来文章会侧重在以下几方面
1、零基础如何进行人工智能的自学(以找工作为目的),包括路径规划,怎么学等等。
2、我的个人感悟,关于转行、工作、创业、希望能给大家一些启发。
3、好的学习资源分享
先说一下个人背景,一本,经济学毕业,上学时从未学过编程。我这里指的零基础指的是,没有编程基础、没有数学基础(数学需要一些基本的,如果没有,后续也会帮助大家的)。
刚毕业第一年时,迷茫,不知道做什么。
第一阶段:边工作边自学爬虫,失败
毕业一年后,觉得编程可能是自己想要的,所以开始自学编程。
最开始学的是爬虫,python语言。每天学6个小时,一周五到六天。学了4个月后,去面了五六家企业,没有成功。原因是爬虫的知识够,可是计算机的基础太薄弱。什么算法、计算机网络这些,统统没学。因为我当时是完全自学,没有人带,导致我也不知道要学这些。第一阶段,失败,说实话,有点气馁,那可是每天没日没夜的学习啊,最后却换来一场空。可是生活还得继续,怨天尤人有什么用。
第二阶段:边工作边自学人工智能,成功
面试失败后,考虑了要把编程基础学一下再去面试,还是学点别的。我的决定是学人工智能,当时对这个比较感兴趣。好了,又是学了半年多,每天学6个小时,一周6天。从机器学习学到深度学习再学回机器学习。面试,成功地去公司从事机器学习深度学习方面的基础工作。不过实力肯定没有那些编程出身,数学、统计出身的人强,所以很多时候也是边学边做,打打杂。
其实我说的很简单很轻松的样子,但其中的艰辛只有自己是最清楚。所以我很希望通过我未来经验学习的分享,帮助大家少走一些弯路。
第三阶段:自己干
现在,已从公司辞职,自己开发网站,做社群,开网店。就是觉得,其实编程也只是我的一个工具,这个人就是比较喜欢自己做点事情,编程挺累的,哈哈哈。如果大家有什么合作的好点子,也欢迎随时来找我哦。
十问十答:
1、零基础转行学编程可以吗?可以,要做好吃苦的准备。学习是个漫长的过程,你上班的话,能否保证一定时间的学习呢,这个是你要问自己的。我也是边工作边学习,不同的是,我工作很清闲,所以我基本可以在上班时间学习。如果你还在上学,恭喜你这是你最好的机会了。
2、该自学还是去培训班?我觉得自学就够了,培训班真是又贵又水。这是我进过培训班的朋友告诉我的。其实你工作之后会发现,很多东西都是要自学的。如果你连自学都没办法自学的话,你又怎么能工作。而且,自学的效率会更高,当然前提是路径不能错。
3、转行编程,就业率怎么样?说实话,如果你不是编程出身的,要转行编程其实是比较难的,毕竟人家4年的正统学习不是白学的。但这不意味着就没办法。找准目标,规划好路径,学习最必要的知识,这样就有机会。但是,请做好学完仍找不到工作的心理准备。
4、最理想的自学环境是怎么样的?清晰的学习路径+自学+交流讨论的环境+有人指导
5、人工智能零基础可以学吗?可以,但是比一般转行编程的要难,因为要自学的东西更多,要求的门槛也会更高。这个后续会着重讲到。
6、学人工智能需要数学吗?不要因为数学而望而切步,数学是需要的,但没有要求的高不可攀,通过必要的学习,是可以达到入门水准的。
7、以前没接触过编程,怎么办?可以学习python,这真的是一门对零基础的人来说很友好的语言了,其他的我不懂。
8、一般转行编程的周期要多久?按我跟我周边朋友的经验来看。一周5-6天,一天6小时学习时间,4-7个月,这应该是比较正常的。
9、我是怎么坚持下来的?期间有很多次想要放弃,有的时候是真的看不懂,也没人教,纯自学,安装个工具有什么时候就要安装半天,不多说,都是泪啊。你的欲望有多强烈,就能有多坚持。
10、现在学编程还来得及吗?永远都来得及,学编程不一定是为了好工作,它更是一个全新的世界,你会发现很多对自己有帮助的东西。就算以后你不做这个,我相信这个学习的过程也会有所收获。
这是我之后会写的文章的大概目录,大家可以参考一下。
以下系列是暂定的,一篇文章可能会写成好几篇。这个系列不仅仅以学习为目的,目的是为了达到机器学习的工作入门标准。并不简单,但努力就有可能。网上的教程我看了很多,路径大部分都没有错。只是我觉得第一,太贵,明明网上有很多免费的更好的资源。第二,练习的量远远不够达到能去找工作的标准。
目录:
零基础自学人工智能系列(1):机器学习的最佳学习路径规划(亲身经验)
零基础自学人工智能系列(2):机器学习的知识准备(数学与python,附学习资源)
零基础自学人工智能系列(3):机器学习的知识准备(数学篇详解)
零基础自学人工智能系列(4):机器学习的知识准备(python篇详解)
零基础自学人工智能系列(5):机器学习的理论学习规划(附资源)
零基础自学人工智能系列(6):深度学习的理论学习规划(附资源)
零基础自学人工智能系列(7):机器学习的实战操作(附资源和代码)
零基础自学人工智能系列(8):深度学习的实战操作(附资源和代码)
零基础自学人工智能系列(9):找工作篇,需加强的部分(类似数据结构与算法)
最后,我希望我能给大家树立一些信心。不管你现在处于什么水平,只要肯努力,什么都有可能的。
首先我们需要一定的数学基础,如:高数、线性代数、概率论、统计学等等。很多人可能要问,我学习人工智能为什么要有数学基础呢?二者看似毫不相干,实则不然。线性代数能让我们了解如何将研究对象形象化,概率论能让我们懂得如何描述统计规律,此外还有许多其他数学科目,这些数学基础能让我们在学习人工智能的时候事半功倍。
1、学习并掌握一些数学知识
高等数学是基础中的基础,一切理工科都需要这个打底,数据挖掘、人工智能、模式识别此类跟数据打交道的又尤其需要多元微积分运算基础
线性代数很重要,一般来说线性模型是你最先要考虑的模型,加上很可能要处理多维数据,你需要用线性代数来简洁清晰的描述问题,为分析求解奠定基础
概率论、数理统计、随机过程更是少不了,涉及数据的问题,不确定性几乎是不可避免的,引入随机变量顺理成章,相关理论、方法、模型非常丰富。很多机器学习的算法都是建立在概率论和统计学的基础上的,比如贝叶斯分类器、高斯隐马尔可夫链。
再就是优化理论与算法,除非你的问题是像二元一次方程求根那样有现成的公式,否则你将不得不面对各种看起来无解但是要解的问题,优化将是你的GPS为你指路
有以上这些知识打底,就可以开拔了,针对具体应用再补充相关的知识与理论,比如说一些我觉得有帮助的是数值计算、图论、拓扑,更理论一点的还有实/复分析、测度论,偏工程类一点的还有信号处理、数据结构。
2、掌握经典机器学习理论和算法
如果有时间可以为自己建立一个机器学习的知识图谱,并争取掌握每一个经典的机器学习理论和算法,我简单地总结如下:
1) 回归算法:常见的回归算法包括最小二乘法(OrdinaryLeast Square),逻辑回归(Logistic Regression),逐步式回归(Stepwise Regression),多元自适应回归样条(MultivariateAdaptive Regression Splines)以及本地散点平滑估计(Locally Estimated Scatterplot Smoothing);
2) 基于实例的算法:常见的算法包括 k-Nearest Neighbor(KNN), 学习矢量量化(Learning Vector Quantization, LVQ),以及自组织映射算法(Self-Organizing Map , SOM);
3) 基于正则化方法:常见的算法包括:Ridge Regression, Least Absolute Shrinkage and Selection Operator(LASSO),以及弹性网络(Elastic Net);
4) 决策树学习:常见的算法包括:分类及回归树(ClassificationAnd Regression Tree, CART), ID3 (Iterative Dichotomiser 3), C4.5, Chi-squared Automatic Interaction Detection(CHAID), Decision Stump, 随机森林(Random Forest), 多元自适应回归样条(MARS)以及梯度推进机(Gradient Boosting Machine, GBM);
5) 基于贝叶斯方法:常见算法包括:朴素贝叶斯算法,平均单依赖估计(AveragedOne-Dependence Estimators, AODE),以及Bayesian Belief Network(BBN);
6) 基于核的算法:常见的算法包括支持向量机(SupportVector Machine, SVM), 径向基函数(Radial Basis Function ,RBF), 以及线性判别分析(Linear Discriminate Analysis ,LDA)等;
7) 聚类算法:常见的聚类算法包括 k-Means算法以及期望最大化算法(Expectation Maximization, EM);
8) 基于关联规则学习:常见算法包括 Apriori算法和Eclat算法等;
9) 人工神经网络:重要的人工神经网络算法包括:感知器神经网络(PerceptronNeural Network), 反向传递(Back Propagation), Hopfield网络,自组织映射(Self-OrganizingMap, SOM)。学习矢量量化(Learning Vector Quantization, LVQ);
10) 深度学习:常见的深度学习算法包括:受限波尔兹曼机(RestrictedBoltzmann Machine, RBN), Deep Belief Networks(DBN),卷积网络(Convolutional Network), 堆栈式自动编码器(Stacked Auto-encoders);
11) 降低维度的算法:常见的算法包括主成份分析(PrincipleComponent Analysis, PCA),偏最小二乘回归(Partial Least Square Regression,PLS), Sammon映射,多维尺度(Multi-Dimensional Scaling, MDS), 投影追踪(ProjectionPursuit)等;
12) 集成算法:常见的算法包括:Boosting, Bootstrapped Aggregation(Bagging),AdaBoost,堆叠泛化(Stacked Generalization, Blending),梯度推进机(GradientBoosting Machine, GBM),随机森林(Random Forest)。
3、掌握一种编程工具,比如Python
一方面Python是脚本语言,简便,拿个记事本就能写,写完拿控制台就能跑;另外,Python非常高效,效率比java、r、matlab高。matlab虽然包也多,但是效率是这四个里面最低的。
4、了解行业最新动态和研究成果,比如各大牛的经典论文、博客、读书笔记、微博微信等媒体资讯。
5、买一个GPU,找一个开源框架,自己多动手训练深度神经网络,多动手写写代码,多做一些与人工智能相关的项目。
6、选择自己感兴趣或者工作相关的一个领域深入下去
人工智能有很多方向,比如NLP、语音识别、计算机视觉等等,生命有限,必须得选一个方向深入的专研下去,这样才能成为人工智能领域的大牛,有所成就。
再回答第二个问题,人工智能到底是不是一项技术?
根据网络给的定义,人工智能(Artificial Intelligence),英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的还能的理论、方法、技术及应用系统的一门新的技术科学。
网络关于人工智能的定义详解中说道:人工智能是计算机的一个分支,二十世纪七十年代以来被称为世界三大尖端技术之一(空间技术、能源技术、人工智能)。也被认为是二十一世纪三大尖端技术(基因工程、纳米科学、人工智能)之一。这是因为近三十年来它获得了迅速的发展,在很多学科领域都获得了广泛应用,并取得了丰硕的成果,人工智能已逐步成为一个独立的分支,无论在理论和实践上都已自成一个系统。
综上,从定义上讲,人工智能是一项技术。
希望能帮到你。
人工智能需要学习的主要内容包括:数学基础课学科基础课,包括程序设计基础、数据结构、人工智能导论、计算机原理、 数字电路 、系统控制等;专业选修课,比如 神经网络 、深度学习以及认知科学、神经科学、计算金融、计算生物学、计算语言学等交叉课程。
一、人工智能专业学什么
1.认知与神经科学课程群
具体课程:认知心理学、神经科学基础、人类的记忆与学习、语言与思维、计算神经工程
2.人工智能伦理课程群
具体课程:《人工智能、 社会 与人文》、《人工智能哲学基础与伦理》
3.科学和工程课程群
新一代人工智能的发展需要脑科学、神经科学、认知心理学、信息科学等相关学科的实验科学家和理论科学家的共同努力,寻找人工智能的突破点,同时必须要以严谨的态度进行科学研究,让人工智能学科走在正确、 健康 的发展道路上。
4.先进机器人学课程群
具体课程:《先进机器人控制》、《认知机器人》、,《机器人规划与学习》、《仿生机器人》
5.人工智能平台与工具课程群
具体课程:《群体智能与自主系统》《无人驾驶技术与系统实现》《 游戏 设计与开发》《计算机图形学》《虚拟现实与增强现实》。
6.人工智能核心课程群
具体课程:《人工智能的现代方法I》《问题表达与求解》、《人工智能的现代方法II》《机器学习、自然语言处理、计算机视觉等》。
二、人工智能专业培养目标及要求
以培养掌握人工智能理论与工程技术的专门人才为目标,学习机器学习的理论和方法、深度学习框架、工具与实践平台、自然语言处理技术、语音处理与识别技术、视觉智能处理技术、国际人工智能专业领域最前沿的理论方法,培养人工智能专业技能和素养,构建解决科研和实际工程问题的专业思维、专业方法和专业嗅觉。
探索 实践适合中国高等人工智能人才培养的教学内容和教学方法,培养中国人工智能产业的应用型人才。
三、人工智能专业简介
人工智能专业是中国高校人计划设立的专业,旨在培养中国人工智能产业的应用型人才,推动人工智能一级学科建设。2018年4月,教育部在研究制定《高等学校引领人工智能创新行动计划》,并研究设立人工智能专业,进一步完善中国高校人工智能学科体系。2019年3月,教育部印发了《教育部关于公布2018年度普通高等学校本科专业备案和审批结果的通知》,根据通知,全国共有35所高校获首批“人工智能”新专业建设资格。
2020年3月3日,教育部公布2019年度普通高等学校本科专业备案和审批结果,“人工智能”专业成为热门。
人工智能是一个综合学科,其本身涉及很多方面,比如神经网络、机器识别、机器视觉、机器人等,因此,我们想要学好整个人工智能是很不容易的。
首先我们需要一定的数学基础,如:高数、线性代数、概率论、统计学等等。很多人可能要问,我学习人工智能为什么要有数学基础呢?二者看似毫不相干,实则不然。线性代数能让我们了解如何将研究对象形象化,概率论能让我们懂得如何描述统计规律,此外还有许多其他数学科目,这些数学基础能让我们在学习人工智能的时候事半功倍。
然后我们需要的就是对算法的累积,比如人工神经网络、遗传算法等。人工智能的本身还是通过算法对生活中的事物进行计算模拟,最后做出相应操作的一种智能化工具,算法在其中扮演的角色非常重要,可以说是不可或缺的一部分。
最后需要掌握和学习的就是编程语言,毕竟算法的实现还是需要编程的,推荐学习的有Java以及Python。如果以后想往大数据方向发展,就学习Java,而Python可以说是学习人工智能所必须要掌握的一门编程语言。当然,只掌握一门编程语言是不够的,因为大多数机器人的仿真都是采用的混合编程模式,即采用多种编程软件及语言组合使用,在人工智能方面一般使用的较多的有汇编和C++,此外还有MATLAB、VC++等,总之一句话,编程是必不可少的一项技能,需要我们花费大量时间和精力去掌握。
人工智能现在发展得越来越快速,这得益于计算机科学的飞速发展。可以预料到,在未来,我们的生活中将随处可见人工智能的产品,而这些产品能为我们的生活带来很大的便利,而人工智能行业的未来发展前景也是十分光明的。所以,选择人工智能行业不会错,但正如文章开头所说,想入行,需要我们下足功夫,全面掌握这个行业所需要的技能才行。
,首先呢,如果你是在校大学生,想要以后从事人工智能专业相关工作,我这里给你分享下 南京大学人工智能学院院长周志华教授 曾经在论坛上分享的南京大学人工智能专业本科生教育培养大纲的相关课程。
首先是基础数学部分:
人工智能亦称智械、机器智能,指由人制造出来的机器所表现出来的智能。通常人工智能是指通过普通计算机程序来呈现人类智能的技术。通过医学、神经科学、机器人学及统计学等的进步,有些预测则认为人类的无数职业也逐渐被人工智能取代。
⑵ 安徽大学计算机科学与技术学院的研究生专业
一.概况
计算机应用技术专业现设有计算机应用技术的二级学科博士点和硕士点,其培养方式为硕士、博士、提前攻博等等。2002年获准国家立项的计算机应用技术重点学科,2003年获准建立计算机应用技术博士后流动站。硕士研究生学制3年,实行学分制,2005年招生规模为30人。博士研究生学制2年,实行学分制,2005年招生规模为12人。
近年来,本学科先后获得211工程和国家重点学科经费资助,软硬件设施得到了根本改善,在主要研究方向已形成人才高地。
二.学科研究方向介绍
主要研究方向是计算智能与知识工程,包括问题求解商空间理论及其应用、基于商空间理论的粒度计算理论及其应用、构造性机器学习理论及其应用、优化理论与方法的研究、新的层次机器学习理论和方法的研究以及复杂系统的优化技术和方法等等,获得了一批原创性在国内外有重要影响的科研成果。
三.专业课程设置
1.学位课
英语、科学社会主义理论与实践、自然辩证法概论、组合数学、算法设计分析、高级数据库系统、计算机科学数学理论、人工神经网络的理论及应用、人工智能高级教程、高级数据库技术等等
2.非学位课
并行计算、智能计算、计算机视觉、知识发现、专家系统及其开发环境、优化理论及方法、构造性学习理论与方法和数据仓库及数据采集等等
四、学科导师队伍
张铃:男,1937年5月生,福建福清人,1961年毕业于南京大学数学天文系.同年分配至安徽工作,先后在安徽四所大学任教。1993年调至安徽大学人工智能研究所,任所长、教授、博士生导师至今。1986年4月由讲师破格晋升为正教授,1988年被授予国家有突出贡献的中青年专家称号,1991年获享受国家特殊津贴待遇,先后被清华大学、浙江大学、同济大学和中科院智能所等单位聘为客座教授。获得荣誉称号:改革开放以来,获全国教育系统劳动模范等省级以上荣誉称号八次;先后获国家自然科学奖等省级以上学术奖励十次;1978年获安徽省首届科技大会成果奖;1984年获第六届ICL欧洲人工智能奖;1987年获国家教委科学技术进步一等奖;1991年获国家教委科学技术进步二等奖;1992年专着《问题求解理论及应用》获全国高等学校出版社优秀学术专着特等奖;1992年专着《新一代计算技术前沿的研究》获全国优秀科技图书一等奖;1993年获电子工业部科技进步一等奖;1995年获国家自然科学三等奖;1999年获“全国优秀科技图书奖”暨“科技进步奖(科技着作)”一等奖;1999年获安徽省自然科学二等奖。目前主要研究方向有:商空间粒度计算理论(这是目前国际上三大粒度计算理论之一)、智能计算、机器学习理论和方法等。
程家兴:男,澳大利亚南澳大学博士,教授,现任安徽大学计算智能与信号处理教育部重点实验室主任,博士生导师,安徽省计算机学会常务理事,澳大利亚南澳大学SCG研究所研究员。主持和参加国家自然科学基金项目,国家自然科学基金中澳特别基金项目、教育部“优秀青年教师资助计划”项目、教育部博士点基金项目等。与澳大利亚南澳大学建立国际合作关系。研究方向:智能计算,算法分析与设计,最优化方法。获安徽省高校科技进步3等奖,安徽省第三届自然科学优秀学术论文2等奖.。目前,指导博士生5名,硕士生9名。主讲课程有具体数学,智能计算,优化理论与方法,组合数学以及本科生离散数学教学课程等。
张燕平:女,1962.2出生,安徽巢湖人;1981年毕业于上海电力学院热工自动化专业; 1989年作为合肥工业大学微机应用研究所研究生获工学硕士;2000年9月至2003年7月在职读博士研究生,并获得安徽大学计算机应用专业工学博士学位。2000年6月任安徽大学计算机系副教授;2003年担任计算机应用专业硕士研究生导师; 2004年11月任教授。主持完成安徽省教育厅自然科学研究项目1项,参加国家自然科学基金项目多项。2004年获安徽省科技进步二等奖。已在《计算机学报》、《计算机研究与发展》等国家重点期刊和国家级期刊发表学术论文18篇。
汪继文:男,1958年9月生,安徽宿松人。1982年1月本科毕业于安庆师范学院数学系,获理学学士学位。1989年7月硕士毕业于安徽大学数学系,获理学硕士学位。2001年7月博士毕业于中国科学技术大学数学系,获理学博士学位。2001.12 进入中国科技大学动力工程及工程热物理博士后流动站火灾科学国家重点实验室做博士后。2004.8出站,获博士后证书。1982.1-1986.9在安庆师范学院数学系任教。1989年7月硕士毕业后留校到安徽大学计算机学院(原为计算机系)任教到至今。2001年6月担任硕士生导师,2002年9月受聘为教授。2002.12入选为安徽省高校中青年学科带头人培养对象。三次获教学优秀奖,一次获安徽省高校科技进步三等奖。目前主要研究方向是计算机数值模拟技术,先后参加了5项国家自然科学基金项目的研究工作,主持完成两项省教委项目。目前参加一项国家自然科学基金项目,主持一项省自然科学基金项目。已发表学术论文28篇,SCI收录论文4篇。 1. 智能软件
学科带头人李龙澍教授,博士生导师,主要研究兴趣为软件体系结构、不精确知识表示和智能Agent技术,发表研究论文50多篇,主持开发的主要系统有:农业气象决策支持系统、大型数据库管理系统、电子政务系统、网络信息管理系统。
软件体系结构的研究:探讨知识的继承机制和抽象原理,使智能软件系统的数据库、模型库和方法库融为一体,引进了知识的层次结构,增强系统的可用性和维护效率。完成国家“863”项目“基于气象分析的指导农作物种植管理软构件”,主持研究国家自然科学基金项目“智能软件体系结构和组件技术的研究”,深入研究模糊商结构理论,将粒度计算理论用于建造软件体系结构模型,提出了一种基于商空间的智能软件体系结构构造模型,研究成果在农业气象、河流污染、公路管理、煤矿救护等GIS系统中有广泛应用。
不精确知识表示的研究:深入研究不精确知识表示的特点,提出一种适合领域特征的信息处理系统的框架和数据约简、知识发现方案,促进知识库系统开发技术水平的发展。研制适合模糊粗糙集信息处理的新的智能软件体系结构,不仅具有重大学术价值,而且在农业气象分析应用中取得其它方法和系统无法替代的明显效果,结合农业气象信息,分析模糊粗糙集的特性和优点,研制适合知识处理的构件模型,用于建造减灾防灾、农作物管理等实际决策支持系统,产生巨大的社会经济效益。
智能Agent技术的研究:Agent体系结构是智能Agent研究中一个重要的研究方向,它所要解决的问题是智能Agent是由哪些模块组成,这些模块之间如何交换信息,以及如何将这些模块用软件或硬件的方式组合起来形成一个有机的整体。结合完成国家“863”项目、国家自然科学基金项目等重大科研项目和机器人世界杯足球锦标赛RoboCup(Robot World Cup),面向大中型企业的数据仓库进行数据挖掘和建造基于Agent技术的智能决策支持系统,为安徽现代化建设产生重大社会经济效益。
2. 数据库与Web技术
学科带头人郑诚博士、副教授。2002年12月毕业于中国科学技术大学计算机系,并获博士学位,研究方向:数据库与数据仓库技术、知识发现与数据挖掘技术、人工智能与机器学习、新一代Web技术等。2005年9月起在安徽大学计算机科学与技术博士后流动站进行博士后研究(在职)。安徽大学中青年骨干教师,安徽省高校骨干教师培养对象。近几年内作为主要骨干参加国家自然科学基金、863计划、安徽省自然科学基金项目等项目4项。主持省教育厅自然科学研究项目二项,发表学术论文20余篇。
数据库与Web技术方向:研究数据库与数据仓库及其应用技术、基于数据库和数据仓库的数据挖掘技术,研究多粒度数据挖掘技术,将它们应用于税务、网络安全等领域;研究语义Web技术,在Web中引入有关智能技术,让计算机能理解Web上的信息。
3. 并行计算
学科带头人刘锋,博士,教授。主要研究方向:软件工程、并行计算、网格计算,承担国家自然基金项目、教育部科研项目、安徽省自然基金项目和安徽省教育厅自然基金项目多项。
近期发表的主要论着:
1. 基于改进型遗传算法的门阵列模式布局 (EI)小型微型计算机系统 2002,no.3
2. 求复函数方程根的遗传算法 计算机工程与应用2001年,37卷,第24期
3. PVM环境下求复函数方程根的并行遗传算法 小型微型计算机系统 2003,no.7
4. ORACLE数据库的MIT在营业帐务系统中的应用 电信技术 2001.9
5. 电子出版物与纸质出版物异同论 情报科学 2001.7
6. 基于遗传算法的方程求根算法的设计和实现 (EI)控制理论与应用 2004年第3期
7. Internet QoS控制机制综述 计算机科学 2002.3.
8. 基于分布理论和遗传算法的多项式求根算法 微机发展 2001年第6期
9. 基于Agent网格计算性能的实时调节 计算机工程与应用 2003年第39期
10. 并行遗传算法求复函数方程根的设计和实现 (EI)系统工程理论与实践 2004年第6期
4. 中间件技术
学科带头人邹海,博士,高工。2001年3月至2003年7月在中国矿业大学电气工程(信息与电子技术)博士后科研流动站从事博士后研究。近年来主要专注于模糊与随机环境下的粗糙集理论与知识获取、中间件技术等方面的研究。主持或参与完成了国家自然科学基金项目1项、948项目1项、省部级自然科学基金2项和10多项横向合作项目,目前在研省青年教师基金项目1项、省教育厅自然科学基金1项,获省、部级科学技术进步奖3项,发表论文10余篇。近年来承担了包括东北晚中生代资源预测专家系统、坝工建筑物实时监测数据采集系统、基于网络通讯的远程分布式遥测系统、基于数据挖掘的防汛抗旱调度指挥系统、B/S/S架构的客户关系管理系统在内的多个应用系统的设计和研发工作,并得以成功应用。
模糊与随机环境下的粗糙集理论与知识获取研究:针对信息识别中大量存在的不完备信息和随机环境这一的特点,结合智能信息处理领域近年来迅速发展起来的粗糙集(Rough Set)理论,深入研究在复杂系统中不完备信息及其随机环境下知识的表示、知识的约简、知识的学习、归纳和推理等。
中间件技术的研究:中间件技术作为90年代初发展起来的基础软件,近几年来逐渐成为构建网络分布式应用系统的重要支撑工具。它能够解决网络分布计算环境中多种异构数据资源互联共享问题,实现多种应用软件的协同工作。研究方向涉及分布式高性能高可靠企业级基础软件平台架构与机制、应用集成架构与技术、J2EE应用服务器、、工作流技术、移动中间件技术、反射中间件技术、嵌入式中间件技术、网络即插即用中间技术件、普适计算中间件技术、网格计算中间件技术、CORBA高级技术等。目前,中间件已与操作系统、数据库、前端应用软件一起,跻身于软件业发展的重点之列,并成为分布式应用的关键性软件。它可广泛适用于政府部门、银行、证券、电力、电信、交通与军事等关键性的网络分布应用。 一、研究生始招时间及在校研究生规模
始招时间:2002年
在校研究生规模:约60人
二、导师梯队介绍
1.计算机视觉及应用方向
韦穗:安徽大学副校长、教授、中国图像图形学会副理事长、教育部科学委员会信息学部委员,1983年4月至1985年9月在美国密执安大学及弗吉尼亚多理学院作访问学者。长期从事计算机视觉、图像图形学、模式识别、数学形态学和全息成像等领域的研究。近年来承担了多项国家自然科学基金项目和863项目。其中大容量快速图像分析系统(负责人)获中科院科技进步二等奖;并荣获国家863计划智能机器人主题先进工作者称号及国家科技部授予的国家863计划先进工作者称号。863项目“基于VR技术的装配帮助系统”(负责人)的研究, 2000年经863专家组组织验收,认为该项目的成果对于本领域的研究起到了开拓性的作用。国家自然基金项目“基于SVD分解的射影重构算法研究”在图形学中的多视图几何、3D重构和基于图像的绘制、图像获取几何和降低计算复杂性,实现复杂景物的3D描述与显示方面取得了一定的研究成果。主持了2002年第二届国际图像图形学会年会,编辑了两本会议论文集,其中大部分论文都被EI收录,翻译出版《计算机视觉中的多视图几何》(由英国剑桥大学出版社和原着作者Richard Hartley和Sman的授权)。
梁栋:博士、教授(博导),安徽大学电子科学与技术学院副院长。1985年和1990年在安徽大学获学士和硕士学位,2002年获安徽大学计算机应用技术专业工学博士学位。1991年晋升为安徽大学讲师,1996年晋升为安徽大学副教授,2002年晋升为安徽大学教授。1995年被评为安徽大学中青年骨干教师和安徽省中青年骨干教师培养对象,2002年被评为安徽省高等学校中青年学科带头人培养对象。近年来,在国内外学术期刊和学术会议上发表专题学术论文30多篇,主持和参加安徽省自然科学基金、国家自然科学基金、国家863计划、国家科技部科技型中小企业技术创新基金等科研项目20多项,先后获安徽省科技进步四等奖1项、安徽省高等学校科技进步三等奖2项、新型实用专利1项、安徽大学教学成果二等奖1项。主要研究领域:计算机视觉、图象信息处理。
2.图像处理与识别方向
罗斌:博士、教授(博导),英国约克大学计算机科学博士,安徽大学计算机科学与技术学院教授,博士生导师,安徽省首批“皖江学者”特聘教授,安徽省跨世纪学术技术带头人后备人选,安徽大学计算机科学与技术学院院长。中国图象图形学会理事、学术委员会、青年工作委员会委员,IEEE学会会员,IEEE计算机学会会员,英国BMVA会员。研究领域为数字图像处理与模式识别。目前主持国家自然科学基金项目《基于邻接图谱理论的图像聚类方法研究》,以及教育部“优秀青年教师资助计划”项目、安徽省人才开发基金和安徽省教育厅自然科学研究项目等。与国外同行专家保持有良好的合作关系,参加英国EPSRC项目的研究。主要研究成果有:应用现代图的分解理论对图像的结构化描述、图匹配理论和图的聚类方法进行了研究;利用EM算法和矩阵的SVD分解理论得到不同大小及包含结构噪声图的匹配方法,提出一种基于图匹配的图像配准算法;将图的谱分解理论应用于图像的识别和聚类,提出图谱结构特征提取方法,以及利用谱特征进行图的识别与聚类,并应用于图像库的检索。研究成果曾获亚洲计算机视觉学术会议最佳论文奖和安徽省科技进步三等奖。在国内外学术刊物和国际会议上发表论文70余篇,论文被SCI、EI、ISTP等索引40多次,论文代表作曾发表于《IEEE Transactions on Pattern Analysis and Machine Intelligence》、《Computer Vision and Image Understanding》、《Pattern Recognition》、《Pattern Recognition Letters》、《Image Vision Computing》等学术期刊。
3.智能信息处理方向
吴小培:博士、教授(博导)。2002年12月于中国科学技术大学获博士学位,研究方向为生物医学信号处理。2003年10月起在中国科学技术大学信号与信息处理博士后流动站进行博士后研究(在职), 2004年4月-9月美国加州大学圣地亚哥分校访问学者。安徽大学中青年骨干教师,安徽省高校学科带头人培养对象。研究领域:盲信号处理,生物医学信号处理和语音、图像处理和识别。近年内主持和参加国家自然科学基金、安徽省自然科学基金项目等项目5项。发表学术论文40余篇。在盲源分离、独立分量分析和脑电信号处理等方面的研究成果在国内有一定的影响,相关论文多次被同行引用。
柴晓冬:教授,博士。安徽省高校中青年骨干教师。目前在中国科技大学电子技术与科学系做博士后研究(在职),研究内容为基于生物特征识别的信息安全。参与研究国家自然科学基金项目两项,主持省教委自然科学基金项目二项,在国内外重要学术刊物及学术会议上发表论文三十余篇。
4.多维信号处理方向
陶亮:博士、教授(博导),安徽省高校学科拔尖人才,计算机科学与技术学院院长助理。2003年于中国科技大学获得信息与通信工程专业博士学位。1997年考取国家留学基金委公派访问学者资格,次年被派往加拿大温莎大学访问研修一年。1999年被选为安徽大学中青年骨干教师,2001年入选教育部优秀青年教师资助计划并获项目资助,2002年入选安徽省高校首批学科拔尖人才。自1988年研究生毕业留校以来,一直从事教学与科研工作,曾给本科生、研究生开设或主讲过多门专业课程,获得过校教学成果奖和校教书育人先进个人称号;是本校信号与信息处理专业硕士生导师(该学位授予点开点导师之一),同时也是本校计算机应用技术专业博士生导师。参加或主持过多项科学研究,近期主持了安徽省教育厅自然科学重点研究项目、安徽省自然科学基金项目及教育部优秀青年教师资助计划项目的研究各一项。主要研究方向为多维信号处理、生物特征识别技术。在《Journal of Computer Science and Technology》、《Chinese Journal of Electronics》、《电子学报》、《Chinese Optics Letters》等核心学术期刊以及国际学术会议上发表论文50多篇,获得过安徽省第四届自然科学优秀学术论文奖,目前(2005年4月)已有2篇论文被SCI收录,22篇论文被EI收录,10篇论文被ISTP收录,多篇论文被他人引用;有专着1部(《实值Gabor变换理论及应用》);是《电路与系统学报》和《计算机辅助设计和图形学学报》审稿人以及IEEE国际电路与系统专业学术年会审稿人(被邀请担任过审稿委员会委员、专题分会主持)。
三、主要学术成果
1.在国家自然科学基金项目“基本矩阵的鲁棒性计算及应用”支持下,应用视觉理论、投影几何、代数几何、矩阵分析和现代数学最优化理论,完成了基本矩阵的鲁棒性算法研究,并给出了在3维计算机视觉中相关问题的鲁棒性算法。
2.在国家自然科学基金项目“基于SVD分解的射影重构算法研究”支持下,对基于SVD分解的射影重构算法作深入系统的研究,并通过模拟数据和真实图像两方面的实验,获得图像中匹配点噪声效应的定量理解和算法性能的定性理解。
3.在国家自然科学基金项目“基于照片的场景重现”支持下,对基于序列图像的全景漫游技术进行了研究,主要包括:图像插补问题、图像整合问题及全景图生成问题。
4.在国家“863”计划项目“基于虚拟现实技术的装配帮助系统”支持下,完成了以下研究工作:1)建立一个Windows环境下的多模综合实验平台;2)实现一个基于视点的物体识别、定位的帮助装配系统的虚拟现实系统;3)对摄像机自标定、基于视点的插补、3D重构等问题进行了深入地研究。经国家“863”专家组鉴定:对本领域的研究起到了开拓性的作用。
5.在国家自然科学基金项目支持下对计算机产生体视全息图进行了研究。全息技术能提供所有视点、距离上的3D(深度)感知,它是目前最理想的3D显示。当今来自计算机、卫星、先进医学成像设备、战场环境的精确模拟以及地质勘探等各个领域的数据与日俱增,人们越来越希望能将这些数据变换成人们更易理解的形式,即真3D显示的形式。它无须借助眼镜、头盔等辅助设备,并用计算机生成3D显示的编码,由光电器件生成空间显示。
6.先后完成“基于图像的交通肇事现场测距系统”、“基于图像序列的交互式全景漫游生成系统”、“合肥风光交互式全景漫游系统”、“基于图像的犯罪现场重现系统”、“芜湖长江大桥和合肥中心油库交互演示系统”、“宜昌交互式招商引资展示系统”等开发和研制,并应用于交通事故处理、公安刑侦、城市规划、旅游宣传等多个方面,取得了较好的社会效益和经济效益。对计算机视觉、图像处理以及虚拟现实技术的推广应用起到了积极的促进作用。其中“基于图像的交通肇事现场测距系统”和“合肥风光交互式全景漫游系统”经合肥市科技局组织专家鉴定:核心技术水平达到国际先进水平,系统达到国内领先水平,并填补国内空白。
7. 在国家自然科学基金、安徽省自然科学基金项目等项目的支持下,初步验证了用独立分量描述思维脑电特征的可行性,并提出了基于思维脑电独立分量特征的脑机接口技术研究新设想。该研究思路和阶段性成果获得了国内外专家的肯定;研究了小波变换和独立分量分析进行结合的可行性,实验结果表明,基于小波变换和ICA的时频空三域分析方法能较好地解决多导脑电信号ICA分析中存在的过完备问题和非平稳问题;研究了在线ICA算法及其实现技术,提出了一种简单实用的在线Infomax算法,并用于实测脑电数据的在线消澡问题,取得了较理想的结果,该项成果是对Infomax 盲源分离算法的扩展和补充。
8.在教育部优秀青年教师资助计划项目、安徽省自然科学基金项目以及安徽省教育厅自然科学重点研究项目的支持下,研究提出了实值离散Gabor变换(RDGT)理论与快速算法,提出了基于RDGT的瞬变信号表示算法、基于过抽样RDGT的核磁共振FID信号增强算法,以及基于RDGT的线性时变系统表示与逼近方法;研究了基于人脸识别的身份认证方法与系统。研究成果以40多篇论文中英文形式发表在《Journal of Computer Science and Technology》、《Chinese Journal of Electronics》、《电子学报》等重要的核心学术期刊和若干国际学术会议上,并且已有20多篇论文被SCI、EI、ISTP收录。
四、学科研究方向介绍
1.计算机视觉及应用方向
将多视图几何与矩阵分析、谐波分析和现代数学最优化理论结合起来,研究基于图像的3D成像几何与物理中的算法和应用,包括基本矩阵的鲁棒计算及应用、基于照片的场景重现和SVD重构、基于虚拟现实技术的装配帮助系统、计算机产生体视全息图的研究及其在交通事故处理、公安刑侦、城市规划、旅游宣传、文化遗产保护等方面的应用。
2.图像处理与识别方向
将现代图的分解理论、现代统计学理论和模式识别理论应用于数字图像的分析与识别,对图像的结构化描述、图像特征的提取、图像的配准、结构模式识别中的图匹配理论和图的聚类方法进行研究,并将图匹配理论和图聚类方法应用于图像库的检索和索引。
3.智能信息处理方向
研究小波分析理论及其在脑电信号处理中的应用、基于时-频-空三域分析方法的思维脑电特征提取与识别、思维脑电的独立分量分析及其在脑机接口中的应用、在线盲源分离算法及其DSP实现。
4.多维信号处理方向
研究多维信号分析与处理技术的新理论和新方法,并应用于生物信息、语音、图像信号的处理和识别。如一维和二维实值离散Gabor变换理论、快速算法及应用的研究;复杂背景下灰度图像和彩色图像中人眼自动定位算法;基于人脸识别的身份认证方法与系统实现;支持向量机快速学习算法及应用;语音消澡和识别技术等。
⑶ 在C语言中,什么是迭代法
迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程,跟迭代法相对应的是直接法,即一次性解决问题。迭代法又分为精确迭代和近似迭代。“二分法”和“牛顿迭代法”属于近似迭代法。迭代算法是用计算机解决问题的一种基本方法。它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。
迭代是数值分析中通过从一个初始估计出发寻找一系列近似解来解决问题(一般是解方程或者方程组)的过程,为实现这一过程所使用的方法统称为迭代法(Iterative Method)。
一般可以做如下定义:对于给定的线性方程组x=Bx+f(这里的x、B、f同为矩阵,任意线性方程组都可以变换成此形式),用公式x(k+1)=Bx(k)+f(括号中为上标,代表迭代k次得到的x,初始时k=0)逐步带入求近似解的方法称为迭代法(或称一阶定常迭代法)。如果k趋向无穷大时limx(k)存在,记为x*,称此迭代法收敛。显然x*就是此方程组的解,否则称为迭代法发散。
跟迭代法相对应的是直接法(或者称为一次解法),即一次性的快速解决问题,例如通过开方解决方程x +3= 4。一般如果可能,直接解法总是优先考虑的。但当遇到复杂问题时,特别是在未知量很多,方程为非线性时,我们无法找到直接解法(例如五次以及更高次的代数方程没有解析解,参见阿贝耳定理),这时候或许可以通过迭代法寻求方程(组)的近似解。
最常见的迭代法是牛顿法。其他还包括最速下降法、共轭迭代法、变尺度迭代法、最小二乘法、线性规划、非线性规划、单纯型法、惩罚函数法、斜率投影法、遗传算法、模拟退火等等。
利用迭代算法解决问题,需要做好以下三个方面的工作:
确定迭代变量
在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。
建立迭代关系式
所谓迭代关系式,指如何从变量的前一个值推出其下一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以顺推或倒推的方法来完成。
对迭代过程进行控制
在
什么时候结束迭代过程?这是编写迭代程序必须考虑的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为两种情况:一种是所需的迭代次数
是个确定的值,可以计算出来;另一种是所需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需
要进一步分析出用来结束迭代过程的条件。
举例
例 1 :一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,每月新生一只兔子,新生的兔子也如此繁殖。如果所有的兔子都不死去,问到第 12 个月时,该饲养场共有兔子多少只?
分析:这是一个典型的递推问题。我们不妨假设第 1 个月时兔子的只数为 u 1 ,第 2 个月时兔子的只数为 u 2 ,第 3 个月时兔子的只数为 u 3 ,……根据题意,“这种兔子从出生的下一个月开始,每月新生一只兔子”,则有
u 1 = 1 , u 2 = u 1 + u 1 × 1 = 2 , u 3 = u 2 + u 2 × 1 = 4 ,……
根据这个规律,可以归纳出下面的递推公式:
u n = u(n - 1)× 2 (n ≥ 2)
对应 u n 和 u(n - 1),定义两个迭代变量 y 和 x ,可将上面的递推公式转换成如下迭代关系:
y=x*2
x=y
让计算机对这个迭代关系重复执行 11 次,就可以算出第 12 个月时的兔子数。参考程序如下:
cls
x=1
for i=2 to 12
y=x*2
x=y
next i
print y
end
例 2 :阿米巴用简单分裂的方式繁殖,它每分裂一次要用 3 分钟。将若干个阿米巴放在一个盛满营养参液的容器内, 45 分钟后容器内充满了阿米巴。已知容器最多可以装阿米巴 220,220个。试问,开始的时候往容器内放了多少个阿米巴?请编程序算出。
分析:根据题意,阿米巴每 3 分钟分裂一次,那么从开始的时候将阿米巴放入容器里面,到 45
分钟后充满容器,需要分裂 45/3=15 次。而“容器最多可以装阿米巴2^ 20 个”,即阿米巴分裂 15 次以后得到的个数是
2^20。题目要求我们计算分裂之前的阿米巴数,不妨使用倒推的方法,从第 15 次分裂之后的 2^20 个,倒推出第 15 次分裂之前(即第 14
次分裂之后)的个数,再进一步倒推出第 13 次分裂之后、第 12 次分裂之后、……第 1 次分裂之前的个数。
设第 1 次分裂之前的个数为 x 0 、第 1 次分裂之后的个数为 x 1 、第 2 次分裂之后的个数为 x 2 、……第 15 次分裂之后的个数为 x 15 ,则有
x 14 =x 15 /2 、 x 13 =x 14 /2 、…… x n-1 =x n /2 (n ≥ 1)
因为第 15 次分裂之后的个数 x 15 是已知的,如果定义迭代变量为 x ,则可以将上面的倒推公式转换成如下的迭代公式:
x=x/2 (x 的初值为第 15 次分裂之后的个数 2^20)
让这个迭代公式重复执行 15 次,就可以倒推出第 1 次分裂之前的阿米巴个数。因为所需的迭代次数是个确定的值,我们可以使用一个固定次数的循环来实现对迭代过程的控制。参考程序如下:
cls
x=2^20
for i=1 to 15
x=x/2
next i
print x
end
ps:java中幂的算法是Math.pow(2,20);返回double,稍微注意一下
例 3 :验证谷角猜想。日本数学家谷角静夫在研究自然数时发现了一个奇怪现象:对于任意一个自然数 n ,若 n 为偶数,则将其除以 2 ;若 n 为奇数,则将其乘以 3 ,然后再加 1。如此经过有限次运算后,总可以得到自然数 1。人们把谷角静夫的这一发现叫做“谷角猜想”。
要求:编写一个程序,由键盘输入一个自然数 n ,把 n 经过有限次运算后,最终变成自然数 1 的全过程打印出来。
分析:定义迭代变量为 n ,按照谷角猜想的内容,可以得到两种情况下的迭代关系式:当 n 为偶数时, n=n/2 ;当 n 为奇数时, n=n*3+1。用 QBASIC 语言把它描述出来就是:
if n 为偶数 then
n=n/2
else
n=n*3+1
end if
这就是需要计算机重复执行的迭代过程。这个迭代过程需要重复执行多少次,才能使迭代变量 n 最终变成自然数 1
,这是我们无法计算出来的。因此,还需进一步确定用来结束迭代过程的条件。仔细分析题目要求,不难看出,对任意给定的一个自然数 n
,只要经过有限次运算后,能够得到自然数 1 ,就已经完成了验证工作。因此,用来结束迭代过程的条件可以定义为:n=1。参考程序如下:
cls
input "Please input n=";n
do until n=1
if n mod 2=0 then
rem 如果 n 为偶数,则调用迭代公式 n=n/2
n=n/2
print "—";n;
else
n=n*3+1
print "—";n;
end if
loop
end
迭代法开平方:
#include<stdio.h>
#include<math.h>
void main()
{
double a,x0,x1;
printf("Input a:\n");
scanf("%lf",&a);//为什么在VC6.0中不能写成“scanf("%f",&a);”?
if(a<0)
printf("Error!\n");
else
{
x0=a/2;
x1=(x0+a/x0)/2;
do
{
x0=x1;
x1=(x0+a/x0)/2;
}while(fabs(x0-x1)>=1e-6);
}
printf("Result:\n");
printf("sqrt(%g)=%g\n",a,x1);
}
求平方根的迭代公式:x1=1/2*(x0+a/x0)。
算法:1.先自定一个初值x0,作为a的平方根值,在我们的程序中取a/2作为a的初值;利用迭代公式求出一个x1。此值与真正的a的平方根值相比,误差很大。
⒉把新求得的x1代入x0中,准备用此新的x0再去求出一个新的x1.
⒊利用迭代公式再求出一个新的x1的值,也就是用新的x0又求出一个新的平方根值x1,此值将更趋近于真正的平方根值。
⒋比较前后两次求得的平方根值x0和x1,如果它们的差值小于我们指定的值,即达到我们要求的精度,则认为x1就是a的平方根值,去执行步骤5;否则执行步骤2,即循环进行迭代。
迭代法是用于求方程或方程组近似根的一种常用的算法设计方法。设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行:
⑴ 选一个方程的近似根,赋给变量x0;
⑵ 将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0;
⑶ 当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤⑵的计算。
若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为:
【算法】迭代法求方程的根
{ x0=初始近似根;
do {
x1=x0;
x0=g(x1); /*按特定的方程计算新的近似根*/
} while (fabs(x0-x1)>Epsilon);
printf(“方程的近似根是%f\n”,x0);
}
迭代算法也常用于求方程组的根,令
X=(x0,x1,…,xn-1)
设方程组为:
xi=gi(X) (I=0,1,…,n-1)
则求方程组根的迭代算法可描述如下:
【算法】迭代法求方程组的根
{ for (i=0;i
x=初始近似根;
do {
for (i=0;i
y=x;
for (i=0;i
x=gi(X);
for (delta=0.0,i=0;i
if (fabs(y-x)>delta) delta=fabs(y-x);
} while (delta>Epsilon);
for (i=0;i
printf(“变量x[%d]的近似根是 %f”,I,x);
printf(“\n”);
}
具体使用迭代法求根时应注意以下两种可能发生的情况:
⑴ 如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制;
⑵ 方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。
递归
递归是设计和描述算法的一种有力的工具,由于它在复杂算法的描述中被经常采用,为此在进一步介绍其他算法设计方法之前先讨论它。
能采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。
【问题】 编写计算斐波那契(Fibonacci)数列的第n项函数fib(n)。
斐波那契数列为:0、1、1、2、3、……,即:
fib(0)=0;
fib⑴=1;
fib(n)=fib(n-1)+fib(n-2) (当n>1时)。
写成递归函数有:
int fib(int n)
{ if (n==0) return 0;
if (n==1) return 1;
if (n>1) return fib(n-1)+fib(n-2);
}
递归算法的执行过程分递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问
题(规模小于n)的求解。例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。也就是说,为计算fib(n),必须先计算
fib(n-1)和fib(n-
2),而计算fib(n-1)和fib(n-2),又必须先计算fib(n-3)和fib(n-4)。依次类推,直至计算fib⑴和fib(0),分别能
立即得到结果1和0。在递推阶段,必须要有终止递归的情况。例如在函数fib中,当n为1和0的情况。
在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到fib⑴和fib(0)后,返回得到fib⑵的结果,……,在得到了fib(n-1)和fib(n-2)的结果后,返回得到fib(n)的结果。
在编写递归函数时要注意,函数中的局部变量和参数知识局限于当前调用层,当递推进入“简单问题”层时,原来层次上的参数和局部变量便被隐蔽起来。在一系列“简单问题”层,它们各有自己的参数和局部变量。
由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。当某个递归算法能较方便地转换成递推算法时,通常按递推算法编写程序。例如上例计算斐波那契数列的第n项的函数fib(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项。
【问题】 组合问题
问题描述:找出从自然数1、2、……、n中任取r个数的所有组合。例如n=5,r=3的所有组合为:⑴5、4、3 ⑵5、4、2 ⑶5、4、1
⑷5、3、2 ⑸5、3、1 ⑹5、2、1
⑺4、3、2 ⑻4、3、1 ⑼4、2、1
⑽3、2、1
分析所列的10个组合,可以采用这样的递归思想来考虑求组合函数的算法。设函数为void comb(int
m,int
k)为找出从自然数1、2、……、m中任取k个数的所有组合。当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。这就将求m
个数中取k个数的组合问题转化成求m-1个数中取k-1个数的组合问题。设函数引入工作数组a[
]存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放在a[k]中,当一个组合求出后,才将a[
]中的一个组合输出。第一个数可以是m、m-1、……、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组合的其余元素,继续递
归去确定;或因已确定了组合的全部元素,输出这个组合。细节见以下程序中的函数comb。
【程序】
# include
# define MAXN 100
int a[MAXN];
void comb(int m,int k)
{ int i,j;
for (i=m;i>=k;i--)
{ a[k]=i;
if (k>1)
comb(i-1,k-1);
else
{ for (j=a[0];j>0;j--)
printf(“%4d”,a[j]);
printf(“\n”);
}
}
}
void main()
{ a[0]=3;
comb(5,3);
}
【问题】 背包问题
问题描述:有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。
设n
件物品的重量分别为w0、w1、…、wn-1,物品的价值分别为v0、v1、…、vn-1。采用递归寻找物品的选择方案。设前面已有了多种选择的方案,并
保留了其中总价值最大的方案于数组option[ ],该方案的总价值存于变量maxv。当前正在考察新方案,其物品选择情况保存于数组cop[
]。假定当前方案已考虑了前i-1件物品,现在要考虑第i件物品;当前方案已包含的物品的重量之和为tw;至此,若其余物品都选择是可能的话,本方案能达
到的总价值的期望值为tv。算法引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值maxv时,继续考察当前方案变成无意义的工作,应终止
当前方案,立即去考察下一个方案。因为当方案的总价值不比maxv大时,该方案不会被再考察,这同时保证函数后找到的方案一定会比前面的方案更好。
对于第i件物品的选择考虑有两种可能:
⑴ 考虑物品i被选择,这种可能性仅当包含它不会超过方案总重量限制时才是可行的。选中后,继续递归去考虑其余物品的选择。
⑵ 考虑物品i不被选择,这种可能性仅当不包含物品i也有可能会找到价值更大的方案的情况。
按以上思想写出递归算法如下:
try(物品i,当前选择已达到的重量和,本方案可能达到的总价值tv)
{ /*考虑物品i包含在当前方案中的可能性*/
if(包含物品i是可以接受的)
{ 将物品i包含在当前方案中;
if (i
try(i+1,tw+物品i的重量,tv);
else
/*又一个完整方案,因为它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
恢复物品i不包含状态;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (不包含物品i仅是可男考虑的)
if (i
try(i+1,tw,tv-物品i的价值);
else
/*又一个完整方案,因它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
}
为了理解上述算法,特举以下实例。设有4件物品,它们的重量和价值见表:
物品 0 1 2 3
重量 5 3 2 1
价值 4 4 3 1
并设限制重量为7。则按以上算法,下图表示找解过程。由图知,一旦找到一个解,算法就进一步找更好的佳。如能判定某个查找分支不会找到更好的解,算法不会在该分支继续查找,而是立即终止该分支,并去考察下一个分支。
按上述算法编写函数和程序如下:
【程序】
# include
# define N 100
double limitW,totV,maxV;
int option[N],cop[N];
struct { double weight;
double value;
}a[N];
int n;
void find(int i,double tw,double tv)
{ int k;
/*考虑物品i包含在当前方案中的可能性*/
if (tw+a.weight<=limitW)
{ cop=1;
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv;
}
cop=0;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (tv-a.value>maxV)
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv-a.value;
}
}
void main()
{ int k;
double w,v;
printf(“输入物品种数\n”);
scanf((“%d”,&n);
printf(“输入各物品的重量和价值\n”);
for (totv=0.0,k=0;k
{ scanf(“%1f%1f”,&w,&v);
a[k].weight=w;
a[k].value=v;
totV+=V;
}
printf(“输入限制重量\n”);
scanf(“%1f”,&limitV);
maxv=0.0;
for (k=0;k find(0,0.0,totV);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“\n总价值为%.2f\n”,maxv);
}
作为对比,下面以同样的解题思想,考虑非递归的程序解。为了提高找解速度,程序不是简单地逐一生成所有候选解,而是
从每个物品对候选解的影响来形成值得进一步考虑的候选解,一个候选解是通过依次考察每个物品形成的。对物品i的考察有这样几种情况:当该物品被包含在候选
解中依旧满足解的总重量的限制,该物品被包含在候选解中是应该继续考虑的;反之,该物品不应该包括在当前正在形成的候选解中。同样地,仅当物品不被包括在
候选解中,还是有可能找到比目前临时最佳解更好的候选解时,才去考虑该物品不被包括在候选解中;反之,该物品不包括在当前候选解中的方案也不应继续考虑。
对于任一值得继续考虑的方案,程序就去进一步考虑下一个物品。
【程序】
# include
# define N 100
double limitW;
int cop[N];
struct ele { double weight;
double value;
} a[N];
int k,n;
struct { int ;
double tw;
double tv;
}twv[N];
void next(int i,double tw,double tv)
{ twv.=1;
twv tw=tw;
twv tv=tv;
}
double find(struct ele *a,int n)
{ int i,k,f;
double maxv,tw,tv,totv;
maxv=0;
for (totv=0.0,k=0;k
totv+=a[k].value;
next(0,0.0,totv);
i=0;
While (i>=0)
{ f=twv.;
tw=twv tw;
tv=twv tv;
switch(f)
{ case 1: twv.++;
if (tw+a.weight<=limitW)
if (i
{ next(i+1,tw+a.weight,tv);
i++;
}
else
{ maxv=tv;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
case 0: i--;
break;
default: twv.=0;
if (tv-a.value>maxv)
if (i
{ next(i+1,tw,tv-a.value);
i++;
}
else
{ maxv=tv-a.value;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
}
}
return maxv;
}
void main()
{ double maxv;
printf(“输入物品种数\n”);
scanf((“%d”,&n);
printf(“输入限制重量\n”);
scanf(“%1f”,&limitW);
printf(“输入各物品的重量和价值\n”);
for (k=0;k
scanf(“%1f%1f”,&a[k].weight,&a[k].value);
maxv=find(a,n);
printf(“\n选中的物品为\n”);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“\n总价值为%.2f\n”,maxv);
}
⑷ 梯度下降法和粒子群优化算法的区别
粒子群(PSO)算法是近几年来最为流行的进化算法,最早是由Kenned和Eberhart于1995年提出.PSO 算法和其他进化算法类似,也采用“群体”和“进化”的概念,通过个体间的协作与竞争,实现复杂空间中最优解的搜索.PSO 先生成初始种群,即在可行解空间中随机初始化一群粒子,每个粒子都为优化问题的一个可行解,并由目标函数为之确定一个适应值(fitness value).PSO 不像其他进化算法那样对于个体使用进化算子,而是将每个个体看作是在n 维搜索空间中的一个没有体积和重量的粒子,每个粒子将在解空间中运动,并由一个速度决定其方向和距离.通常粒子将追随当前的最优粒子而运动,并经逐代搜索最后得到最优解.在每一代中,粒子将跟踪两个极值,一为粒子本身迄今找到的最优解 pbest ,另一为全种群迄今找到的最优解 gbest.由于认识到 PSO 在函数优化等领域所蕴含的广阔的应用前景,在 Kenned 和 Eberhart 之后很多学者都进行了这方面的研究.目前已提出了多种 PSO改进算法,并广泛应用到许多领域。
⑸ 迭代法的算法
迭代是数值分析中通过从一个初始估计出发寻找一系列近似解来解决问题(一般是解方程或者方程组)的过程,为实现这一过程所使用的方法统称为迭代法(Iterative Method)。
一般可以做如下定义:对于给定的线性方程组(这里的x、B、f同为矩阵,任意线性方程组都可以变换成此形式),用公式 (代表迭代k次得到的x,初始时k=0)逐步带入求近似解的方法称为迭代法(或称一阶定常迭代法)。如果存在,记为x*,称此迭代法收敛。显然x*就是此方程组的解,否则称为迭代法发散。
跟迭代法相对应的是直接法(或者称为一次解法),即一次性的快速解决问题,例如通过开方解决方程x +3= 4。一般如果可能,直接解法总是优先考虑的。但当遇到复杂问题时,特别是在未知量很多,方程为非线性时,我们无法找到直接解法(例如五次以及更高次的代数方程没有解析解,参见阿贝耳定理),这时候或许可以通过迭代法寻求方程(组)的近似解。
最常见的迭代法是牛顿法。其他还包括最速下降法、共轭迭代法、变尺度迭代法、最小二乘法、线性规划、非线性规划、单纯型法、惩罚函数法、斜率投影法、遗传算法、模拟退火等等。
利用迭代算法解决问题,需要做好以下三个方面的工作: 例 1 :一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,每月新生一只兔子,新生的兔子也如此繁殖。如果所有的兔子都不死去,问到第 12 个月时,该饲养场共有兔子多少只?
分析:这是一个典型的递推问题。我们不妨假设第 1 个月时兔子的只数为 u 1 ,第 2 个月时兔子的只数为 u 2 ,第 3 个月时兔子的只数为 u 3 ,……根据题意,“这种兔子从出生的下一个月开始,每月新生一只兔子”,则有
u 1 = 1 , u 2 = u 1 + u 1 × 1 = 2 , u 3 = u 2 + u 2 × 1 = 4 ,……
根据这个规律,可以归纳出下面的递推公式:
u n = u(n - 1)× 2 (n ≥ 2)
对应 u n 和 u(n - 1),定义两个迭代变量 y 和 x ,可将上面的递推公式转换成如下迭代关系:
y=x*2
x=y
让计算机对这个迭代关系重复执行 11 次,就可以算出第 12 个月时的兔子数。参考程序如下:
cls
x=1
for i=2 to 12
y=x*2
x=y
next i
print y
end
例 2 :阿米巴用简单分裂的方式繁殖,它每分裂一次要用 3 分钟。将若干个阿米巴放在一个盛满营养参液的容器内, 45 分钟后容器内充满了阿米巴。已知容器最多可以装阿米巴 220,220个。试问,开始的时候往容器内放了多少个阿米巴?请编程序算出。
分析:根据题意,阿米巴每 3 分钟分裂一次,那么从开始的时候将阿米巴放入容器里面,到 45 分钟后充满容器,需要分裂 45/3=15 次。而“容器最多可以装阿米巴2^ 20 个”,即阿米巴分裂 15 次以后得到的个数是 2^20。题目要求我们计算分裂之前的阿米巴数,不妨使用倒推的方法,从第 15 次分裂之后的 2^20 个,倒推出第 15 次分裂之前(即第 14 次分裂之后)的个数,再进一步倒推出第 13 次分裂之后、第 12 次分裂之后、……第 1 次分裂之前的个数。
设第 1 次分裂之前的个数为 x 0 、第 1 次分裂之后的个数为 x 1 、第 2 次分裂之后的个数为 x 2 、……第 15 次分裂之后的个数为 x 15 ,则有
x 14 =x 15 /2 、 x 13 =x 14 /2 、…… x n-1 =x n /2 (n ≥ 1)
因为第 15 次分裂之后的个数 x 15 是已知的,如果定义迭代变量为 x ,则可以将上面的倒推公式转换成如下的迭代公式:
x=x/2 (x 的初值为第 15 次分裂之后的个数 2^20)
让这个迭代公式重复执行 15 次,就可以倒推出第 1 次分裂之前的阿米巴个数。因为所需的迭代次数是个确定的值,我们可以使用一个固定次数的循环来实现对迭代过程的控制。参考程序如下:
cls
x=2^20
for i=1 to 15
x=x/2
next i
print x
end
ps:java中幂的算法是Math.pow(2,20);返回double,稍微注意一下
例 3 :验证谷角猜想。日本数学家谷角静夫在研究自然数时发现了一个奇怪现象:对于任意一个自然数 n ,若 n 为偶数,则将其除以 2 ;若 n 为奇数,则将其乘以 3 ,然后再加 1。如此经过有限次运算后,总可以得到自然数 1。人们把谷角静夫的这一发现叫做“谷角猜想”。
要求:编写一个程序,由键盘输入一个自然数 n ,把 n 经过有限次运算后,最终变成自然数 1 的全过程打印出来。
分析:定义迭代变量为 n ,按照谷角猜想的内容,可以得到两种情况下的迭代关系式:当 n 为偶数时, n=n/2 ;当 n 为奇数时, n=n*3+1。用 QBASIC 语言把它描述出来就是:
if n 为偶数 then
n=n/2
else
n=n*3+1
end if
这就是需要计算机重复执行的迭代过程。这个迭代过程需要重复执行多少次,才能使迭代变量 n 最终变成自然数 1 ,这是我们无法计算出来的。因此,还需进一步确定用来结束迭代过程的条件。仔细分析题目要求,不难看出,对任意给定的一个自然数 n ,只要经过有限次运算后,能够得到自然数 1 ,就已经完成了验证工作。因此,用来结束迭代过程的条件可以定义为:n=1。参考程序如下:
cls
input Please input n=;n
do until n=1
if n mod 2=0 then
rem 如果 n 为偶数,则调用迭代公式 n=n/2
n=n/2
print —;n;
else
n=n*3+1
print —;n;
end if
loop
end
迭代法开平方:
#include<stdio.h>
#include<math.h>
void main()
{
double a,x0,x1;
printf(Input a:
);
scanf(%lf,&a);//因为a是double型数据,所以要用%lf,而不是%f
if(a<0)
printf(Error!
);
else
{
x0=a/2;
x1=(x0+a/x0)/2;
do
{
x0=x1;
x1=(x0+a/x0)/2;
}while(fabs(x0-x1)>=1e-6);
}
printf(Result:
);
printf(sqrt(%g)=%g
,a,x1);
}
求平方根的迭代公式:x1=1/2*(x0+a/x0)。
算法:1.先自定一个初值x0,作为a的平方根值,在我们的程序中取a/2作为a的初值;利用迭代公式求出一个x1。此值与真正的a的平方根值相比,误差很大。
⒉把新求得的x1代入x0中,准备用此新的x0再去求出一个新的x1.
⒊利用迭代公式再求出一个新的x1的值,也就是用新的x0又求出一个新的平方根值x1,此值将更趋近于真正的平方根值。
⒋比较前后两次求得的平方根值x0和x1,如果它们的差值小于我们指定的值,即达到我们要求的精度,则认为x1就是a的平方根值,去执行步骤5;否则执行步骤2,即循环进行迭代。
迭代法是用于求方程或方程组近似根的一种常用的算法设计方法。设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行:
⑴ 选一个方程的近似根,赋给变量x0;
⑵ 将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0;
⑶ 当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤⑵的计算。
若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为:
【算法】迭代法求方程的根
{ x0=初始近似根;
do {
x1=x0;
x0=g(x1); /*按特定的方程计算新的近似根*/
} while (fabs(x0-x1)>Epsilon);
printf(“方程的近似根是%f
”,x0);
}
迭代算法也常用于求方程组的根,令
X=(x0,x1,…,xn-1)
设方程组为:
xi=gi(X) (I=0,1,…,n-1)
则求方程组根的迭代算法可描述如下:
【算法】迭代法求方程组的根
{ for (i=0;i
x=初始近似根;
do {
for (i=0;i
y=x;
for (i=0;i
x=gi(X);
for (delta=0.0,i=0;i
if (fabs(y-x)>delta) delta=fabs(y-x);
} while (delta>Epsilon);
for (i=0;i
printf(“变量x[%d]的近似根是 %f”,I,x);
printf(“
”);
}
具体使用迭代法求根时应注意以下两种可能发生的情况:
⑴ 如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制;
⑵ 方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。
递归
递归是设计和描述算法的一种有力的工具,由于它在复杂算法的描述中被经常采用,为此在进一步介绍其他算法设计方法之前先讨论它。
能采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。
【问题】 编写计算斐波那契(Fibonacci)数列的第n项函数fib(n)。
斐波那契数列为:0、1、1、2、3、……,即:
fib(0)=0;
fib⑴=1;
fib(n)=fib(n-1)+fib(n-2) (当n>1时)。
写成递归函数有:
int fib(int n)
{ if (n==0) return 0;
if (n==1) return 1;
if (n>1) return fib(n-1)+fib(n-2);
}
递归算法的执行过程分递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解。例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。也就是说,为计算fib(n),必须先计算fib(n-1)和fib(n- 2),而计算fib(n-1)和fib(n-2),又必须先计算fib(n-3)和fib(n-4)。依次类推,直至计算fib⑴和fib(0),分别能立即得到结果1和0。在递推阶段,必须要有终止递归的情况。例如在函数fib中,当n为1和0的情况。
在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到fib⑴和fib(0)后,返回得到fib⑵的结果,……,在得到了fib(n-1)和fib(n-2)的结果后,返回得到fib(n)的结果。
在编写递归函数时要注意,函数中的局部变量和参数知识局限于当前调用层,当递推进入“简单问题”层时,原来层次上的参数和局部变量便被隐蔽起来。在一系列“简单问题”层,它们各有自己的参数和局部变量。
由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。当某个递归算法能较方便地转换成递推算法时,通常按递推算法编写程序。例如上例计算斐波那契数列的第n项的函数fib(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项。
【问题】 组合问题
问题描述:找出从自然数1、2、……、n中任取r个数的所有组合。例如n=5,r=3的所有组合为:⑴5、4、3 ⑵5、4、2 ⑶5、4、1
⑷5、3、2 ⑸5、3、1 ⑹5、2、1
⑺4、3、2 ⑻4、3、1 ⑼4、2、1
⑽3、2、1
分析所列的10个组合,可以采用这样的递归思想来考虑求组合函数的算法。设函数为void comb(int m,int k)为找出从自然数1、2、……、m中任取k个数的所有组合。当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。这就将求m 个数中取k个数的组合问题转化成求m-1个数中取k-1个数的组合问题。设函数引入工作数组a[ ]存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放在a[k]中,当一个组合求出后,才将a[ ]中的一个组合输出。第一个数可以是m、m-1、……、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组合的其余元素,继续递归去确定;或因已确定了组合的全部元素,输出这个组合。细节见以下程序中的函数comb。
【程序】
# include
# define MAXN 100
int a[MAXN];
void comb(int m,int k)
{ int i,j;
for (i=m;i>=k;i--)
{ a[k]=i;
if (k>1)
comb(i-1,k-1);
else
{ for (j=a[0];j>0;j--)
printf(“%4d”,a[j]);
printf(“
”);
}
}
}
void main()
{ a[0]=3;
comb(5,3);
}
【问题】 背包问题
问题描述:有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。
设n 件物品的重量分别为w0、w1、…、wn-1,物品的价值分别为v0、v1、…、vn-1。采用递归寻找物品的选择方案。设前面已有了多种选择的方案,并保留了其中总价值最大的方案于数组option[ ],该方案的总价值存于变量maxv。当前正在考察新方案,其物品选择情况保存于数组cop[ ]。假定当前方案已考虑了前i-1件物品,现在要考虑第i件物品;当前方案已包含的物品的重量之和为tw;至此,若其余物品都选择是可能的话,本方案能达到的总价值的期望值为tv。算法引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值maxv时,继续考察当前方案变成无意义的工作,应终止当前方案,立即去考察下一个方案。因为当方案的总价值不比maxv大时,该方案不会被再考察,这同时保证函数后找到的方案一定会比前面的方案更好。
对于第i件物品的选择考虑有两种可能:
⑴ 考虑物品i被选择,这种可能性仅当包含它不会超过方案总重量限制时才是可行的。选中后,继续递归去考虑其余物品的选择。
⑵ 考虑物品i不被选择,这种可能性仅当不包含物品i也有可能会找到价值更大的方案的情况。
按以上思想写出递归算法如下:
try(物品i,当前选择已达到的重量和,本方案可能达到的总价值tv)
{ /*考虑物品i包含在当前方案中的可能性*/
if(包含物品i是可以接受的)
{ 将物品i包含在当前方案中;
if (i
try(i+1,tw+物品i的重量,tv);
else
/*又一个完整方案,因为它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
恢复物品i不包含状态;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (不包含物品i仅是可男考虑的)
if (i
try(i+1,tw,tv-物品i的价值);
else
/*又一个完整方案,因它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
}
为了理解上述算法,特举以下实例。设有4件物品,它们的重量和价值见表:
物品 0 1 2 3
重量 5 3 2 1
价值 4 4 3 1
并设限制重量为7。则按以上算法,下图表示找解过程。由图知,一旦找到一个解,算法就进一步找更好的佳。如能判定某个查找分支不会找到更好的解,算法不会在该分支继续查找,而是立即终止该分支,并去考察下一个分支。
按上述算法编写函数和程序如下:
【程序】
# include
# define N 100
double limitW,totV,maxV;
int option[N],cop[N];
struct { double weight;
double value;
}a[N];
int n;
void find(int i,double tw,double tv)
{ int k;
/*考虑物品i包含在当前方案中的可能性*/
if (tw+a.weight<=limitW)
{ cop=1;
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv;
}
cop=0;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (tv-a.value>maxV)
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv-a.value;
}
}
void main()
{ int k;
double w,v;
printf(“输入物品种数
”);
scanf((“%d”,&n);
printf(“输入各物品的重量和价值
”);
for (totv=0.0,k=0;k
{ scanf(“%1f%1f”,&w,&v);
a[k].weight=w;
a[k].value=v;
totV+=V;
}
printf(“输入限制重量
”);
scanf(“%1f”,&limitV);
maxv=0.0;
for (k=0;k find(0,0.0,totV);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“
总价值为%.2f
”,maxv);
}
作为对比,下面以同样的解题思想,考虑非递归的程序解。为了提高找解速度,程序不是简单地逐一生成所有候选解,而是从每个物品对候选解的影响来形成值得进一步考虑的候选解,一个候选解是通过依次考察每个物品形成的。对物品i的考察有这样几种情况:当该物品被包含在候选解中依旧满足解的总重量的限制,该物品被包含在候选解中是应该继续考虑的;反之,该物品不应该包括在当前正在形成的候选解中。同样地,仅当物品不被包括在候选解中,还是有可能找到比目前临时最佳解更好的候选解时,才去考虑该物品不被包括在候选解中;反之,该物品不包括在当前候选解中的方案也不应继续考虑。对于任一值得继续考虑的方案,程序就去进一步考虑下一个物品。
【程序】
# include
# define N 100
double limitW;
int cop[N];
struct ele { double weight;
double value;
} a[N];
int k,n;
struct { int ;
double tw;
double tv;
}twv[N];
void next(int i,double tw,double tv)
{ twv.=1;
twv tw=tw;
twv tv=tv;
}
double find(struct ele *a,int n)
{ int i,k,f;
double maxv,tw,tv,totv;
maxv=0;
for (totv=0.0,k=0;k
totv+=a[k].value;
next(0,0.0,totv);
i=0;
While (i>=0)
{ f=twv.;
tw=twv tw;
tv=twv tv;
switch(f)
{ case 1: twv.++;
if (tw+a.weight<=limitW)
if (i
{ next(i+1,tw+a.weight,tv);
i++;
}
else
{ maxv=tv;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
case 0: i--;
break;
default: twv.=0;
if (tv-a.value>maxv)
if (i
{ next(i+1,tw,tv-a.value);
i++;
}
else
{ maxv=tv-a.value;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
}
}
return maxv;
}
void main()
{ double maxv;
printf(“输入物品种数
”);
scanf((“%d”,&n);
printf(“输入限制重量
”);
scanf(“%1f”,&limitW);
printf(“输入各物品的重量和价值
”);
for (k=0;k
scanf(“%1f%1f”,&a[k].weight,&a[k].value);
maxv=find(a,n);
printf(“
选中的物品为
”);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“
总价值为%.2f
”,maxv);
}
⑹ 迭代法为什么能任取初始向量
一般可以做如下定义:对于给定的线性方程组(这里的x、B、f同为矩阵,任意线性方程组都可以变换成此形式),用公式 (代表迭代k次得到的x,初始时k=0)逐步带入求近似解的方法称为迭代法(或称一阶定常迭代法)。如果存在,记为x*,称此迭代法收敛。显然x*就是此方程组的解,否则称为迭代法发散。
跟迭代法相对应的是直接法(或者称为一次解法),即一次性的快速解决问题,例如通过开方解决方程x +3= 4。一般如果可能,直接解法总是优先考虑的。但当遇到复杂问题时,特别是在未知量很多,方程为非线性时,我们无法找到直接解法(例如五次以及更高次的代数方程没有解析解,参见阿贝耳定理),这时候或许可以通过迭代法寻求方程(组)的近似解。
最常见的迭代法是牛顿法。其他还包括最速下降法、共轭迭代法、变尺度迭代法、最小二乘法、线性规划、非线性规划、单纯型法、惩罚函数法、斜率投影法、遗传算法、模拟退火等等。
利用迭代算法解决问题,需要做好以下三个方面的工作: 例 1 :一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,每月新生一只兔子,新生的兔子也如此繁殖。如果所有的兔子都不死去,问到第 12 个月时,该饲养场共有兔子多少只?
分析:这是一个典型的递推问题。我们不妨假设第 1 个月时兔子的只数为 u 1 ,第 2 个月时兔子的只数为 u 2 ,第 3 个月时兔子的只数为 u 3 ,……根据题意,“这种兔子从出生的下一个月开始,每月新生一只兔子”,则有
u 1 = 1 , u 2 = u 1 + u 1 × 1 = 2 , u 3 = u 2 + u 2 × 1 = 4 ,……
根据这个规律,可以归纳出下面的递推公式:
u n = u(n - 1)× 2 (n ≥ 2)
对应 u n 和 u(n - 1),定义两个迭代变量 y 和 x ,可将上面的递推公式转换成如下迭代关系:
y=x*2
x=y
让计算机对这个迭代关系重复执行 11 次,就可以算出第 12 个月时的兔子数。参考程序如下:
cls
x=1
for i=2 to 12
y=x*2
x=y
next i
print y
end
例 2 :阿米巴用简单分裂的方式繁殖,它每分裂一次要用 3 分钟。将若干个阿米巴放在一个盛满营养参液的容器内, 45 分钟后容器内充满了阿米巴。已知容器最多可以装阿米巴 220,220个。试问,开始的时候往容器内放了多少个阿米巴?请编程序算出。
分析:根据题意,阿米巴每 3 分钟分裂一次,那么从开始的时候将阿米巴放入容器里面,到 45 分钟后充满容器,需要分裂 45/3=15 次。而“容器最多可以装阿米巴2^ 20 个”,即阿米巴分裂 15 次以后得到的个数是 2^20。题目要求我们计算分裂之前的阿米巴数,不妨使用倒推的方法,从第 15 次分裂之后的 2^20 个,倒推出第 15 次分裂之前(即第 14 次分裂之后)的个数,再进一步倒推出第 13 次分裂之后、第 12 次分裂之后、……第 1 次分裂之前的个数。
设第 1 次分裂之前的个数为 x 0 、第 1 次分裂之后的个数为 x 1 、第 2 次分裂之后的个数为 x 2 、……第 15 次分裂之后的个数为 x 15 ,则有
x 14 =x 15 /2 、 x 13 =x 14 /2 、…… x n-1 =x n /2 (n ≥ 1)
因为第 15 次分裂之后的个数 x 15 是已知的,如果定义迭代变量为 x ,则可以将上面的倒推公式转换成如下的迭代公式:
x=x/2 (x 的初值为第 15 次分裂之后的个数 2^20)
让这个迭代公式重复执行 15 次,就可以倒推出第 1 次分裂之前的阿米巴个数。因为所需的迭代次数是个确定的值,我们可以使用一个固定次数的循环来实现对迭代过程的控制。参考程序如下:
cls
x=2^20
for i=1 to 15
x=x/2
next i
print x
end
ps:java中幂的算法是Math.pow(2,20);返回double,稍微注意一下
例 3 :验证谷角猜想。日本数学家谷角静夫在研究自然数时发现了一个奇怪现象:对于任意一个自然数 n ,若 n 为偶数,则将其除以 2 ;若 n 为奇数,则将其乘以 3 ,然后再加 1。如此经过有限次运算后,总可以得到自然数 1。人们把谷角静夫的这一发现叫做“谷角猜想”。
要求:编写一个程序,由键盘输入一个自然数 n ,把 n 经过有限次运算后,最终变成自然数 1 的全过程打印出来。
分析:定义迭代变量为 n ,按照谷角猜想的内容,可以得到两种情况下的迭代关系式:当 n 为偶数时, n=n/2 ;当 n 为奇数时, n=n*3+1。用 QBASIC 语言把它描述出来就是:
if n 为偶数 then
n=n/2
else
n=n*3+1
end if
这就是需要计算机重复执行的迭代过程。这个迭代过程需要重复执行多少次,才能使迭代变量 n 最终变成自然数 1 ,这是我们无法计算出来的。因此,还需进一步确定用来结束迭代过程的条件。仔细分析题目要求,不难看出,对任意给定的一个自然数 n ,只要经过有限次运算后,能够得到自然数 1 ,就已经完成了验证工作。因此,用来结束迭代过程的条件可以定义为:n=1。参考程序如下:
cls
input Please input n=;n
do until n=1
if n mod 2=0 then
rem 如果 n 为偶数,则调用迭代公式 n=n/2
n=n/2
print —;n;
else
n=n*3+1
print —;n;
end if
loop
end
迭代法开平方:
#include<stdio.h>
#include<math.h>
void main()
{
double a,x0,x1;
printf(Input a:\n);
scanf(%lf,&a);//因为a是double型数据,所以要用%lf,而不是%f
if(a<0)
printf(Error!\n);
else
{
x0=a/2;
x1=(x0+a/x0)/2;
do
{
x0=x1;
x1=(x0+a/x0)/2;
}while(fabs(x0-x1)>=1e-6);
}
printf(Result:\n);
printf(sqrt(%g)=%g\n,a,x1);
}
求平方根的迭代公式:x1=1/2*(x0+a/x0)。
算法:1.先自定一个初值x0,作为a的平方根值,在我们的程序中取a/2作为a的初值;利用迭代公式求出一个x1。此值与真正的a的平方根值相比,误差很大。
⒉把新求得的x1代入x0中,准备用此新的x0再去求出一个新的x1.
⒊利用迭代公式再求出一个新的x1的值,也就是用新的x0又求出一个新的平方根值x1,此值将更趋近于真正的平方根值。
⒋比较前后两次求得的平方根值x0和x1,如果它们的差值小于我们指定的值,即达到我们要求的精度,则认为x1就是a的平方根值,去执行步骤5;否则执行步骤2,即循环进行迭代。
迭代法是用于求方程或方程组近似根的一种常用的算法设计方法。设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行:
⑴ 选一个方程的近似根,赋给变量x0;
⑵ 将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0;
⑶ 当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤⑵的计算。
若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为:
【算法】迭代法求方程的根
{ x0=初始近似根;
do {
x1=x0;
x0=g(x1); /*按特定的方程计算新的近似根*/
} while (fabs(x0-x1)>Epsilon);
printf(“方程的近似根是%f\n”,x0);
}
迭代算法也常用于求方程组的根,令
X=(x0,x1,…,xn-1)
设方程组为:
xi=gi(X) (I=0,1,…,n-1)
则求方程组根的迭代算法可描述如下:
【算法】迭代法求方程组的根
{ for (i=0;i
x=初始近似根;
do {
for (i=0;i
y=x;
for (i=0;i
x=gi(X);
for (delta=0.0,i=0;i
if (fabs(y-x)>delta) delta=fabs(y-x);
} while (delta>Epsilon);
for (i=0;i
printf(“变量x[%d]的近似根是 %f”,I,x);
printf(“\n”);
}
具体使用迭代法求根时应注意以下两种可能发生的情况:
⑴ 如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制;
⑵ 方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。
递归
递归是设计和描述算法的一种有力的工具,由于它在复杂算法的描述中被经常采用,为此在进一步介绍其他算法设计方法之前先讨论它。
能采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。
【问题】 编写计算斐波那契(Fibonacci)数列的第n项函数fib(n)。
斐波那契数列为:0、1、1、2、3、……,即:
fib(0)=0;
fib⑴=1;
fib(n)=fib(n-1)+fib(n-2) (当n>1时)。
写成递归函数有:
int fib(int n)
{ if (n==0) return 0;
if (n==1) return 1;
if (n>1) return fib(n-1)+fib(n-2);
}
递归算法的执行过程分递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解。例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。也就是说,为计算fib(n),必须先计算fib(n-1)和fib(n- 2),而计算fib(n-1)和fib(n-2),又必须先计算fib(n-3)和fib(n-4)。依次类推,直至计算fib⑴和fib(0),分别能立即得到结果1和0。在递推阶段,必须要有终止递归的情况。例如在函数fib中,当n为1和0的情况。
在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到fib⑴和fib(0)后,返回得到fib⑵的结果,……,在得到了fib(n-1)和fib(n-2)的结果后,返回得到fib(n)的结果。
在编写递归函数时要注意,函数中的局部变量和参数知识局限于当前调用层,当递推进入“简单问题”层时,原来层次上的参数和局部变量便被隐蔽起来。在一系列“简单问题”层,它们各有自己的参数和局部变量。
由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。当某个递归算法能较方便地转换成递推算法时,通常按递推算法编写程序。例如上例计算斐波那契数列的第n项的函数fib(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项。
【问题】 组合问题
问题描述:找出从自然数1、2、……、n中任取r个数的所有组合。例如n=5,r=3的所有组合为:⑴5、4、3 ⑵5、4、2 ⑶5、4、1
⑷5、3、2 ⑸5、3、1 ⑹5、2、1
⑺4、3、2 ⑻4、3、1 ⑼4、2、1
⑽3、2、1
分析所列的10个组合,可以采用这样的递归思想来考虑求组合函数的算法。设函数为void comb(int m,int k)为找出从自然数1、2、……、m中任取k个数的所有组合。当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。这就将求m 个数中取k个数的组合问题转化成求m-1个数中取k-1个数的组合问题。设函数引入工作数组a[ ]存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放在a[k]中,当一个组合求出后,才将a[ ]中的一个组合输出。第一个数可以是m、m-1、……、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组合的其余元素,继续递归去确定;或因已确定了组合的全部元素,输出这个组合。细节见以下程序中的函数comb。
【程序】
# include
# define MAXN 100
int a[MAXN];
void comb(int m,int k)
{ int i,j;
for (i=m;i>=k;i--)
{ a[k]=i;
if (k>1)
comb(i-1,k-1);
else
{ for (j=a[0];j>0;j--)
printf(“%4d”,a[j]);
printf(“\n”);
}
}
}
void main()
{ a[0]=3;
comb(5,3);
}
【问题】 背包问题
问题描述:有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。
设n 件物品的重量分别为w0、w1、…、wn-1,物品的价值分别为v0、v1、…、vn-1。采用递归寻找物品的选择方案。设前面已有了多种选择的方案,并保留了其中总价值最大的方案于数组option[ ],该方案的总价值存于变量maxv。当前正在考察新方案,其物品选择情况保存于数组cop[ ]。假定当前方案已考虑了前i-1件物品,现在要考虑第i件物品;当前方案已包含的物品的重量之和为tw;至此,若其余物品都选择是可能的话,本方案能达到的总价值的期望值为tv。算法引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值maxv时,继续考察当前方案变成无意义的工作,应终止当前方案,立即去考察下一个方案。因为当方案的总价值不比maxv大时,该方案不会被再考察,这同时保证函数后找到的方案一定会比前面的方案更好。
对于第i件物品的选择考虑有两种可能:
⑴ 考虑物品i被选择,这种可能性仅当包含它不会超过方案总重量限制时才是可行的。选中后,继续递归去考虑其余物品的选择。
⑵ 考虑物品i不被选择,这种可能性仅当不包含物品i也有可能会找到价值更大的方案的情况。
按以上思想写出递归算法如下:
try(物品i,当前选择已达到的重量和,本方案可能达到的总价值tv)
{ /*考虑物品i包含在当前方案中的可能性*/
if(包含物品i是可以接受的)
{ 将物品i包含在当前方案中;
if (i
try(i+1,tw+物品i的重量,tv);
else
/*又一个完整方案,因为它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
恢复物品i不包含状态;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (不包含物品i仅是可男考虑的)
if (i
try(i+1,tw,tv-物品i的价值);
else
/*又一个完整方案,因它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
}
为了理解上述算法,特举以下实例。设有4件物品,它们的重量和价值见表:
物品 0 1 2 3
重量 5 3 2 1
价值 4 4 3 1
并设限制重量为7。则按以上算法,下图表示找解过程。由图知,一旦找到一个解,算法就进一步找更好的佳。如能判定某个查找分支不会找到更好的解,算法不会在该分支继续查找,而是立即终止该分支,并去考察下一个分支。
按上述算法编写函数和程序如下:
【程序】
# include
# define N 100
double limitW,totV,maxV;
int option[N],cop[N];
struct { double weight;
double value;
}a[N];
int n;
void find(int i,double tw,double tv)
{ int k;
/*考虑物品i包含在当前方案中的可能性*/
if (tw+a.weight<=limitW)
{ cop=1;
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv;
}
cop=0;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (tv-a.value>maxV)
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv-a.value;
}
}
void main()
{ int k;
double w,v;
printf(“输入物品种数\n”);
scanf((“%d”,&n);
printf(“输入各物品的重量和价值\n”);
for (totv=0.0,k=0;k
{ scanf(“%1f%1f”,&w,&v);
a[k].weight=w;
a[k].value=v;
totV+=V;
}
printf(“输入限制重量\n”);
scanf(“%1f”,&limitV);
maxv=0.0;
for (k=0;k find(0,0.0,totV);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“\n总价值为%.2f\n”,maxv);
}
作为对比,下面以同样的解题思想,考虑非递归的程序解。为了提高找解速度,程序不是简单地逐一生成所有候选解,而是从每个物品对候选解的影响来形成值得进一步考虑的候选解,一个候选解是通过依次考察每个物品形成的。对物品i的考察有这样几种情况:当该物品被包含在候选解中依旧满足解的总重量的限制,该物品被包含在候选解中是应该继续考虑的;反之,该物品不应该包括在当前正在形成的候选解中。同样地,仅当物品不被包括在候选解中,还是有可能找到比目前临时最佳解更好的候选解时,才去考虑该物品不被包括在候选解中;反之,该物品不包括在当前候选解中的方案也不应继续考虑。对于任一值得继续考虑的方案,程序就去进一步考虑下一个物品。
【程序】
# include
# define N 100
double limitW;
int cop[N];
struct ele { double weight;
double value;
} a[N];
int k,n;
struct { int ;
double tw;
double tv;
}twv[N];
void next(int i,double tw,double tv)
{ twv.=1;
twv tw=tw;
twv tv=tv;
}
double find(struct ele *a,int n)
{ int i,k,f;
double maxv,tw,tv,totv;
maxv=0;
for (totv=0.0,k=0;k
totv+=a[k].value;
next(0,0.0,totv);
i=0;
While (i>=0)
{ f=twv.;
tw=twv tw;
tv=twv tv;
switch(f)
{ case 1: twv.++;
if (tw+a.weight<=limitW)
if (i
{ next(i+1,tw+a.weight,tv);
i++;
}
else
{ maxv=tv;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
case 0: i--;
break;
default: twv.=0;
if (tv-a.value>maxv)
if (i
{ next(i+1,tw,tv-a.value);
i++;
}
else
{ maxv=tv-a.value;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
}
}
return maxv;
}
void main()
{ double maxv;
printf(“输入物品种数\n”);
scanf((“%d”,&n);
printf(“输入限制重量\n”);
scanf(“%1f”,&limitW);
printf(“输入各物品的重量和价值\n”);
for (k=0;k
scanf(“%1f%1f”,&a[k].weight,&a[k].value);
maxv=find(a,n);
printf(“\n选中的物品为\n”);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“\n总价值为%.2f\n”,maxv);
}
⑺ 人工智能是学习什么
1、学习并掌握一些数学知识
高等数学是基础中的基础,一切理工科都需要这个打底,数据挖掘、人工智能、模式识别此类跟数据打交道的又尤其需要多元微积分运算基础。
线性代数很重要,一般来说线性模型是你最先要考虑的模型,加上很可能要处理多维数据,你需要用线性代数来简洁清晰的描述问题,为分析求解奠定基础。
概率论、数理统计、随机过程更是少不了,涉及数据的问题,不确定性几乎是不可避免的,引入随机变量顺理成章,相关理论、方法、模型非常丰富。很多机器学习的算法都是建立在概率论和统计学的基础上的,比如贝叶斯分类器、高斯隐马尔可夫链。
再就是优化理论与算法,除非你的问题是像二元一次方程求根那样有现成的公式,否则你将不得不面对各种看起来无解但是要解的问题,优化将是你的GPS为你指路。
以上这些知识打底,就可以开拔了,针对具体应用再补充相关的知识与理论,比如说一些我觉得有帮助的是数值计算、图论、拓扑,更理论一点的还有实/复分析、测度论,偏工程类一点的还有信号处理、数据结构。
2、掌握经典机器学习理论和算法
如果有时间可以为自己建立一个机器学习的知识图谱,并争取掌握每一个经典的机器学习理论和算法,我简单地总结如下:
1) 回归算法:常见的回归算法包括最小二乘法(OrdinaryLeast Square),逻辑回归(Logistic Regression),逐步式回归(Stepwise Regression),多元自适应回归样条(MultivariateAdaptive Regression Splines)以及本地散点平滑估计(Locally Estimated Scatterplot Smoothing);
2) 基于实例的算法:常见的算法包括 k-Nearest Neighbor(KNN), 学习矢量量化(Learning Vector Quantization, LVQ),以及自组织映射算法(Self-Organizing Map , SOM);
3) 基于正则化方法:常见的算法包括:Ridge Regression, Least Absolute Shrinkage and Selection Operator(LASSO),以及弹性网络(Elastic Net);
4) 决策树学习:常见的算法包括:分类及回归树(ClassificationAnd Regression Tree, CART), ID3 (Iterative Dichotomiser 3), C4.5, Chi-squared Automatic Interaction Detection(CHAID), Decision Stump, 随机森林(Random Forest), 多元自适应回归样条(MARS)以及梯度推进机(Gradient Boosting Machine, GBM);
5) 基于贝叶斯方法:常见算法包括:朴素贝叶斯算法,平均单依赖估计(AveragedOne-Dependence Estimators, AODE),以及Bayesian Belief Network(BBN);
6) 基于核的算法:常见的算法包括支持向量机(SupportVector Machine, SVM), 径向基函数(Radial Basis Function ,RBF), 以及线性判别分析(Linear Discriminate Analysis ,LDA)等;
7) 聚类算法:常见的聚类算法包括 k-Means算法以及期望最大化算法(Expectation Maximization, EM);
8) 基于关联规则学习:常见算法包括 Apriori算法和Eclat算法等;
9) 人工神经网络:重要的人工神经网络算法包括:感知器神经网络(PerceptronNeural Network), 反向传递(Back Propagation), Hopfield网络,自组织映射(Self-OrganizingMap, SOM)。学习矢量量化(Learning Vector Quantization, LVQ);
10) 深度学习:常见的深度学习算法包括:受限波尔兹曼机(RestrictedBoltzmann Machine, RBN), Deep Belief Networks(DBN),卷积网络(Convolutional Network), 堆栈式自动编码器(Stacked Auto-encoders);
11) 降低维度的算法:常见的算法包括主成份分析(PrincipleComponent Analysis, PCA),偏最小二乘回归(Partial Least Square Regression,PLS), Sammon映射,多维尺度(Multi-Dimensional Scaling, MDS), 投影追踪(ProjectionPursuit)等;
12) 集成算法:常见的算法包括:Boosting, Bootstrapped Aggregation(Bagging),AdaBoost,堆叠泛化(Stacked Generalization, Blending),梯度推进机(GradientBoosting Machine, GBM),随机森林(Random Forest)。
3、掌握一种编程工具,比如Python
一方面Python是脚本语言,简便,拿个记事本就能写,写完拿控制台就能跑;另外,Python非常高效,效率比java、r、matlab高。matlab虽然包也多,但是效率是这四个里面最低的。
4、了解行业最新动态和研究成果,比如各大牛的经典论文、博客、读书笔记、微博微信等媒体资讯。
5、买一个GPU,找一个开源框架,自己多动手训练深度神经网络,多动手写写代码,多做一些与人工智能相关的项目。
6、选择自己感兴趣或者工作相关的一个领域深入下去
人工智能有很多方向,比如NLP、语音识别、计算机视觉等等,生命有限,必须得选一个方向深入的钻研下去,这样才能成为人工智能领域的大牛,有所成就。
根据网络给的定义,人工智能(Artificial Intelligence),英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的还能的理论、方法、技术及应用系统的一门新的技术科学。
网络关于人工智能的定义详解中说道:人工智能是计算机的一个分支,二十世纪七十年代以来被称为世界三大尖端技术之一(空间技术、能源技术、人工智能)。也被认为是二十一世纪三大尖端技术(基因工程、纳米科学、人工智能)之一。这是因为近三十年来它获得了迅速的发展,在很多学科领域都获得了广泛应用,并取得了丰硕的成果,人工智能已逐步成为一个独立的分支,无论在理论和实践上都已自成一个系统。
综上,从定义上讲,人工智能是一项技术。
⑻ 二分法、一般迭代法、牛顿切线法、弦截法、高斯消元法、矩阵的三角分解法、矩阵求逆、
迭代是数值分析中通过从一个初始估计出发寻找一系列近似解来解决问题(一般是解方程或者方程组)的过程,为实现这一过程所使用的方法统称为迭代法(Iterative Method)。
一般可以做如下定义:对于给定的线性方程组x=Bx+f(这里的x、B、f同为矩阵,任意线性方程组都可以变换成此形式),用公式x(k+1)=Bx(k)+f(括号中为上标,代表迭代k次得到的x,初始时k=0)逐步带入求近似解的方法称为迭代法(或称一阶定常迭代法)。如果k趋向无穷大时limx(k)存在,记为x*,称此迭代法收敛。显然x*就是此方程组的解,否则称为迭代法发散。
跟迭代法相对应的是直接法(或者称为一次解法),即一次性的快速解决问题,例如通过开方解决方程x +3= 4。一般如果可能,直接解法总是优先考虑的。但当遇到复杂问题时,特别是在未知量很多,方程为非线性时,我们无法找到直接解法(例如五次以及更高次的代数方程没有解析解,参见阿贝耳定理),这时候或许可以通过迭代法寻求方程(组)的近似解。
最常见的迭代法是牛顿法。其他还包括最速下降法、共轭迭代法、变尺度迭代法、最小二乘法、线性规划、非线性规划、单纯型法、惩罚函数法、斜率投影法、遗传算法、模拟退火等等。
利用迭代算法解决问题,需要做好以下三个方面的工作:
确定迭代变量
在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。
建立迭代关系式
所谓迭代关系式,指如何从变量的前一个值推出其下一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以顺推或倒推的方法来完成。
对迭代过程进行控制
在什么时候结束迭代过程?这是编写迭代程序必须考虑的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为两种情况:一种是所需的迭代次数是个确定的值,可以计算出来;另一种是所需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程的条件。
举例
例 1 :一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,每月新生一只兔子,新生的兔子也如此繁殖。如果所有的兔子都不死去,问到第 12 个月时,该饲养场共有兔子多少只?
分析:这是一个典型的递推问题。我们不妨假设第 1 个月时兔子的只数为 u 1 ,第 2 个月时兔子的只数为 u 2 ,第 3 个月时兔子的只数为 u 3 ,……根据题意,“这种兔子从出生的下一个月开始,每月新生一只兔子”,则有
u 1 = 1 , u 2 = u 1 + u 1 × 1 = 2 , u 3 = u 2 + u 2 × 1 = 4 ,……
根据这个规律,可以归纳出下面的递推公式:
u n = u(n - 1)× 2 (n ≥ 2)
对应 u n 和 u(n - 1),定义两个迭代变量 y 和 x ,可将上面的递推公式转换成如下迭代关系:
y=x*2
x=y
让计算机对这个迭代关系重复执行 11 次,就可以算出第 12 个月时的兔子数。参考程序如下:
cls
x=1
for i=2 to 12
y=x*2
x=y
next i
print y
end
例 2 :阿米巴用简单分裂的方式繁殖,它每分裂一次要用 3 分钟。将若干个阿米巴放在一个盛满营养参液的容器内, 45 分钟后容器内充满了阿米巴。已知容器最多可以装阿米巴 220,220个。试问,开始的时候往容器内放了多少个阿米巴?请编程序算出。
分析:根据题意,阿米巴每 3 分钟分裂一次,那么从开始的时候将阿米巴放入容器里面,到 45 分钟后充满容器,需要分裂 45/3=15 次。而“容器最多可以装阿米巴2^ 20 个”,即阿米巴分裂 15 次以后得到的个数是 2^20。题目要求我们计算分裂之前的阿米巴数,不妨使用倒推的方法,从第 15 次分裂之后的 2^20 个,倒推出第 15 次分裂之前(即第 14 次分裂之后)的个数,再进一步倒推出第 13 次分裂之后、第 12 次分裂之后、……第 1 次分裂之前的个数。
设第 1 次分裂之前的个数为 x 0 、第 1 次分裂之后的个数为 x 1 、第 2 次分裂之后的个数为 x 2 、……第 15 次分裂之后的个数为 x 15 ,则有
x 14 =x 15 /2 、 x 13 =x 14 /2 、…… x n-1 =x n /2 (n ≥ 1)
因为第 15 次分裂之后的个数 x 15 是已知的,如果定义迭代变量为 x ,则可以将上面的倒推公式转换成如下的迭代公式:
x=x/2 (x 的初值为第 15 次分裂之后的个数 2^20)
让这个迭代公式重复执行 15 次,就可以倒推出第 1 次分裂之前的阿米巴个数。因为所需的迭代次数是个确定的值,我们可以使用一个固定次数的循环来实现对迭代过程的控制。参考程序如下:
cls
x=2^20
for i=1 to 15
x=x/2
next i
print x
end
ps:java中幂的算法是Math.pow(2,20);返回double,稍微注意一下
例 3 :验证谷角猜想。日本数学家谷角静夫在研究自然数时发现了一个奇怪现象:对于任意一个自然数 n ,若 n 为偶数,则将其除以 2 ;若 n 为奇数,则将其乘以 3 ,然后再加 1。如此经过有限次运算后,总可以得到自然数 1。人们把谷角静夫的这一发现叫做“谷角猜想”。
要求:编写一个程序,由键盘输入一个自然数 n ,把 n 经过有限次运算后,最终变成自然数 1 的全过程打印出来。
分析:定义迭代变量为 n ,按照谷角猜想的内容,可以得到两种情况下的迭代关系式:当 n 为偶数时, n=n/2 ;当 n 为奇数时, n=n*3+1。用 QBASIC 语言把它描述出来就是:
if n 为偶数 then
n=n/2
else
n=n*3+1
end if
这就是需要计算机重复执行的迭代过程。这个迭代过程需要重复执行多少次,才能使迭代变量 n 最终变成自然数 1 ,这是我们无法计算出来的。因此,还需进一步确定用来结束迭代过程的条件。仔细分析题目要求,不难看出,对任意给定的一个自然数 n ,只要经过有限次运算后,能够得到自然数 1 ,就已经完成了验证工作。因此,用来结束迭代过程的条件可以定义为:n=1。参考程序如下:
cls
input "Please input n=";n
do until n=1
if n mod 2=0 then
rem 如果 n 为偶数,则调用迭代公式 n=n/2
n=n/2
print "—";n;
else
n=n*3+1
print "—";n;
end if
loop
end
迭代法开平方:
#include<stdio.h>
#include<math.h>
void main()
{
double a,x0,x1;
printf("Input a:\n");
scanf("%lf",&a);//为什么在VC6.0中不能写成“scanf("%f",&a);”?
if(a<0)
printf("Error!\n");
else
{
x0=a/2;
x1=(x0+a/x0)/2;
do
{
x0=x1;
x1=(x0+a/x0)/2;
}while(fabs(x0-x1)>=1e-6);
}
printf("Result:\n");
printf("sqrt(%g)=%g\n",a,x1);
}
求平方根的迭代公式:x1=1/2*(x0+a/x0)。
算法:1.先自定一个初值x0,作为a的平方根值,在我们的程序中取a/2作为a的初值;利用迭代公式求出一个x1。此值与真正的a的平方根值相比,误差很大。
⒉把新求得的x1代入x0中,准备用此新的x0再去求出一个新的x1.
⒊利用迭代公式再求出一个新的x1的值,也就是用新的x0又求出一个新的平方根值x1,此值将更趋近于真正的平方根值。
⒋比较前后两次求得的平方根值x0和x1,如果它们的差值小于我们指定的值,即达到我们要求的精度,则认为x1就是a的平方根值,去执行步骤5;否则执行步骤2,即循环进行迭代。
迭代法是用于求方程或方程组近似根的一种常用的算法设计方法。设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行:
⑴ 选一个方程的近似根,赋给变量x0;
⑵ 将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0;
⑶ 当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤⑵的计算。
若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为:
【算法】迭代法求方程的根
{ x0=初始近似根;
do {
x1=x0;
x0=g(x1); /*按特定的方程计算新的近似根*/
} while (fabs(x0-x1)>Epsilon);
printf(“方程的近似根是%f\n”,x0);
}
迭代算法也常用于求方程组的根,令
X=(x0,x1,…,xn-1)
设方程组为:
xi=gi(X) (I=0,1,…,n-1)
则求方程组根的迭代算法可描述如下:
【算法】迭代法求方程组的根
{ for (i=0;i
x=初始近似根;
do {
for (i=0;i
y=x;
for (i=0;i
x=gi(X);
for (delta=0.0,i=0;i
if (fabs(y-x)>delta) delta=fabs(y-x);
} while (delta>Epsilon);
for (i=0;i
printf(“变量x[%d]的近似根是 %f”,I,x);
printf(“\n”);
}
具体使用迭代法求根时应注意以下两种可能发生的情况:
⑴ 如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制;
⑵ 方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。
递归
递归是设计和描述算法的一种有力的工具,由于它在复杂算法的描述中被经常采用,为此在进一步介绍其他算法设计方法之前先讨论它。
能采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。
【问题】 编写计算斐波那契(Fibonacci)数列的第n项函数fib(n)。
斐波那契数列为:0、1、1、2、3、……,即:
fib(0)=0;
fib⑴=1;
fib(n)=fib(n-1)+fib(n-2) (当n>1时)。
写成递归函数有:
int fib(int n)
{ if (n==0) return 0;
if (n==1) return 1;
if (n>1) return fib(n-1)+fib(n-2);
}
递归算法的执行过程分递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解。例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。也就是说,为计算fib(n),必须先计算fib(n-1)和fib(n- 2),而计算fib(n-1)和fib(n-2),又必须先计算fib(n-3)和fib(n-4)。依次类推,直至计算fib⑴和fib(0),分别能立即得到结果1和0。在递推阶段,必须要有终止递归的情况。例如在函数fib中,当n为1和0的情况。
在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到fib⑴和fib(0)后,返回得到fib⑵的结果,……,在得到了fib(n-1)和fib(n-2)的结果后,返回得到fib(n)的结果。
在编写递归函数时要注意,函数中的局部变量和参数知识局限于当前调用层,当递推进入“简单问题”层时,原来层次上的参数和局部变量便被隐蔽起来。在一系列“简单问题”层,它们各有自己的参数和局部变量。
由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。当某个递归算法能较方便地转换成递推算法时,通常按递推算法编写程序。例如上例计算斐波那契数列的第n项的函数fib(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项。
【问题】 组合问题
问题描述:找出从自然数1、2、……、n中任取r个数的所有组合。例如n=5,r=3的所有组合为:⑴5、4、3 ⑵5、4、2 ⑶5、4、1
⑷5、3、2 ⑸5、3、1 ⑹5、2、1
⑺4、3、2 ⑻4、3、1 ⑼4、2、1
⑽3、2、1
分析所列的10个组合,可以采用这样的递归思想来考虑求组合函数的算法。设函数为void comb(int m,int k)为找出从自然数1、2、……、m中任取k个数的所有组合。当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。这就将求m 个数中取k个数的组合问题转化成求m-1个数中取k-1个数的组合问题。设函数引入工作数组a[ ]存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放在a[k]中,当一个组合求出后,才将a[ ]中的一个组合输出。第一个数可以是m、m-1、……、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组合的其余元素,继续递归去确定;或因已确定了组合的全部元素,输出这个组合。细节见以下程序中的函数comb。
【程序】
# include
# define MAXN 100
int a[MAXN];
void comb(int m,int k)
{ int i,j;
for (i=m;i>=k;i--)
{ a[k]=i;
if (k>1)
comb(i-1,k-1);
else
{ for (j=a[0];j>0;j--)
printf(“%4d”,a[j]);
printf(“\n”);
}
}
}
void main()
{ a[0]=3;
comb(5,3);
}
【问题】 背包问题
问题描述:有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。
设n 件物品的重量分别为w0、w1、…、wn-1,物品的价值分别为v0、v1、…、vn-1。采用递归寻找物品的选择方案。设前面已有了多种选择的方案,并保留了其中总价值最大的方案于数组option[ ],该方案的总价值存于变量maxv。当前正在考察新方案,其物品选择情况保存于数组cop[ ]。假定当前方案已考虑了前i-1件物品,现在要考虑第i件物品;当前方案已包含的物品的重量之和为tw;至此,若其余物品都选择是可能的话,本方案能达到的总价值的期望值为tv。算法引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值maxv时,继续考察当前方案变成无意义的工作,应终止当前方案,立即去考察下一个方案。因为当方案的总价值不比maxv大时,该方案不会被再考察,这同时保证函数后找到的方案一定会比前面的方案更好。
对于第i件物品的选择考虑有两种可能:
⑴ 考虑物品i被选择,这种可能性仅当包含它不会超过方案总重量限制时才是可行的。选中后,继续递归去考虑其余物品的选择。
⑵ 考虑物品i不被选择,这种可能性仅当不包含物品i也有可能会找到价值更大的方案的情况。
按以上思想写出递归算法如下:
try(物品i,当前选择已达到的重量和,本方案可能达到的总价值tv)
{ /*考虑物品i包含在当前方案中的可能性*/
if(包含物品i是可以接受的)
{ 将物品i包含在当前方案中;
if (i
try(i+1,tw+物品i的重量,tv);
else
/*又一个完整方案,因为它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
恢复物品i不包含状态;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (不包含物品i仅是可男考虑的)
if (i
try(i+1,tw,tv-物品i的价值);
else
/*又一个完整方案,因它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
}
为了理解上述算法,特举以下实例。设有4件物品,它们的重量和价值见表:
物品 0 1 2 3
重量 5 3 2 1
价值 4 4 3 1
并设限制重量为7。则按以上算法,下图表示找解过程。由图知,一旦找到一个解,算法就进一步找更好的佳。如能判定某个查找分支不会找到更好的解,算法不会在该分支继续查找,而是立即终止该分支,并去考察下一个分支。
按上述算法编写函数和程序如下:
【程序】
# include
# define N 100
double limitW,totV,maxV;
int option[N],cop[N];
struct { double weight;
double value;
}a[N];
int n;
void find(int i,double tw,double tv)
{ int k;
/*考虑物品i包含在当前方案中的可能性*/
if (tw+a.weight<=limitW)
{ cop=1;
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv;
}
cop=0;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (tv-a.value>maxV)
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv-a.value;
}
}
void main()
{ int k;
double w,v;
printf(“输入物品种数\n”);
scanf((“%d”,&n);
printf(“输入各物品的重量和价值\n”);
for (totv=0.0,k=0;k
{ scanf(“%1f%1f”,&w,&v);
a[k].weight=w;
a[k].value=v;
totV+=V;
}
printf(“输入限制重量\n”);
scanf(“%1f”,&limitV);
maxv=0.0;
for (k=0;k find(0,0.0,totV);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“\n总价值为%.2f\n”,maxv);
}
作为对比,下面以同样的解题思想,考虑非递归的程序解。为了提高找解速度,程序不是简单地逐一生成所有候选解,而是从每个物品对候选解的影响来形成值得进一步考虑的候选解,一个候选解是通过依次考察每个物品形成的。对物品i的考察有这样几种情况:当该物品被包含在候选解中依旧满足解的总重量的限制,该物品被包含在候选解中是应该继续考虑的;反之,该物品不应该包括在当前正在形成的候选解中。同样地,仅当物品不被包括在候选解中,还是有可能找到比目前临时最佳解更好的候选解时,才去考虑该物品不被包括在候选解中;反之,该物品不包括在当前候选解中的方案也不应继续考虑。对于任一值得继续考虑的方案,程序就去进一步考虑下一个物品。
【程序】
# include
# define N 100
double limitW;
int cop[N];
struct ele { double weight;
double value;
} a[N];
int k,n;
struct { int ;
double tw;
double tv;
}twv[N];
void next(int i,double tw,double tv)
{ twv.=1;
twv tw=tw;
twv tv=tv;
}
double find(struct ele *a,int n)
{ int i,k,f;
double maxv,tw,tv,totv;
maxv=0;
for (totv=0.0,k=0;k
totv+=a[k].value;
next(0,0.0,totv);
i=0;
While (i>=0)
{ f=twv.;
tw=twv tw;
tv=twv tv;
switch(f)
{ case 1: twv.++;
if (tw+a.weight<=limitW)
if (i
{ next(i+1,tw+a.weight,tv);
i++;
}
else
{ maxv=tv;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
case 0: i--;
break;
default: twv.=0;
if (tv-a.value>maxv)
if (i
{ next(i+1,tw,tv-a.value);
i++;
}
else
{ maxv=tv-a.value;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
}
}
return maxv;
}
void main()
{ double maxv;
printf(“输入物品种数\n”);
scanf((“%d”,&n);
printf(“输入限制重量\n”);
scanf(“%1f”,&limitW);
printf(“输入各物品的重量和价值\n”);
for (k=0;k
scanf(“%1f%1f”,&a[k].weight,&a[k].value);
maxv=find(a,n);
printf(“\n选中的物品为\n”);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“\n总价值为%.2f\n”,maxv);
}
编辑本段递归的基本概念和特点
程序调用自身的编程技巧称为递归(recursion)。
一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。
一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
注意:
⑴ 递归就是在过程或函数里调用自身;
⑵ 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。
⑼ 粒子群优化算法和多模态优化算法有什么区别
摘 要:,粒子群算法据自己的速度来决定搜索过程,只有最优的粒子把信息给予其他的粒子,整个搜索更新过程是跟随当前最优解的过程,所有的粒子还可以更快的收敛于最优解。由于微粒群算法简单,容易实现,与其它求解约束优化问题的方法相比较,具有一定的优势。实验结果表明,对于无约束的非线性求解,粒子群算法表现出较好的收敛性和健壮性。
关键词:粒子群算法;函数优化;极值寻优
0 引言
非线性方程的求根问题是多年来数学家努力解决的问题之一。长期以来,人们已找出多种用于解决方程求根的方法,例如牛顿法、弦割法、抛物线法等。然而,很多传统的方法仅能运用于相应的小的问题集,推广性相对较差。对于一个现实世界中的优化问题,必须尝试很多不同的方法,甚至要发明相应的新的方法来解决,这显然是不现实的。我们需要另外的方法来克服这样的困难。
粒子群算法是一种现代启发式算法,具有推广性强、鲁棒性高等特点[1]。该算法具有群体智能、内在并行性、迭代格式简单、可快速收敛到最优解所在区域等优点[2]。本文采用粒子群算法,对函数的极值进行寻优计算,实现了对函数的极值求解。
1 粒子群算法
1.1 基本原理
粒子群算法(PSO)是一种基于群体的随机优化技术,它的思想来源于对鸟群捕食行为的研究与模拟。粒子群算法与其它基于群体的进化算法相类似,选用“群体”和“进化”的概念,按照个体的适应度值进行操作,也是一种基于迭代的寻优技术。区别在于,粒子群算法中没有交叉变异等进化算子,而是将每个个体看作搜索空间中的微粒,每个微粒没有重量和体积,但都有自己的位置向量、速度向量和适应度值。所有微粒以一定的速度飞行于搜索空间中,其中的飞行速度是由个体飞行经验和群体的飞行经验动态调整,通过追踪当前搜索到的最优值来寻找全局最优值。
1.2 参数选择
粒子群算法需要修改的参数很少,但对参数的选择却十分敏感。El-Gallad A, El-Hawary M, Sallam A, Kalas A[3]主要对算法中的种群规模、迭代次数和粒子速度的选择方法进行了详细分析,利用统计方法对约束优化问题的求解论证了这 3 个参数对算法性能的影响,并给出了具有一定通用性的3 个参数选择原则[4]。
种群规模:通常根据待优化问题的复杂程度确定。
最大速度:决定粒子在一次迭代中的最大移动距离,通常设定为不超过粒子的范围宽度。
加速常数:加速常数c1和c2通常是由经验值决定的,它代表粒子向pbest和gbest靠拢的加速项的权重。一般取值为:c1=c2=2。
中止条件:达到最大迭代次数或得到最小误差要求,通常要由具体问题确定。
惯性权重:惯性权重能够针对待优化问题调整算法的局部和全局搜索能力。当该值较大时有利于全局搜索,较小时有利于局部搜索。所以通常在算法开始时设置较大的惯性权重,以便扩大搜索范围、加快收敛。而随着迭代次数的增加逐渐减小惯性权重的值,使其进行精确搜索,避免跳过最优解。
1.3 算法步骤
PSO算法步骤如下:
Step1:初始化一个规模为 m 的粒子群,设定初始位置和速度。
初始化过程如下:
(1)设定群体规模m;
(2)对任意的i,s,在[-xmax, xmax]内均匀分布,产生初始位置xis;
(3)对任意的i,s,在[-vmax, vmax]内均匀分布,产生速度vis;
(4)对任意的i,设yi=xi,保存个体。
Step2:计算每个粒子的适应度值。
Step3:对每个粒子的适应度值和得到过的最好位置pis的适应度值进行比较,若相对较好,则将其作为当前的最好位置。
Step4:对每个粒子的适应度值和全局得到过的最好位置pgs的适应度值进行比较,若相对较好,则将其作为当前的全局最好位置。
Step5:分别对粒子的所在位置和速度进行更新。
Step6:如果满足终止条件,则输出最优解;否则,返回Step2。
1.4 粒子群算法函数极值求解
粒子群算法优化是计算机智能领域,除蚁群算法外的另一种基于群体智能的优化算法。粒子群算法是一种群体智能的烟花计算技术。与遗传算法相比,粒子群算法没有遗传算法的选择(Selection)、交叉(Crossover)、变异(Mutation)等操作,而是通过粒子在解空间追随最优的粒子进行搜索。
粒子群算法流程如图所示:
粒子群为由n个粒子组成的种群X = (X1,X2,X3,…Xn).
第i个粒子表示一个D维向量Xi = (X1,X2,X3,…XD)T.
第i个粒子的速度为Vi = (Vi1,Vi2,Vi3,…ViD)T.
个体极值为Pi = (Pi1,Pi2,Pi3,…PiD)T.
全局极值为Pg = (Pg1,Pg2,Pg3,…PgD)T.
速度更新为,式中,c1和c2为其两个学习因子的参数值;r1和r2为其两个随机值。
位置更新为.
2 粒子群算法应用举例
2.1 实验问题
这是一个无约束函数的极值寻优,对于Ackley函数,
.
其中c1=20,e=2. 71289。
2.2 实验步骤
对于Ackley函数图形,选取一个凹峰进行分析,程序运行结果如图所示。
图1 Ackley函数图形
可以看出,选取区间内的Ackley函数图形只有一个极小值点。因此,对于该段函数进行寻优,不会陷入局部最小。采用粒子群算法对该函数进行极值寻优。
首先,进行初始化粒子群,编写的MATLAB代码如下:
% 初始化种群
for i=1:sizepop
x1 = popmin1 (popmax1-popmin1)*rand;
% 产生随机个体
x2 = popmin2 (popmax2-popmin2)*rand;
pop(i,1) = x1; % 保存产生的随机个体
pop(i,2) = x2;
fitness(i) = fun([x1,x2]); % 适应度值
V(i,1) = 0; % 初始化粒子速度
V(i,2) = 0;
end
程序运行后所产生的个体值为:
表1 函数个体值
然后,根据待寻优的目标函数,计算适应度值。待寻优的目标函数为:
function y = fun(x)
y=-20*exp(-0.2*sqrt((x(1)^2x(2)^2)/2))-exp((cos(2*pi*x(1)) cos(2*pi*x(2)))/2) 20 2.71289;
根据每一组个体,通过目标函数,得到的适应度值为:
表2 函数适应度值
搜索个体最优极值,即搜索最小的适应度值,我们可利用MATLAB绘图将所有个体的适应度值绘成plot图查看相对最小值。
图3 函数适应度plot图
从图中可看出,当个体=20时,得到相对最小值,在程序中,将其保存下来。
之后进行迭代寻优,直到满足终止条件。
最后,得到的最优值为:
图4 MATLAB运行得到结果
迭代后得到的运行结果图如下:
图5 迭代曲线图
2.3 实验结果
通过图5中可看出,该函数的寻优是收敛的,最优个体和实际情况较吻合。因此,采用粒子群算法进行函数极值寻优,快速、准确且鲁棒性较好。
3 结论
本文阐述了粒子群算法求解最化问题的过程,实验结果表明了该算法对于无约束问题的可行性。与其它的进化算法相比,粒子群算法容易理解、编码简单、容易实现。但是参数的设置对于该算法的性能却有很大的影响,例如控制收敛,避免早熟等。在未来的工作中,将努力于将其它计算智能算法或其它优化技术应用于粒子群算法中,以进一步提高粒子群算法的性能。
⑽ 在C语言中,什么是迭代法
迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程,跟迭代法相对应的是直接法,即一次性解决问题。迭代法又分为精确迭代和近似迭代。“二分法”和“牛顿迭代法”属于近似迭代法。迭代算法是用计算机解决问题的一种基本方法。它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。
迭代是数值分析中通过从一个初始估计出发寻找一系列近似解来解决问题(一般是解方程或者方程组)的过程,为实现这一过程所使用的方法统称为迭代法(Iterative Method)。
一般可以做如下定义:对于给定的线性方程组x=Bx+f(这里的x、B、f同为矩阵,任意线性方程组都可以变换成此形式),用公式x(k+1)=Bx(k)+f(括号中为上标,代表迭代k次得到的x,初始时k=0)逐步带入求近似解的方法称为迭代法(或称一阶定常迭代法)。如果k趋向无穷大时limx(k)存在,记为x*,称此迭代法收敛。显然x*就是此方程组的解,否则称为迭代法发散。
跟迭代法相对应的是直接法(或者称为一次解法),即一次性的快速解决问题,例如通过开方解决方程x +3= 4。一般如果可能,直接解法总是优先考虑的。但当遇到复杂问题时,特别是在未知量很多,方程为非线性时,我们无法找到直接解法(例如五次以及更高次的代数方程没有解析解,参见阿贝耳定理),这时候或许可以通过迭代法寻求方程(组)的近似解。
最常见的迭代法是牛顿法。其他还包括最速下降法、共轭迭代法、变尺度迭代法、最小二乘法、线性规划、非线性规划、单纯型法、惩罚函数法、斜率投影法、遗传算法、模拟退火等等。
利用迭代算法解决问题,需要做好以下三个方面的工作:
确定迭代变量
在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。
建立迭代关系式
所谓迭代关系式,指如何从变量的前一个值推出其下一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以顺推或倒推的方法来完成。
对迭代过程进行控制
在
什么时候结束迭代过程?这是编写迭代程序必须考虑的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为两种情况:一种是所需的迭代次数
是个确定的值,可以计算出来;另一种是所需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需
要进一步分析出用来结束迭代过程的条件。
举例
例 1 :一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,每月新生一只兔子,新生的兔子也如此繁殖。如果所有的兔子都不死去,问到第 12 个月时,该饲养场共有兔子多少只?
分析:这是一个典型的递推问题。我们不妨假设第 1 个月时兔子的只数为 u 1 ,第 2 个月时兔子的只数为 u 2 ,第 3 个月时兔子的只数为 u 3 ,……根据题意,“这种兔子从出生的下一个月开始,每月新生一只兔子”,则有
u 1 = 1 , u 2 = u 1 + u 1 × 1 = 2 , u 3 = u 2 + u 2 × 1 = 4 ,……
根据这个规律,可以归纳出下面的递推公式:
u n = u(n - 1)× 2 (n ≥ 2)
对应 u n 和 u(n - 1),定义两个迭代变量 y 和 x ,可将上面的递推公式转换成如下迭代关系:
y=x*2
x=y
让计算机对这个迭代关系重复执行 11 次,就可以算出第 12 个月时的兔子数。参考程序如下:
cls
x=1
for i=2 to 12
y=x*2
x=y
next i
print y
end
例 2 :阿米巴用简单分裂的方式繁殖,它每分裂一次要用 3 分钟。将若干个阿米巴放在一个盛满营养参液的容器内, 45 分钟后容器内充满了阿米巴。已知容器最多可以装阿米巴 220,220个。试问,开始的时候往容器内放了多少个阿米巴?请编程序算出。
分析:根据题意,阿米巴每 3 分钟分裂一次,那么从开始的时候将阿米巴放入容器里面,到 45
分钟后充满容器,需要分裂 45/3=15 次。而“容器最多可以装阿米巴2^ 20 个”,即阿米巴分裂 15 次以后得到的个数是
2^20。题目要求我们计算分裂之前的阿米巴数,不妨使用倒推的方法,从第 15 次分裂之后的 2^20 个,倒推出第 15 次分裂之前(即第 14
次分裂之后)的个数,再进一步倒推出第 13 次分裂之后、第 12 次分裂之后、……第 1 次分裂之前的个数。
设第 1 次分裂之前的个数为 x 0 、第 1 次分裂之后的个数为 x 1 、第 2 次分裂之后的个数为 x 2 、……第 15 次分裂之后的个数为 x 15 ,则有
x 14 =x 15 /2 、 x 13 =x 14 /2 、…… x n-1 =x n /2 (n ≥ 1)
因为第 15 次分裂之后的个数 x 15 是已知的,如果定义迭代变量为 x ,则可以将上面的倒推公式转换成如下的迭代公式:
x=x/2 (x 的初值为第 15 次分裂之后的个数 2^20)
让这个迭代公式重复执行 15 次,就可以倒推出第 1 次分裂之前的阿米巴个数。因为所需的迭代次数是个确定的值,我们可以使用一个固定次数的循环来实现对迭代过程的控制。参考程序如下:
cls
x=2^20
for i=1 to 15
x=x/2
next i
print x
end
ps:java中幂的算法是Math.pow(2,20);返回double,稍微注意一下
例 3 :验证谷角猜想。日本数学家谷角静夫在研究自然数时发现了一个奇怪现象:对于任意一个自然数 n ,若 n 为偶数,则将其除以 2 ;若 n 为奇数,则将其乘以 3 ,然后再加 1。如此经过有限次运算后,总可以得到自然数 1。人们把谷角静夫的这一发现叫做“谷角猜想”。
要求:编写一个程序,由键盘输入一个自然数 n ,把 n 经过有限次运算后,最终变成自然数 1 的全过程打印出来。
分析:定义迭代变量为 n ,按照谷角猜想的内容,可以得到两种情况下的迭代关系式:当 n 为偶数时, n=n/2 ;当 n 为奇数时, n=n*3+1。用 QBASIC 语言把它描述出来就是:
if n 为偶数 then
n=n/2
else
n=n*3+1
end if
这就是需要计算机重复执行的迭代过程。这个迭代过程需要重复执行多少次,才能使迭代变量 n 最终变成自然数 1
,这是我们无法计算出来的。因此,还需进一步确定用来结束迭代过程的条件。仔细分析题目要求,不难看出,对任意给定的一个自然数 n
,只要经过有限次运算后,能够得到自然数 1 ,就已经完成了验证工作。因此,用来结束迭代过程的条件可以定义为:n=1。参考程序如下:
cls
input "Please input n=";n
do until n=1
if n mod 2=0 then
rem 如果 n 为偶数,则调用迭代公式 n=n/2
n=n/2
print "—";n;
else
n=n*3+1
print "—";n;
end if
loop
end
迭代法开平方:
#include<stdio.h>
#include<math.h>
void main()
{
double a,x0,x1;
printf("Input a:\n");
scanf("%lf",&a);//为什么在VC6.0中不能写成“scanf("%f",&a);”?
if(a<0)
printf("Error!\n");
else
{
x0=a/2;
x1=(x0+a/x0)/2;
do
{
x0=x1;
x1=(x0+a/x0)/2;
}while(fabs(x0-x1)>=1e-6);
}
printf("Result:\n");
printf("sqrt(%g)=%g\n",a,x1);
}
求平方根的迭代公式:x1=1/2*(x0+a/x0)。
算法:1.先自定一个初值x0,作为a的平方根值,在我们的程序中取a/2作为a的初值;利用迭代公式求出一个x1。此值与真正的a的平方根值相比,误差很大。
⒉把新求得的x1代入x0中,准备用此新的x0再去求出一个新的x1.
⒊利用迭代公式再求出一个新的x1的值,也就是用新的x0又求出一个新的平方根值x1,此值将更趋近于真正的平方根值。
⒋比较前后两次求得的平方根值x0和x1,如果它们的差值小于我们指定的值,即达到我们要求的精度,则认为x1就是a的平方根值,去执行步骤5;否则执行步骤2,即循环进行迭代。
迭代法是用于求方程或方程组近似根的一种常用的算法设计方法。设方程为f(x)=0,用某种数学方法导出等价的形式x=g(x),然后按以下步骤执行:
⑴ 选一个方程的近似根,赋给变量x0;
⑵ 将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0;
⑶ 当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤⑵的计算。
若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为:
【算法】迭代法求方程的根
{ x0=初始近似根;
do {
x1=x0;
x0=g(x1); /*按特定的方程计算新的近似根*/
} while (fabs(x0-x1)>Epsilon);
printf(“方程的近似根是%f\n”,x0);
}
迭代算法也常用于求方程组的根,令
X=(x0,x1,…,xn-1)
设方程组为:
xi=gi(X) (I=0,1,…,n-1)
则求方程组根的迭代算法可描述如下:
【算法】迭代法求方程组的根
{ for (i=0;i
x=初始近似根;
do {
for (i=0;i
y=x;
for (i=0;i
x=gi(X);
for (delta=0.0,i=0;i
if (fabs(y-x)>delta) delta=fabs(y-x);
} while (delta>Epsilon);
for (i=0;i
printf(“变量x[%d]的近似根是 %f”,I,x);
printf(“\n”);
}
具体使用迭代法求根时应注意以下两种可能发生的情况:
⑴ 如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制;
⑵ 方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。
递归
递归是设计和描述算法的一种有力的工具,由于它在复杂算法的描述中被经常采用,为此在进一步介绍其他算法设计方法之前先讨论它。
能采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。
【问题】 编写计算斐波那契(Fibonacci)数列的第n项函数fib(n)。
斐波那契数列为:0、1、1、2、3、……,即:
fib(0)=0;
fib⑴=1;
fib(n)=fib(n-1)+fib(n-2) (当n>1时)。
写成递归函数有:
int fib(int n)
{ if (n==0) return 0;
if (n==1) return 1;
if (n>1) return fib(n-1)+fib(n-2);
}
递归算法的执行过程分递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问
题(规模小于n)的求解。例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。也就是说,为计算fib(n),必须先计算
fib(n-1)和fib(n-
2),而计算fib(n-1)和fib(n-2),又必须先计算fib(n-3)和fib(n-4)。依次类推,直至计算fib⑴和fib(0),分别能
立即得到结果1和0。在递推阶段,必须要有终止递归的情况。例如在函数fib中,当n为1和0的情况。
在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到fib⑴和fib(0)后,返回得到fib⑵的结果,……,在得到了fib(n-1)和fib(n-2)的结果后,返回得到fib(n)的结果。
在编写递归函数时要注意,函数中的局部变量和参数知识局限于当前调用层,当递推进入“简单问题”层时,原来层次上的参数和局部变量便被隐蔽起来。在一系列“简单问题”层,它们各有自己的参数和局部变量。
由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。当某个递归算法能较方便地转换成递推算法时,通常按递推算法编写程序。例如上例计算斐波那契数列的第n项的函数fib(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项。
【问题】 组合问题
问题描述:找出从自然数1、2、……、n中任取r个数的所有组合。例如n=5,r=3的所有组合为:⑴5、4、3 ⑵5、4、2 ⑶5、4、1
⑷5、3、2 ⑸5、3、1 ⑹5、2、1
⑺4、3、2 ⑻4、3、1 ⑼4、2、1
⑽3、2、1
分析所列的10个组合,可以采用这样的递归思想来考虑求组合函数的算法。设函数为void comb(int
m,int
k)为找出从自然数1、2、……、m中任取k个数的所有组合。当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。这就将求m
个数中取k个数的组合问题转化成求m-1个数中取k-1个数的组合问题。设函数引入工作数组a[
]存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放在a[k]中,当一个组合求出后,才将a[
]中的一个组合输出。第一个数可以是m、m-1、……、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组合的其余元素,继续递
归去确定;或因已确定了组合的全部元素,输出这个组合。细节见以下程序中的函数comb。
【程序】
# include
# define MAXN 100
int a[MAXN];
void comb(int m,int k)
{ int i,j;
for (i=m;i>=k;i--)
{ a[k]=i;
if (k>1)
comb(i-1,k-1);
else
{ for (j=a[0];j>0;j--)
printf(“%4d”,a[j]);
printf(“\n”);
}
}
}
void main()
{ a[0]=3;
comb(5,3);
}
【问题】 背包问题
问题描述:有不同价值、不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,使选中物品的总重量不超过指定的限制重量,但选中物品的价值之和最大。
设n
件物品的重量分别为w0、w1、…、wn-1,物品的价值分别为v0、v1、…、vn-1。采用递归寻找物品的选择方案。设前面已有了多种选择的方案,并
保留了其中总价值最大的方案于数组option[ ],该方案的总价值存于变量maxv。当前正在考察新方案,其物品选择情况保存于数组cop[
]。假定当前方案已考虑了前i-1件物品,现在要考虑第i件物品;当前方案已包含的物品的重量之和为tw;至此,若其余物品都选择是可能的话,本方案能达
到的总价值的期望值为tv。算法引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值maxv时,继续考察当前方案变成无意义的工作,应终止
当前方案,立即去考察下一个方案。因为当方案的总价值不比maxv大时,该方案不会被再考察,这同时保证函数后找到的方案一定会比前面的方案更好。
对于第i件物品的选择考虑有两种可能:
⑴ 考虑物品i被选择,这种可能性仅当包含它不会超过方案总重量限制时才是可行的。选中后,继续递归去考虑其余物品的选择。
⑵ 考虑物品i不被选择,这种可能性仅当不包含物品i也有可能会找到价值更大的方案的情况。
按以上思想写出递归算法如下:
try(物品i,当前选择已达到的重量和,本方案可能达到的总价值tv)
{ /*考虑物品i包含在当前方案中的可能性*/
if(包含物品i是可以接受的)
{ 将物品i包含在当前方案中;
if (i
try(i+1,tw+物品i的重量,tv);
else
/*又一个完整方案,因为它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
恢复物品i不包含状态;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (不包含物品i仅是可男考虑的)
if (i
try(i+1,tw,tv-物品i的价值);
else
/*又一个完整方案,因它比前面的方案好,以它作为最佳方案*/
以当前方案作为临时最佳方案保存;
}
为了理解上述算法,特举以下实例。设有4件物品,它们的重量和价值见表:
物品 0 1 2 3
重量 5 3 2 1
价值 4 4 3 1
并设限制重量为7。则按以上算法,下图表示找解过程。由图知,一旦找到一个解,算法就进一步找更好的佳。如能判定某个查找分支不会找到更好的解,算法不会在该分支继续查找,而是立即终止该分支,并去考察下一个分支。
按上述算法编写函数和程序如下:
【程序】
# include
# define N 100
double limitW,totV,maxV;
int option[N],cop[N];
struct { double weight;
double value;
}a[N];
int n;
void find(int i,double tw,double tv)
{ int k;
/*考虑物品i包含在当前方案中的可能性*/
if (tw+a.weight<=limitW)
{ cop=1;
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv;
}
cop=0;
}
/*考虑物品i不包含在当前方案中的可能性*/
if (tv-a.value>maxV)
if (i
else
{ for (k=0;k
option[k]=cop[k];
maxv=tv-a.value;
}
}
void main()
{ int k;
double w,v;
printf(“输入物品种数\n”);
scanf((“%d”,&n);
printf(“输入各物品的重量和价值\n”);
for (totv=0.0,k=0;k
{ scanf(“%1f%1f”,&w,&v);
a[k].weight=w;
a[k].value=v;
totV+=V;
}
printf(“输入限制重量\n”);
scanf(“%1f”,&limitV);
maxv=0.0;
for (k=0;k find(0,0.0,totV);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“\n总价值为%.2f\n”,maxv);
}
作为对比,下面以同样的解题思想,考虑非递归的程序解。为了提高找解速度,程序不是简单地逐一生成所有候选解,而是
从每个物品对候选解的影响来形成值得进一步考虑的候选解,一个候选解是通过依次考察每个物品形成的。对物品i的考察有这样几种情况:当该物品被包含在候选
解中依旧满足解的总重量的限制,该物品被包含在候选解中是应该继续考虑的;反之,该物品不应该包括在当前正在形成的候选解中。同样地,仅当物品不被包括在
候选解中,还是有可能找到比目前临时最佳解更好的候选解时,才去考虑该物品不被包括在候选解中;反之,该物品不包括在当前候选解中的方案也不应继续考虑。
对于任一值得继续考虑的方案,程序就去进一步考虑下一个物品。
【程序】
# include
# define N 100
double limitW;
int cop[N];
struct ele { double weight;
double value;
} a[N];
int k,n;
struct { int ;
double tw;
double tv;
}twv[N];
void next(int i,double tw,double tv)
{ twv.=1;
twv tw=tw;
twv tv=tv;
}
double find(struct ele *a,int n)
{ int i,k,f;
double maxv,tw,tv,totv;
maxv=0;
for (totv=0.0,k=0;k
totv+=a[k].value;
next(0,0.0,totv);
i=0;
While (i>=0)
{ f=twv.;
tw=twv tw;
tv=twv tv;
switch(f)
{ case 1: twv.++;
if (tw+a.weight<=limitW)
if (i
{ next(i+1,tw+a.weight,tv);
i++;
}
else
{ maxv=tv;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
case 0: i--;
break;
default: twv.=0;
if (tv-a.value>maxv)
if (i
{ next(i+1,tw,tv-a.value);
i++;
}
else
{ maxv=tv-a.value;
for (k=0;k
cop[k]=twv[k].!=0;
}
break;
}
}
return maxv;
}
void main()
{ double maxv;
printf(“输入物品种数\n”);
scanf((“%d”,&n);
printf(“输入限制重量\n”);
scanf(“%1f”,&limitW);
printf(“输入各物品的重量和价值\n”);
for (k=0;k
scanf(“%1f%1f”,&a[k].weight,&a[k].value);
maxv=find(a,n);
printf(“\n选中的物品为\n”);
for (k=0;k
if (option[k]) printf(“%4d”,k+1);
printf(“\n总价值为%.2f\n”,maxv);
}