MIT算法导论(一)——算法分析和引论

文章目录

  • 1 算法分析及引论
    • 1.1 算法
    • 1.2 排序
      • 1.2.1 插入排序
        • 1.2.1.1 插入排序原理
        • 1.2.1.2 时间复杂度
        • 1.2.1.3 渐进时间复杂度
        • 1.2.1.4 回到算法
      • 1.2.2 归并排序
        • 1.2.2.1 归并排序原理
        • 1.2.2.2 归并排序时间复杂度

1 算法分析及引论

1.1 算法

算法是一门关注性能的学科,也就是说,我们致力于让我们要做的事变得更快。让我们提出一个问题,在程序设计中,什么比性能更重要?

成本(程序员的时间成本),可维护性,稳定性(健壮性,是否老崩溃),功能性,安全,可扩展性。

既然这么多因素都比性能更重要,那我们为何还要研究算法,到底是性能重要还是用户的体验更重要呢?

可以肯定的是,很多时候性能和用户体验是紧密联系在一起的,很多时候没有什么东西比干等着更难受了。

我们研究算法的缘由是:很多时候性能的好坏决定其是否可行,如我们对实时的要求,如果算法性能不快,那么就会导致无法追求实时的效果,实时当然就不可行了。或者说,如果它占用过多的内存,它也是不可行的,因为硬件需求跟不上。总的来说:算法走在所有因素的最前沿

第二个关键是,算法是一种解释性语言。其通用让几乎所有程序员都接收,它是一种让程序最为简洁的思考方式。我们有一个很好说明的例子:我们把算法比作货币,那么货币作为社会最底层的东西,如果你想要买吃的,买住的,都离不开货币。

第三个关键是,算法决定了一个程序的安全和用户体验的好坏。虽然C比Java快很多,java编程性能会损失三倍左右,但是人们还是喜欢用java来编写程序,因为java的面向对象机制以及异常机制值得人们为之付出。这也是为什么我们要提高性能,因为我们总是把性能分摊给其他的要素。

第四个关键是,我们对此乐此不疲。人们总是喜欢快的东西,比如火箭、滑雪,我们总喜欢这些快速的东西。

在某种意义上,上述这些简单的问题是我们研究下列问题的指路明灯。

1.2 排序

让我们来看看一个非常古老的问题,排序。排序包含了许多基本的算法。让我们举一个排序的例子:我们输入一组序列a1,a2...ana_1,a_2...a_na1,a2...an,按照需求排列后作为输出。排列一般是使得如a1<=a2...<=ana_1<=a_2...<=a_na1<=a2...<=an一般。按一定顺序排序。

1.2.1 插入排序

1.2.1.1 插入排序原理

这里我们介绍第一个算法,即插入排序。通常我们描述算法用的是伪代码而不是编程语言。伪代码中包含有一些解释性语言如英语或者中文,其能够让我们更清楚该算法包含的思想。

值得一提的是,用伪代码描述算法时,我们通常会使用缩进,其相当于大多数语言中标注开始和结束的分隔符。好比Java和C中的大括号。事实上,历史上还真有一些语言(例如python)是用缩进代表分隔,但是不是一种好的主义,因为在换页的时候,你很难知道自己在哪一个嵌套的层级,而是用大括号就能够很好的表示。

让我们缔造一个数组,如下所示:

image-20220227152614333

这个数组通常是无序的,然而我们通过从从左到右指定一个变量,这个变量我们称为,他会从数组中提取数据,然后和前面已经排好的序列做比较,然后插入前面的序列,这也是为这么这个算法叫做插入排序的原因。

比较经典的例子我认为是斗地主,在发牌时,我们时常会事先排好手中的牌,我们是一张一张拿起来的,然后将乱序的牌提取出来,然后插入前面已经排序好的位置中。无论什么时候,左手的牌都是排好序的,而右手拿的牌都是即将要插入到排好序的牌中,并且插入位置符合其自身顺序。

image-20220227152911143

我们来看一下插入排序的伪代码实现:

INSERTION-SORT(A)for j←2 to length[A]do key←A[j]Insert A[j] into the sorted sequence A[1..j-1]i←j-1while i>0 and A[i]>keydo A[i+1]←A[i]i←i-1A[i+1]←key

看上面的伪代码似乎有些费劲,可事实上,它与我们上面讲述的别无二致。让我们引入一个具体的例子来体会这个算法。

