‘壹’ 程序员需要怎样的数学基础
离散数学对程序员来说非常重要,还有组合数学、线性代数、概率论、数论等等,即使你将来不做研究,这些基础知识也能极大地提高你的水平。计算机科学对离散数学的要求很高,建议你先学习前面提到的这些课程,然后学习计算机算法和数据结构,再配合到网上的在线题库做题,过程很艰辛,但是对你的帮助会很大。
推荐书目:
《具体数学》(先学完前面的数学课程,在水平有一定进步以后再看)
《算法导论》(应该人手一本的好书)
简单来说,学数学的目的,一方面是活跃你的思维;另一方面是为了深入学习算法打基础,设想一下,同样的问题,普通人的程序要几十分钟甚至几小时几天才能解决出来,甚至根本无法解决,而你精心设计的程序却能在1秒内解决出来,这就是数学的魅力、算法的魅力。
其实,一切取决于你是否想做一个高级程序员。如果你做体力活(其实一般编程别人都认为是体力活),那你可以不学,因为你用不到,但是,你要是做技术上的创新,做个很强的程序员,没有数学的支持,很难。
你既然学习了C,c++,你也知道算法的重要性,同样一个问题,我用13行程序解决了,我的同学居然用了33行,因为他不懂的用数学。你要达到什么高等,取决于你的数学修养。当然,要做一个普通的程序员就不用学习了。要挑战自己,做个好的,优秀的,学习数学吧!
‘贰’ QQ秀里的魅力值是什么个算法为什么这几天支线下降
魅力直是鲜花工厂里面的鲜花决定的,送给你花的人越多你的魅力直就会增加,鲜花是有花期的,一般免费的玫瑰就是15天,时间一到魅力直就会消失
‘叁’ 递归算法有何特点
递归4—递归的弱点
之所以没有把这段归为算法的讨论,因为这里讨论的不在是算法,而只是讨论一下滥用递归的不好的一面。
递归的用法似乎是很容易的,但是递归还是有她的致命弱点,那就是如果运用不恰当,滥用递归,程序的运行效率会非常的低,低到什么程度,低到出乎你的想象!当然,平时的小程序是看不出什么的,但是一旦在大项目里滥用递归,效率问题将引起程序的实用性的大大降低!
例子:求1到200的自然数的和。
第一种做法:
#include <stdio.h>
void main()
{
int i;
int sum=0;
for(i=1;i<=200;i++)
{
sum+=i;
}
printf("%d\n",sum);
}
该代码中使用变量2个,计算200次。再看下个代码:
#include <stdio.h>
int add(int i)
{
if(i==1)
{
return i;
}
else
{
return i+add(i-1);
}
}
void main()
{
int i;
int sum=0;
sum=add(200);
printf("%d\n",sum);
}
但看add()函数,每次调用要声明一个变量,每次调用要计算一次,所以应该是200个变量,200次计算,对比一下想想,如果程序要求递归次数非常多的时候,而且类似与这种情况,我们还能用递归去做吗?这个时候宁愿麻烦点去考虑其他办法,也要尝试摆脱递归的干扰。
21:21 | 添加评论 | 固定链接 | 引用通告 (0) | 记录它 | 计算机与 Internet
程序算法5—递归3—递归的再次挖掘
递归的魅力就在于递归的代码,写出来实在是太简练了,而且能解决很多看起来似乎有规律但是又不是一下子能表达清楚的一些问题。思路清晰了,递归一写出来问题立即就解决了,给人一重感觉,递归这么好用。我们在此再更深的挖掘一下递归的用法。
之前再强调一点,也许有人会问,你前边的例子用递归似乎是更麻烦了。是,是麻烦了,因为为了方便理解,只能举一些容易理解的例子,一般等实际应用递归的时候,远远不是这种状态。
好了我们现在看一个数字的序列;有一组数的集合{1,2,4,7,11,16,22,29,37,46,56……}我故意多给几项,一般是只给前4项让你找规律的。序列给了,要求是求前50项的和。规律?有?还是没有?一看就象有,但是又看不出来,我多给了几项,应该很快看出来了,哦,原来每相邻的两项的差是个自然数排列,2-1=1,4-2=2,7-4=3,11-7=4,16-11=5……
好了,把规律找出来了,一开始可能觉得没头绪,没问题,咱们把这个序列存放到一个数组总可以吧!那我们就声明一个数组,存放前50个数据,一个一个相加总可以了。于是有了下边的写法:
#include <stdio.h>
void main()
{
int i,a[50],sum=0;
a[0]=1;
for(i=1;i<50;i++)
{
a[i]=a[i-1]+i;
}
for(i=0;i<50;i++)
{
sum+=a[i];
}
printf("%d\n",sum);
}
好了,代码运行一下,结果出来了,正确不正确呢?自己测试吧,把50项改成1、2、3、4、5……项,试试前多少项是不是正确,虽然这不是正确的测试方法,但是的确是常用的测试方法。
等到这个代码已经完全理解了,完全明白了正个计算过程,我们就应该对这段代码进行改写优化了,毕竟这个代码还是不值得用一个数组的,那么我们尝试着只用变量去做一下:
#include <stdio.h>
void main()
{
int i;
int number=1;
int sum=0;
for(i=0;i<50;i++)
{
number+=i;
sum+=number;
}
printf("%d\n",sum);
}
不知道我这样写是不是跨度大了点,但是我不准备详细解释了,很多东西需要你去认真分析的,所以很多东西如果不懂,自己想清楚比别人解释的效果会更好,因为别人讲只能让你理解,如果你自己去想,你就在理解的同时学会了思考。
这个代码写出来,不要继续看下去,先自己尝试着把这个题目用递归做一下看看自己能不能写出来,当然,递归并不是那么轻松就能使用的,有时候也是需要去细心设计的。如果做出来了,对比一下下边的代码,如果没有写出来,建议认真分析后边的代码,然后最好是能完全掌握,能自己随时把这行代码写出来:
#include <stdio.h>
int add(int n,int num,int i)
{
num+=i;
if(i>=n-1)
{
return num;
}
else
{
return num+add(n,num,i+1);
}
}
void main()
{
int sum;
sum=add(50,1,0); /*50表示前50象项*/
printf("%d\n",sum);
}
当然这个代码中的n只是一个参考变量,如果把if(i>=n-1)中的n该成50,那么就不需要这个n了,函数两个参数就可以了,这样写是为了修改方便。
20:28 | 添加评论 | 固定链接 | 引用通告 (0) | 记录它 | 计算机与 Internet
程序算法4—递归2—递归的魅力
两天没有再写下去,因为毕竟有时候会有点心情问题,有时候觉得心情不好,一下子什么东西都想不起来了,很多时候写一些东西是需要状态的,一旦状态有了,想的东西才能顺利的写出来,虽然有些东西写出来在别人看来很垃圾,但是起码自己觉得还是相当满意的,我写这个本来就没有多少技术含量,只是想给初学程序的人一些指引,加快他们对程序的领悟!
好了,言归正传,继续上次递归的讨论,看看递归的魅力所在。
有这样一个问题,说一个猴子和一堆苹果,猴子一天吃一半,然后再吃一个,10天后剩下一个了,也就是说吃了10次,剩下1个了。问原来一共有多少苹果。
当然我们的目的不是求出苹果的数量,而是寻求一种解决问题的方法,这个问题一出来,通常对程序掌握深度不一样的朋友对这个题会有不同的认识,首先介绍一种解决方法,这种人脑袋还是比较聪明的,思路非常的明确,也有可能语言工具掌握的也不错,代码写出来非常准确,先看一下代码再做评价吧:
#include <stdio.h>
void main()
{
int day=10;
int apple;
int i,j;
for(i=1;;i++)
{
apple=i;
for(j=0;j<day;j++)
{
if(apple%2==0&&apple>0)
{
apple/=2;
apple--;
}
else
{
break;
}
}
if(j==day&&apple==1)
{
printf("%d\n",i);
return;
}
}
}
程序的大概思路很明确,简单介绍一下,这种写法就是从一个苹果开始算起,for(i=1;;i++)的作用就是改变苹果的数量,如果1个符合条件,那就试试2个,然后3个、4个一直到适合为止,里边的for循环就是把每一次取得的苹果的数目进行计算,如果每次都能顺利的被2整除(也就是说每次都能保证猴子能正好吃一半),然后再减一一直到最后,如果最后苹果剩下是一个而且天数正好是10天,那么就输出一下苹果的数目,整个程序退出,如果看不明白的没关系,这个写法非常的不适用,我们叫写出这种算法的人傻X,虽然这种人脑袋也挺聪明,能写出一些新鲜的写法,但是又脏又臭,代码既不简练又不高效。
所以说,有时候有些人以为自己学的很好了,自己所做的一切都是最好的,这种想法是不正确的,也许有些初学者没有什么经验写出来的代码却更让人容易明白点,那么也是先看看代码:
#include <stdio.h>
void main()
{
int day[11];
int i;
day[0]=1;
for(i=1;i<11;i++)
{
day[i]=(day[i-1]+1)*2;
}
printf("%d\n",day[10]);
}
代码不长,而且也恰当的应用了题目中的规律,不是说要吃一半然后再吃一个吗?那我用数组来存放每天苹果的数量,用day[0]表示最后一天的苹果数量,那就是剩下的一个,然后就是找规律了,什么规律?就是如果猴子不多吃一个的话,那就是正好吃了一半,也就是说猴子当天吃了之后剩余的苹果的数目加1个然后再乘以2就是前一天的数目了,这样一想这个题目就简单的多了,于是这个题用数组就轻松的做出来了。
那么这个代码究竟是不是已经很好了呢,我们注意到,这里边每个数组元素只用了一次并没有被重复使用,再这种情况下我们是不是可以用一种方法代替数组呢?于是就有了更优化的写法,这个写法似乎已经是相当简练了:
#include <stdio.h>
void main()
{
int apple=1;
int i;
for(i=0;i<10;i++)
{
apple=(apple+1)*2;
}
printf("%d\n",apple);
}
代码写到这里已经把问题完全抽象化了,所以我们就应该站在数学的角度去分析了。也许我们就应该结束了讨论,但是偏偏这个时候,又来了递归,悄悄的通过美丽的调用显示了一下她的魅力:
#include <stdio.h>
int apple(int i)
{
if(i==0)
{
return 1;
}
else
{
return (apple(i-1)+1)*2;
}
}
void main()
{
int i;
i=apple(10);
printf("%d\n",i);
}
原理都还是一样的,但是写出来的格式已经完全变掉了,没有了for循环。假想一个复杂的问题远比这个问题复杂,而且没有固定循环次数,那么我们再使用循环虽然也能解决问题,但是可能面临循环难以设计、控制等问题,这个时候用递归可能就会让问题变的非常的清晰。
另外说一点,一般我这里的代码,并不是从最差到最好的,基本排列是从最差到最合适的代码(当然是本人认为最合适的,也许还有更好的,本人能力所限了),然后最后给出一种比较违反常规的代码,一般是不赞成用最后一种代码的,当然有时候最后一种代码也许是最好的选择,看情况吧!
20:25 | 添加评论 | 固定链接 | 引用通告 (0) | 记录它 | 计算机与 Internet
10月15日
程序算法3—递归1—递归小显威力
现在用C语言实现一个字符串的倒序输出,当然,方法也是很多的,但是如果程序中能有相对优化的方法或者简单明了易读的方法,那对你自己或者别人都是一种幸福。
第一种写法,这类写法既浪费内存又不实用,一般是刚学程序的才这样做,程序的结构很简单,利用的是数组:
#include <stdio.h>
void main()
{
char c[2000];
int i,length=0;
for(i=0;i<2000;i++)
{
scanf("%c",&c[i]);
if(c[i]=='\n')
{
break;
}
else
{
length++;
}
}
for(i=length;i>0;i--)
{
printf("%c",c[i-1]);
}
printf("\n");
}
这段代码中的数组,声明大了浪费内存空间,声明小了又怕不够,所以写这种代码的人一般写完之后会祈祷,祈祷测试的人不要输入的太多,太多就不能完全显示了!
与其这么提心吊胆,于是又有人想出了第二种方法,终于解决了一些问题,而且完全实现了程序的实际要求,于是,这种人经过一番苦想,觉得问题终于可以解决了,这种方法看起来是一种很不错的方法。
#include <stdio.h>
#include <malloc.h>
void main()
{
int i;
char *c;
c=(char *)malloc(1*sizeof(char));
for(i=0;;i++)
{
*(c+i)=getchar();
if(*(c+i)=='\n')
{
*(c+i)='\0';
break;
}
else
c=(char *)realloc(c,(i+2)*sizeof(char));
}
for(--i;i>=0;i--)
{
putchar(*(c+i));
}
printf("\n");
free(c);
}
怎么样?不错,准确的应用内存,几乎没有浪费什么空间,这种方法也体现了一下指针的强大功能,写这个程序虽然不敢说这个人已经掌握了指针的应用,但是起码可以说他已经会用指针了。代码写出来,看起来已经有点美感。
但是也有一些人还是比较喜欢动脑筋的,经过一番思考,终于想出了第三种比较容易写的方法,也许有写初学者可能觉得有些难度,但是事实上这个东西一点都不难,如果稍微有点程序功底之后再看这段代码,应该是相当轻松!
#include <stdio.h>
void run()
{
char c;
c=getchar();
if(c!='\n')
{
run();
}
else
{
return;
}
putchar(c);
}
void main()
{
run();
printf("\n");
}
写出的代码让人眼前一亮,哇!原来递归功能简单而又好用,那我们为什么不好好利用呢?但是递归也不一定就是最好的选择,因为有时候虽然递归用起来很方便,但是效率却不高,以后的讨论中还会详细说明。
‘肆’ 急呀!!请问哪位高手知道用手指计算加减乘除法的呀
如果你是因为看到下面这则新闻才问的,那没办法,不能帮您省个买书的钱了
中学老师掐手指计算百万以下运算
2006年04月03日 15:23:38 杭州网
“我们的两只手,也是一个完美的计算器,一般用它可能进行六位数以内的加减乘除、平方、开平方六种计算。其运算速度,加减速度可与电子计算机相媲美,乘除比珠算还要快,平方、开平方比笔算快得多。”这种堪称神奇的算法便是榆中四中一名中学教师罗彦频历经十年发明的“手指速算法”。
3月30日,当中央电视台《想挑战吗?》
栏目从网上看到此消息后,向这名中学教师发出了真诚的邀请,以期让全国人民目睹这种神奇算法的魅力。昨日,记者来到榆中罗彦频的家中全程见证了这种国内首创、世界独一的“手指速算法”。
100万数字以下随便出题
中午,当清瘦的罗彦频出现在记者面前时,他用05年新出的一本书作为开场白介绍了自己。在一本名为《手指速算法》的书中,记者对罗彦频有了初步的了解。无需借助其他计算工具,只需用两只手,就能快速完美地完成加减乘除、平方、开平方等运算。同时,罗彦频信心十足地告诉记者,只要是100万以下的数字,记者可以随便说出一组数字,他都能快速且正确无误地说出答案。
为验证此话的真实性,记者当场写下79856+97656,只见罗彦频伸出双手,用拇指、食指和无名指掐掐算算,3秒钟后说出了答案:177512。而此时,记者才刚刚在计算器上将数字输入完毕。此后,记者又出了一道带小数点的乘法,5784.7*94,不到30秒罗彦频抬起头,面带微笑地回答:等于543761.8。而记者在纸上演算时仍然慢了他一步。更令人称奇的是,“手指速算法”中的加减乘法等已被罗彦频列到简单的行列,而他最在行的却是开平方。对0.034226进行开平方时,罗彦频仅用了四十秒便正确地说出了答案。在进行一番验证后,罗彦频以绝对的实力证明,他的“手指速算法”绝不是空穴来风,而是一门快速科学、简单实用的计算方法。
央视关注“手指速算法”
为将自己发明创造的“手指速算法”在全国推广,2005年3月,罗彦频历经十年自编的《手指速算法》一书终于面世。该书一经出版,被甘肃省政法学院一名爱好速算的大学生梁权购得。书上的内容深深吸引了梁权,两人由此相识。在梁权的帮助下,在前罗彦频“手指速算”的内容频频出现在各大网站。也就是这个机会,有幸让罗彦频结识了全国各地的朋友。2005年7月,中央电视台《想挑战吗?》栏目从网上发现罗彦频的手指速算消息后,对此进行了极大的关注,并希望罗彦频能亲自到现场录制节日,为全国观众演绎手指速算的全过程,展示一个新奇的速算领域。
自己发明的速算方法得到中央电视台的关注,罗彦频的喜悦之情溢于言表。他高兴地告诉记者,3月30日,中央电视台的工作人员还督促他尽快整理录像资料寄到总部。并清楚地告诉他,等待的时间不会太长。
更让罗彦频受益匪浅的是,他的“手指速算法”也让他结识了全国各地的朋友。几个月来,全国200多人向他打电话或发短信咨询手指速算的学习方法。在罗彦频的手机上,一名黑龙江的男子说,“昨晚我激动得彻夜难眠,15年来,我在寻求探索手指教学,今天终于如愿。是发达的高科技让我们相识,让我们携手共进!同时该男子还诚恳地表示,6月他将抵兰拜罗彦频为师,希望推广速算法。
“速算”问世受侄女启发
当今社会,许多人已习惯了借助于计算工具进行运算。而罗彦频是怎么匠心独运地想到发明创造“手指速算法”?罗彦频笑着说,其实很简单。十年前,当我看到上小学一年级的侄女扳着手指头算数字时,突然萌发了是否发明一种快速简单的运算方法。在此想法的启发下,罗彦频不断地思考、尝试,构思了手指速算的框架。即通过各手指的展、伸、捏、屈等动作完成运算。在解释这种速算法的实用性时,罗彦频说,手指速算人人都能学,人人都能用。对真心想学的人来说,十几分钟就能学会,但需长期坚持练习,熟练后人人都能成为速算专家。一旦学会,将会终生受益,既能算数,又能巧手益智、健脑强身、可谓一举多得。
从发明到熟练运用,罗彦频本人又用了多长时间超过计算机、计算器的运算速度呢?当记者提出这个问题时,罗彦频笑了,“许多给我发短信的人都问过相同的问题”。尽管我发明了“手指速算法”,但我的成长过程也是通过长期坚持不懈的练习才得以成功。因此,可以说要想成功能是没有捷径可走的。
来源:兰州晚报
最后的建议还是:买本书,支持罗彦频老师!!!
再者,如果这里这么小的篇幅就能告诉你怎样用手指计算加减乘除法,人家也不用出上一本书啊,!!出几页纸然后卖报一样地四处发就行啦~~
如果这里这么小的篇幅就能告诉你怎样用手指计算加减乘除法,别人都会来网络知道学那东西,书就一本都难卖了,罗彦频也一定想过这个问题,可书还是照出照卖,所以我们可以确定,这个问题在这儿没得说清!!!
‘伍’ X分之3.6等于3分之二节比例
排序算法第一篇-排序算法介绍
在面试中,现在无论大小公司都会有算法的。其中排序算法也是一种很常见的面试题。比如冒泡,快排等。这些,排序算法自己看了一次又一次,可是过一段时间,又忘掉了。所以,这次就把算法是怎么推导出来的,详细记录下来。看看这次多久还会忘记。
本文主要介绍排序算法的分类、时间复杂度、空间复杂。为了后面的学习做准备的。
通过本文学习,将收获到:排序算法分几类?什么是算法的时间复杂度?是怎么算出来的?什么是算法的空间复杂度?常见的时间复杂度比较。
如果这些您都已经知道了,可以不用耽误时间看了。
约定:
文中的n2表示的是n的2次方(n²),n^2也是表示n的2次方;
n3表示的是n的3次方;
n^k表示的是n的k次方;
long2n表示的是以2为底的对数。
本文出自:凯哥java(微信:kaigejava)学习Java版数据结构与算法笔记。
一:介绍
排序又称排序算法(Sort Algorithm),排序是将一组数据,依据指定的顺序进行排序的过程。
二:分类
排序的分类分为两大类
2.1:内部排序
内部排序是指将需要处理的所有数据一次性都加载到内存中进行排序的。
如:冒泡、快排等这些算法都是内部排序的
2.2:外部排序
数据量过大,无法全部加载到内存中,需要借助于外部存储进行排序的。
如:数据库中数据8个G,内存只有4个G的这种。
2.3:参加分类如下图:
三:算法的时间复杂度
3.1:分类
衡量一个程序(算法)执行时间有两种方法
3.1.1:事后统计的方法
所谓的事后统计方法,顾名思义,就是程序(算法)已经写完了,运行后得到的结果。
这种方法虽然是可行的,但是有两个问题:
①:要想对设计的算法运行的性能进行评估,需要实际运行该程序(浪费时间);
②:运行所得的时间统计严重依赖于机器的硬件、软件等环境因为。
这种方法有个严苛的要求:要在同一台机器在相同状态(软硬件)下运行,才能比较哪个算法更快。
3.1.2:事前估算的方法
通过分析某个算法的时间复杂度来判断哪个算法更优。
3.2:时间频度
概念:一个算法花费的时间与算法中语句执行的次数成正比。哪个算法中语句执行次数多,那么这个算法所花费的时间就多(这不废话吗)。
一个算法中语句执行次数称为语句频度或时间频度。记为:T(n).
(复杂的概念是,时间频度:一个算法执行所消耗的时间,从理论上是 不能算出来的,想要具体数值,必须要将程序上机运行测试才能知道。但是我们不可能也没必要对每个算法都上机进行测试的,只需要知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句执行的次数成正比的,哪个算法中语句执行次数多,那么这个程序花费的时间就多。一个算法中的语句执行次数称为语句频度或者时间频度,即为:T(n))
例如:我们知道的技术从1到100所有数字的和。这个就有两种算法。分别如下:
①:使用for循环,从1到100循环出来,然后累加出来。代码如下:
根据上面概念(注意对概念的理解,total和end这两行相对于for循环来说,可以忽略。后面我们还会详细讲解还会忽略哪些),我们来看下这个算法的时间频度是多少呢?
在for循环中,实际需要执行101次(+1的原因是因为,在for循环的时候,需要做最后一次判断,才能推出。因此n个数的计算一共是n+1次操作)。所以其时间频度就是:T(n)=n+1;
我们再来看看第二种算法:
是不是很简单,只要一行代码就执行完成了。所以第二种算法的T(n)=1了。是不是很快呢?
时间频度是不是一眼就看出来了?是不是不用在代码运行下来比较运行时间了?
(ps:从上面简单地从1到100求和算法中,我们是不是感受到算法的魅力了?感受到编程之美了?)
3.3:时间复杂度
在上面3.2中提到的时间频度中,n称为问题的规模,当n不断变化的时候,时间频度T(n)也会不断变化。但是有时我们想知道它在变化的时候呈现什么样的规律呢?为此,我们引入了时间复杂度概念。
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示。若有某个辅助函数f(n),是的当n趋近于无穷大的时候,T(n)/f(n)的极限值为不等于零的参数,则称为f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n))为算法的渐进的时间复杂度。简称时间复杂度。这就是大O法。
在计算时间复杂度的时候,我们会忽略以下几个数据值
3.3.1:忽略常数项
比如上面,我们计算1到100的第一种算法中,有两行int total=0;和 int end = 100;这两行代码,这个数值是2,我们一般计算时间复杂度的时候,会忽略这个常数项的。为什么呢?请看下面四个函数,随着n的增大而增大运行时间。
T(n) = 2n+20
T(n) = 2*n
T(n)=3n+10
T(n)=3*n
请看下图随着n的增大所呈现的规律:
我们来看看,把这些数据使用折线图展示:
图例说明:上面两个是3*n及3n+10的,下面两个是2n及2n+10的
从上面两个图表中我们可以得到以下结论:
①:2n+20和2*n随着n的增加,执行曲线无限接近(折线图中下面两个),常量值20可以忽略了
②:3n+10和3*n随着n的增加,执行曲线无限接近(折线图中上面两个),常量值10可以忽略了
所以,综上所述,在计算程序(算法)时间复杂度的时候,常量值是可以忽略的
3.3.2:忽略低次项
请看下面四个函数,随着n的增大又会呈现什么规律吗?
T(n)=2n^2+3n+10
T(n)=2n^2
T(n)=n^2+5n+20
T(n)=n^2
说明:n^2表示n的2次方
我们来看看随着n的增加,运行所消耗的时间。如下图:
把上面数据,用折线图表示,如下图:
图例说明:上面两个是2n^2及2n^2+3n+10,下面两个是n^2及 n^2+5n+20
从上面两个图中我们可以得到如下结论:
①:2n^2+3n+10和2n^2随着n的增大,执行曲线无限接近,可以忽略低次项及常量项:3n+10
②:n^2+5n+20和n^2随着n的增大,执行曲线无限接近,可以忽略低次项及常量项:5n+20
综上所述,我们可以得到结论:在计算程序(算法)时间复杂度的时候,低次项(3n=3*n^1比n^2项数少)是可以忽略的
3.3.3:忽略系数
我们再来看看下面四个函数,看看它们随着n的增大呈现出什么样的规律
T(n)=3n^2+2n
T(n)=5n^2+7n
T(n)=n^3+5n
T(n)=6n^3+4n
随着n的增加,运行时间所消耗耗时如下图:
折线图如下:
从上图可以得到如下:
①:随着n值变大,5n^2+7n和3n^2+2n,执行曲线重合,说明这种情况下,系数5和3可以忽略;
②:n^3+5n和6n^3+4n,执行曲线分离,说明多少次方是关键
3.3.4:总结:
计算时间复杂度的时候忽略常数项、忽略低次项、忽略系数
T(n)不同,但时间复杂度可能相同。
如:T(n)=n2+7n+6与T(n)=3n^2+2n+2它们的T(n)不同,但时间复杂相同,都为O(n^2).
计算时间复杂度的方法用常数1代替运行时间中的所有加法常数T(n)=n^2+7n+6 =>T(n)=n^2+7n+1修改后的运行次数函数中,只保留最高阶项T(n)=n^2+7n+1 => T(n)=n^2去除最高阶项的系数T(n)=n^2 =>T(n)=n^2 => O(n^2)
3.4:常见的时间复杂度
常数阶O(1)
对数阶O(log2n)
线性阶O(n)
线性对数阶O(nlog2n)
平方阶O(n^2)
立方阶O(n^3)
K次方阶(n^k)
指数阶O(2^n)
各个时间复杂度复杂度折线图如下图:
总结:
常见算法时间复杂度由小到大依次为:
O(1)
从上图折线图中,我们可以看出,程序(算法)尽可能的避免使用指数阶段的算法。
3.5:常见算法时间复杂度举例
3.5.1:常数阶O(1)
无论代码执行多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就是O(1)
(计算时间复杂度的时候,忽略常数项)
代码demo:
上述代码在执行的时候,消耗的时间并不是随着某个变量的增长而增长,那么无论这类代码有多长,即使有有几万几十万行,都是可以用O(1)来表示它的时间复杂度。
3.5.2:对数阶O(log2n)
代码敬上:
说明:
在while循环里面,每次都是将i*2的。n的值是固定的,所以在i乘完之后,i距离n就越来越近了。假设循环x次之后,i就大于n了,此时这个循环就退出了。也就是说2的x次方等于n了。那么x=log2n。也就是说当循环了log2n次以后,代码就结束了。因此这个代码的时间复杂度就是
O(log2n)。
O(log2n)的这个2时间上是随着代码变化的。如果i = i*3,那么时间复杂度就是O(log3n)
回顾下log的理解(这是初中知识点):
如果a的x次方等于N(a>0,且a≠1),那么熟x就叫做以a为底的对数(logarithm),记作x=logaN.
其中,a叫做对数的底数,N叫做真数,x叫做“以a为底N的对数”。
3.5.3:线性阶O(n)
代码如下:
说明:
这段代码,for循环里面的代码会执行n次。因此它所消耗的时间随着n的变化而变化的,因此这类代码都是可以用O(n)来表示它的时间复杂度。
3.5.4:线性对数阶O(nlogn)
代码如下:
说明:
线性对数阶O(nlogN)其实非常容易理解的。将时间复杂度为O(logn)的代码循环了N次的话,那么它的时间复杂度就是n*O(logn),也就是O(nlogN)
3.5.5:平方阶O(n2)
代码:
说明:
平方阶O(n2)就容易理解了。如果把O(n)的代码再嵌套循环一遍,它的时间复杂度就是O(n2),
上图中的代码起始就是嵌套了2层n循环,它的时间复杂度就是O(n*n),即时O(n2)。如果将其中一层循环的n修改成m,那么它的时间复杂度就变成了O(m*n).
3.5.6:立方阶O(n3)、K次方阶O(n^k)
说明:参考上面的O(n2)去理解就好了。O(n3)起始就相当于是三层n循环了。其他的一次类推。
3.6:平均时间复杂度和最坏时间复杂度
平均时间复杂度:
是指所有可能的输入实例均以概率出现的情况下,该算法的运行时间
最坏时间复杂度:
是指在最坏情况下的时间复杂度称为最坏时间复杂度。一般讨论时间复杂度均是最坏情况下的时间复杂度。
这样做的原因:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的界限。这就保证了算法的运行时间不会比最坏情况更长了。
平均时间复杂度和最坏时间复杂度是否一致,和算法有关。具体如下图:
四:算法的空间复杂度
空间复杂度介绍
类似于时间复杂度的讨论。一个算法的空间复杂度(Space Complexity)定义为该算法所消耗的存储空间,它也是问题规模n的函数;
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。有的算法需要占用临时工作单元数与解决问题的规模n有关。它们随着n的增大而增大,当n较大的时候,将占用较多的存储单元(存储空间)。例如:在快排(快速排序)和归并排序算法就属于这种情况。
在做算法分析的时候,主要讨论的是时间的复杂度。因为从用户的使用体验上来看,更看重的是程序执行的速度的快慢。一般缓存产品(比如Redis)和技术排序算法本质就是拿空间换时间的。
下节预告:
下节我们将讲讲冒泡排序和选择排序。使用的是图解+代码一步一步推导出来演示的。欢迎大家一起学习
‘陆’ 一位出色的程序员的数学知识应该达到怎样的程度
离散数学对程序员来说非常重要,还有组合数学、线性代数、概率论、数论等等,即使你将来不做研究,这些基础知识也能极大地提高你的水平。计算机科学对离散数学的要求很高,建议你先学习前面提到的这些课程,然后学习计算机算法和数据结构,再配合到网上的在线题库做题,过程很艰辛,但是对你的帮助会很大。
推荐书目:
《具体数学》(先学完前面的数学课程,在水平有一定进步以后再看)
《算法导论》(应该人手一本的好书)
简单来说,学数学的目的,一方面是活跃你的思维;另一方面是为了深入学习算法打基础,设想一下,同样的问题,普通人的程序要几十分钟甚至几小时几天才能解决出来,甚至根本无法解决,而你精心设计的程序却能在1秒内解决出来,这就是数学的魅力、算法的魅力。
‘柒’ 正学C语言,不明白程序和算法有什么区别我理解的是算法是解决问题的步骤,可是看看程序似乎也是如此啊
有一句经典的话:程序=算法+数据结构。算法是解决问题的步骤,算法是一种思想。比如:计算1加到100的和,那么(1)你可以从1开始一个一个的加,直到100;(2)(1+100)*50;这两种解决问题的方法就是算法。而这种描述计算机不能理解,因此,可以用C语言或其它语言把它写出来,让它可以在计算机上运行,这就是一个程序。程序可以理解为对算法的一种包装,目的就是让它可以在计算机上能运行。
‘捌’ 常用的数据挖掘算法有哪几类
常用的数据挖掘算法分为以下几类:神经网络,遗传算法,回归算法,聚类分析算法,贝耶斯算法。
目前已经进入大数据的时代,所以数据挖掘和大数据分析的就业前景非常好,学好大数据分析和数据挖掘可以在各个领域中发挥自己的价值;同时,大数据分析并不是一蹴而就的事情,而是需要你日积月累的数据处理经验,不是会被轻易替代的。一家公司的各项工作,基本上都都用数据体现出来,一位高级的数据分析师职位通常是数据职能架构中领航者,拥有较高的分析和思辨能力,对于业务的理解到位,并且深度知晓公司的管理和商业行为,他可以负责一个子产品或模块级别的项目,带领团队来全面解决问题,把控手下数据分析师的工作质量。
想要了解更多有关数据挖掘算法的信息,可以了解一下CDA数据分析师的课程。课程教你学企业需要的敏捷算法建模能力,可以学到前沿且实用的技术,挖掘数据的魅力;教你用可落地、易操作的数据科学思维和技术模板构建出优秀模型,只教实用干货,以专精技术能力提升业务效果与效率。点击预约免费试听课。
‘玖’ 全世界最强的算法平台codeforces究竟有什么魅力
简单介绍一下codeforces这个网站,codeforces位于宇宙编程最强的毛国。据说最早是由俄罗斯的一群大学生维护的,它最大的特点就是代码和题解的公开。所有人都可以随意查看其它大牛的代码,可以说是非常具有开源精神了。
codeforces很大的特点就是题目兼容并蓄,什么难度等级的题目都可以找到。并且题目很有意思,往往思维陷阱比较多,也就是思维题比较多。对于数据结构以及算法的考察相对弱一些,更多的时候往往是告诉你用什么算法你也不知道怎么做……
codeforces另外一个很大的特点就是它有自己的上分系统,基本上每周会举办一到两次在线的算法比赛。一般的比赛时长是两个小时,只要注册账号就可以免费参加。我记得当年第一次参加比赛会获得一个初始分是1500,然后根据你在比赛当中的表现上分或者减分。由于参加的选手水平实力强度不一,所以它开设了好几个档次(div),不同层次的选手面对的题目难度也不一样,这样保证了大家都可以愉快地参赛。
codeforces在比赛的时候只会测试一小部分数据,真正的测试集会放到赛后进行测试。所以在比赛中测试通过的代码,只是通过了小数据验证,很有可能有隐藏的问题没被发现。当你通过了这道题之后,你就可以去查看其他通过人的代码,去分析它们有没有问题,如果发现了bug,可以构造一份数据hack掉他的提交。hack成功之后,你会获得分数的奖励。
你可以双击打开其他人的提交记录,去阅读他们的代码。到了比赛后期,能做的问题做的差不多了之后,就进入了紧张刺激的互相hack阶段。讲道理,这比只是单纯做题的竞赛要有趣多了。
以前我们acm集训队经常晚上一起打codeforces的比赛,有时候看到队友在一个房间里,还会互相关注一下近况,互相hack一把,不得不说现在怀念起来还是非常有意思的。
好了,关于codeforces网站就介绍到这里了,如果你也对算法感兴趣的话,不妨试着用一下它吧,相信你也会找到算法的乐趣。
‘拾’ 最近打算看算法导论,在如何看方面有什么好的建议
算法导论,不适合入门,建议有数据结构和高等数学基础再读
这书上面有些内容太难了,刚开始不适合全看,挑些自己能看懂的来学。
很适合算法初学者体会算法的魅力.这本书讲解的很全面.算法都用伪码实现.对编程语言要求不高.书的前几章是一些数学和概率基础和算法分析的一些说明.后面几章是一些算法的描述.对NP问题感兴趣的话.可以看看这本书的VII部分中的NP-Completeness.我觉得这本书比较好的一部分是它的附录部分.对前面的一些背景知识公式进行了详细的阐述和证明以及一些专有名词进行了索引方便检索.这本书在国内目前只有英文版的.但南大有个中文版的(不过他们太无耻了居然说是他们自己编着)我看了那个版本的.其实是第一版的中文翻译叫<现代计算机常用数据结构和算法>.其他的我想我不需要多说了.有兴趣的可以去体会一下,