我们有一个序列:8 2 4 9 3 6

当我们执行插入排序时,我们的首要任务是从第二个元素开始,依次提取键key,然后和前面排序好的序列做对比再插入。题意来看首先提取的是2,然后插入8之前。原序列则变为2 8 4 9 3 6。接着我们找到4,4和2 8进行比对,然后插入2和8之间,原序列变为2 4 8 9 3 6,以此类推按照排序规则循环移动。

image-20220227153759698

让我们借助数学工具来解剖该算法。如果输入序列之前就有序,那么已排序序列后的排序工作量就大大减少。因为每次遍历,它都是做上面循环往复的事。最坏的情况是该序列刚好是逆序,那么每个元素都将进行插入排序。

对应在这道题上,这个例子总共有6个数据,我们将其输入的规模扩大到100000,那么一旦发生逆序,其时间开销很大。为此在进行算法分析时,我们通常使用渐进时间复杂度,即假设输入的规模为n,趋于无限,则运行时间可以看做是以n为映射的函数。

1.2.1.2 时间复杂度

上述引入的时间复杂度中,显然有最好最坏的情况,最坏对应算法时间的上限,其代表着对用户的一种承诺,(是的尊贵的用户我的程序是不会超过这个时间的)。而最好的情况对应着算法时间的下限。我们通常关注算法的最坏时间复杂度。即我们要给出用户保证,我们的程序总能做到这样,而不是有时能这样,我们通常会把输入考虑成最坏的情况,对应上面插入排序的例子,我们最坏的情况是逆序。

当然有时我们也会讨论平均时间复杂度,这个时候T(n)就成了我们算法的时间期望值。那不禁有人会问,期望值是啥?

我们希望听到一些比较数学味道的回答,所谓的期望实际上就是每种输入的运行时间,乘以那种输入出现的概率。这是一种连续型的思考方式,我们可以理解为加权平均。

那我们如何知道特定输入在给定情况下的概率是多少?有时候我们并不知道,所以我们需要给出一个假设了,一个有关输入的统计分布的假设,否则期望时间无从谈起。

最常见的假设是均匀分布,即所有规模为n的输入情况都是等可能地出现。

最后我们想讲讲最好时间复杂度,我们称之为假象!~bogus(我在大声地笑哈哈),没啥用!因为最好的情况不可能出现。如果一个序列已经排好序,我们还有必要去排序吗,哈哈哈。除非你想要cheat!欺骗!在你得到一个特定输入下,然后你得到一个比较满意的结果后欺骗自己,对!这就是我想要的效果,那你就可以考虑它。

1.2.1.3 渐进时间复杂度

回到这个算法,我们不禁发问,插入排序的最坏情况时间是多少?

最简单的回答是,这取决于你的计算机。你用的是超级计算机还是腕表那么大的计算机,算力会大相径庭。但是通常来说,我们都是比对两个算法在同一台机器上的表现,即相对速度。当然也有人关注绝对速度,我们猜想真有某种算法能无论在什么机器上运行都表现得更好吗?

以上的回答实际上会对我们最开始的问题造成混淆,这不得不提到我们的大局观(big idea)了,这也是为什么算法涉猎如此广泛的原因。我们应该使用抽象的眼光来看待事物,从复杂的情况中提取关键的因素对其分析。这就是所谓的渐进分析。渐进分析的基本思路是:忽略掉依赖于机器的常量,不去检查其因素,而是关注算法本身时间的增长。

使用渐进分析,自然要引入相应的数学符号。这里我们使用的是thetaΘ\ThetaΘ来表示渐进时间复杂度。实际上theta写起来很简单,你需要做的是,弃去它的低阶项,并忽略前面的常数因子。

比如一个算法花费时间是:3n3+90n2−5n+60463n^3+90n^2-5n+60463n3+90n25n+6046,那我们就要使用高数中采取的抓大头准则,使的3n33n^33n3后面的项全部抛弃,然后再把3抛弃掉即可。所得为Θ(n3)\Theta(n^3)Θ(n3)

上述情况需要知道的是,假设有一个算法的时间复杂度是Θ(n2)\Theta(n^2)Θ(n2),那么它迟早比Θ(n3)\Theta(n^3)Θ(n3)速度要快。因为当n逐渐增大时,指数会造成指数爆炸,使得比最大项还小的常数项无法动摇这个最终的结果。这对应到计算机的优劣上,即使你的Θ(n2)\Theta(n^2)Θ(n2)算法是在慢速计算机上运行,总有一天它也会超越在快速计算机上运行的Θ(n3)\Theta(n^3)Θ(n3)算法。

image-20220112103017247

当然,站在工程角度上看,n有时候太大没有节制是不行的,这会导致我们的计算机无法运行该算法,这也是为什么有时候我们对一些慢速算法比较感兴趣,因为它们在n较少的时候能够保持较高的速度。所以仅仅会做算法分析并不能使你成为高手,你需要保持每天编程,并且在实际编程中运用,知道其什么时候相关什么时候不相关。

1.2.1.4 回到算法

这时候我们可以来分析一些刚才的插入排序算法了。就像我们说的我们关注其最坏时间复杂度。即输入为逆序。

INSERTION-SORT(A)for j←2 to length[A]do key←A[j]Insert A[j] into the sorted sequence A[1..j-1]i←j-1while i>0 and A[i]>keydo A[i+1]←A[i]i←i-1A[i+1]←key

在这其中明显有嵌套循环,我们实际上关注循环,因为其他常数操作无关紧要,我们是对其渐进分析。第一个循环中是2到j,而内部循环是从无需的抽出元素对前面的有序序列做插入操作,实际上是由2到j次比对。也就是说,时间复杂度是θ(n2n^2n2)。

那这个时间复杂度到底快不快呢?对于小的n它确实挺快,但是对于巨大的n它就不是那么回事了,所以在下面我会给你一个更快的算法。

1.2.2 归并排序

1.2.2.1 归并排序原理

让我们还是用抽象逐步讲到具体。我们给出一个对于A[1…n]的归并排序。

  • 如果给定序列是1,自然不用排序,因为序列中只有一个元素。
  • 否则进行递归,递归的每一层都会将序列一分为二,直到分出一个元素为止,然后对其一对一排序。
  • 最后将排序后的序列全部重组。

这里我们出现一个新名词——递归,这个知识点实际上有点小难,我推荐你去看一下我写的博客数据结构杂谈番外篇——搞懂递归的小文章_弄鹊-CSDN博客。

上述归并算法关键的部分在于归并。归并实际上是使用了分治法的思想,先将大问题分解成小问题,然后再将小问题求解后合并结果。

假设我现在有这么一个数组 8 4 5 7 1 3 6 2,如果采用归并算法,我们是这么做的:

image-20220227164645894

在我们使用递归进入最深层次(即不可再分的第三层)时,我们开始进行,我们治的方式是用两根数组指针分别指向分开的两个序列,如下图所示:

image-20220227165318106

这个操作的时间复杂度是θ(n),因为我们所花时间都是在合并n次上。实际上分解并不耗费时间,因为每次递归分解都是分解一次。而每次合并要移动指针。

1.2.2.2 归并排序时间复杂度

让我们来看看整个归并排序的时间复杂度是多少。实际上归并排序总时间=子序列排好序时间+合并时间。

现在我们提前使用递归树方法来解决这里的问题,关于详细我们会在下一小节叙述:

假设我们有n个数的序列,那么第一次分就可以分为两个n/2的序列。

T(n)=2∗T(n/2)+合并时间T(n) = 2*T(n/2)+合并时间T(n)=2T(n/2)+。又由于合并的时候按照我们上面所说是循环比较两个指针指向值的大小,所以复杂度为n。则我们可以改写T(n)=2∗T(n/2)+θ(n)T(n) = 2 *T(n/2)+θ(n)T(n)=2T(n/2)+θ(n)。当然了,对于n = 1的情况,T(n) = 1,这个情况我们一般忽略,因为他对递归解没有影响。我们用显式的c∗nc*ncn来替换隐式的θ(n),然后把其写出树状结构如下所示:

image-20220227183443411

c* n指的是多个常数阶步骤所消耗的时间复杂度。常数阶的时间复杂度在进行渐进表示时通常省略,所以我们说他是隐式的,而我们用显式的c*n来表示这些步骤。因为我们在这个时候是要计算T(n)而不是Θ(n)\Theta(n)Θ(n)

也就是说,这个递归树的每一层实际上都是cn。如第二层cn = T(n/2)+T(n/2)。那我们要计算递归树中所有结点所消耗的时间复杂度,即全部相加,用cn乘上树的高度即可。树的高度为lgn,所以在递归树中归并算法花费的时间复杂度为cn∗lgncn*lgncnlgn。这时候我们如果要求渐进时间复杂度,只需忽略常数项,所以由此可得归并排序时间复杂度为Θ(nlgn)\Theta(nlgn)Θ(nlgn)

在计算机这一块领域中,lgn就是log2nlog_2nlog2n。我们设n除2分了一次,除2除了x次,那么最终n/2xn/2^xn/2x = 1,通过指对互换进行反解,即x=log2nx = log_2nx=log2n。也就是说树的高度是log2nlog_2nlog2n。而lgn只不过是log2nlog_2nlog2n的常数倍,由渐进表示可知这个不影响。所以你要拿lgn表示log3、log4nlog_3、log_4nlog3log4n也是可以的。

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

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

相关文章

使用Apache Tomcat Maven插件部署运行 Web 项目

2019独角兽企业重金招聘Python工程师标准>>> 什么是Apache Tomcat Maven Plugin&#xff1f; Maven Plugin 是Apache Tomcat 提供的一个Maven插件&#xff0c;它可以在你没有tomcat容器时将任何一个war项目文件部署在该插件上提供访问。 为什么要用Apache Tomcat Ma…

POJ2104 (平方分割)二分查找理解。

题意&#xff1a;任意区间求第k大数 思路&#xff1a; 预处理&#xff1a;利用平方分割&#xff08;分桶法&#xff09;把区间切割成B sqrt(n)大小的一块块&#xff0c;然后每个各自排序。 二分第k大数x&#xff0c;接着就需要求[l,r]区间中x的排名&#xff0c;与k比较&#x…

每日一题——leetcode237 删除链表中的结点

1 题目 237. 删除链表中的节点 难度简单 请编写一个函数&#xff0c;用于 删除单链表中某个特定节点 。在设计函数时需要注意&#xff0c;你无法访问链表的头节点 head &#xff0c;只能直接访问 要被删除的节点 。 题目数据保证需要删除的节点 不是末尾节点 。 示例 1&am…

机器学习的练功心法(一)——机器学习概述

1 机器学习概述 文章目录1 机器学习概述1.1 学习方法1.2 什么是机器学习1.3 监督学习1.4 无监督学习1.5 强化学习1.6 机器学习的开发流程1.1 学习方法 引入&#xff1a;对于机器学习来说&#xff0c;我们需要有一个大局观&#xff0c;什么是大局观&#xff1f;你站的比别人高&…

项目管理规范

从鼬加入的那一周开始&#xff0c;四代就开始着手准备起草代码规范了。代码规范不可少很多人理直气壮的认为&#xff0c;创业团队&#xff0c;或者说人数少的团队根本不需要代码规范。他们的口头禅经常是&#xff1a;“没办法啊&#xff01;我们需要快速的完成客户的需求啊&…

每日一题——王道考研2.2.4.1

1 题目 从顺序表中删除具有最小值的元素&#xff08;假设唯一&#xff09;&#xff0c;并由函数返回被删元素的值。空出的位置由最后一个元素填补&#xff0c;若顺序表为空&#xff0c;则显示出错信息并退出运行。 ——出自王道书2023版2.2.4的第二大题第一小题 2 思路 既然…

批处理启动vm虚拟机服务 vm12启动无界面启动vm虚拟机系统 windows上如何操作服务 sc net启动关闭服务...

windows(win10)批处理脚本 打开vm虚拟机的服务,并且开启无界面虚拟机 echo off net start "vds" net start "VMAuthdService" net start "VMnetDHCP" net start "VMware NAT Service" net start "VMUSBArbService" net star…

机器学习的练功心法(二)——概述

2 概述 文章目录2 概述2.1 模型概述2.1.1 预测房价问题2.1.2 符号2.2 代价函数2.3 代价函数的用处2.4 回到问题2.5 梯度下降2.6 梯度下降知识点总结2.7 线性回归模型的梯度下降2.1 模型概述 2.1.1 预测房价问题 在我们要开始下面的问题前&#xff0c;我们先来看一些关于房价预…

Jquery对象和DOM对象---Jquery API (1)

文&#xff0f;饥人谷_韩宝亿&#xff08;简书作者&#xff09;原文链接&#xff1a;http://www.jianshu.com/p/98a0c82c47e4著作权归作者所有&#xff0c;转载请联系作者获得授权&#xff0c;并标注“简书作者”。一、为什么要用Jquery? DOM API 1.难用 要想拿到一个对象&…

机器学习的练功心法(三)——特征工程

文章目录致谢3 特征工程3.1 Sklearn工具及数据集3.2 数据集3.3 数据集的划分3.4 特征工程介绍3.4.1 为什么需要特征工程3.4.2 什么是特征工程3.4.3 特征提取3.4.3.1 字典特征提取3.4.3.2 文本特征提取3.4.3.3 中文文本特征提取3.4.3.4 TF-IDF算法3.5 特征预处理3.5.1 特征预处理…

数据库杂谈(六)——数据库管理系统

文章目录6 数据库管理系统6.1 数据库管理系统结构简介6.2 进程结构6.2.1 进程的分类6.2.2 线程的由来6.2.3 建立进程的过程6.3 数据目录6 数据库管理系统 6.1 数据库管理系统结构简介 数据库管理系统DBMS是数据库系统的核心。而目前市场上我们接触到的商品化DBMS大多数是关系…

booth算法实现乘法器

booth算法充分的利用到了补码的重要性&#xff0c;使得我们在利用补码进行计算时减少了很多时序。下面的表格是我们假设2 作为乘数所进行的分析。接下来&#xff0c;我将用代码向大家阐述。 1、开始的时候在乘数2的‘负一位’加上一个默认0值00100 2、先判断[0:-1],结果是2‘b0…

OS实验一实验报告

实验一、命令解释程序的编写实验 专业&#xff1a;商业软件工程 姓名&#xff1a;王泽锴 学号&#xff1a;201406114113 一、实验目的 &#xff08;1&#xff09;掌握命令解释程序的原理&#xff1b; &#xff08;2&#xff09;*掌握简单的DOS调用方法&#xff1b; &#xf…

机器学习的练功方式(四)——KNN算法

文章目录致谢致歉4 KNN算法4.1 sklearn转换器和估计器4.1.1 转换器4.1.2 估计器4.2 KNN算法4.2.1 概述4.2.2 电影类型分析4.2.3 算法实现致谢 闵氏距离_百度百科 (baidu.com) 机器学习之KNN&#xff08;k近邻&#xff09;算法详解_平原的博客-CSDN博客_knn 鸢尾花(Iris)数据集_…

数据库杂谈(七)——数据库的存储结构

文章目录7 数据库的存储结构7.1 数据库访问管理-文件组织7.2 数据库访问管理——索引技术7 数据库的存储结构 让我们重新温习第六讲的所学知识。 这个图实际上我们要关注的是蓝色箭头指向的那一层。在这一层之上&#xff0c;我们都是对SQL语句动手&#xff0c;而在这一层之下&…

JSP中直接在输入框中校验

jsp: <label class"control-label col-sm-2" for"codeAdd">险种代码<span style"color: red;">*</span></label> <div classcontrols col-sm-4> <input class"form-control remote-validate" data-r…

【iCore3 双核心板_FPGA】例程十二:Modelsim仿真实验

实验指导书及代码包下载&#xff1a; 链接&#xff1a;http://pan.baidu.com/s/1hs4zNFY 密码&#xff1a;5z62 iCore3 购买链接&#xff1a; https://item.taobao.com/item.htm?id524229438677

机器学习的练功方式(五)——模型选择及调优

文章目录5 模型选择及调优5.1 数据增强5.2 过拟合5.3 交叉验证5.4 超参数搜索——网格搜索5 模型选择及调优 5.1 数据增强 有时候&#xff0c;你和你的老板说你数据不够&#xff0c;它是不会理你的。老板会发问&#xff1a;为什么你是做机器学习的要那么多数据干嘛&#xff0…

关于内存的一些基础知识

1、free&#xff1a;Display amount of free and used memory in the system. free显示的数值来自/proc/meminfo&#xff08;默认单位是KB&#xff09;。各个项的含义分别是&#xff1a; 1&#xff09;Mem这一行&#xff0c;shared&#xff1a;已废弃&#xff1b;buffers&#…

Flask 单例模式 session

一、单例模式 单例模式分为四种&#xff1a;基于文件的单例模式&#xff0c;基于类方法的单例模式&#xff0c;基于__new__的单例模式&#xff0c;基于metaclass的单例模式 1. 基于类方法的单例模式 - 不支持多线程模式 import threadingclass Singleton(object):def __init__(…