这是一个很普通的小例子,但是可以让我们领略到算法改进之后的强大魅力。
已知a+b+c = 1000,且a^2+b^2=c^2,求a、b、c的所有自然数解。
这个很简单,就是通过代码分别给a、b、c赋值,然后返回符合a+b+c = 1000且a^2+b^2=c^2的解。
这里有两段参考代码如下:
第一段就是按照一般的试错逻辑,分别遍历1~1000赋值给a、b、c,然后通过条件if a + b + c == 1000 and a**2 + b**2 == c**2取出符合的三个值。
# 代码1:
import time
start_time = time.time()
for a in range(1,1001):for b in range(1,1001):for c in range(1,1001):if a + b + c == 1000 and a**2 + b**2 == c**2:print('a:%d, b:%d, c:%d'%(a,b,c))
end_time = time.time()
print('程序总用时:%f'%(end_time-start_time))
代码1的执行结果如下图,本次的执行时间是951.8秒。

再来看看代码2,在代码1的基础上做了一个小改动,只遍历两次1~1000,第三个数通过1000减去前两个数得到,代码如下:
# 代码2:
import time
start_time = time.time()
for a in range(1,1001):for b in range(1,1001):if a**2 + b**2 == (1000-a-b)**2:print('a:%d, b:%d, c:%d'%(a,b,1000-a-b))
end_time = time.time()
print('程序总用时:%f'%(end_time-start_time))
代码2的执行结果如下图,可以看到本次执行该代码只需要1.4秒。

从上面两段代码的执行结果,我们可以看到二者的时间相差700倍左右!用代码2替换代码1,每次执行少等待16分钟左右,这是一种特别好的体验。
感受到算法的小小魅力之后,接下来简单讲讲算法的复杂度问题。
复杂度一般分为两种:时间复杂度和空间复杂度。
时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间,在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度,所以我们如今已经不需要再特别关注一个算法的空间复杂度,更多关注其时间复杂度。
时间复杂度,也是渐进时间复杂度(asymptotic time complexity),官方的定义如下: 计算时间复杂度时,理论上需要考虑到所有需要消耗时间的运行操作,比如说遍历一个元素都是长字符串的列表找到相等的字符串,除了考虑遍历字符串的执行时间,还需要考虑字符串的对比(如下例子)。 一般基础代码,不带任何调用其他对象的代码是常数项,有几行就算执行几次;遍历循环则根据序列的长度定义,假设序列长度为 不过,在实际应用过程,一般会简化时间复杂度,更多时候只是是看影响最大的因素。 在比较时间复杂度时通常会采用以下几个方法进行简化之后对比复杂度: 如:logn+nlognlogn+nlognlogn+nlogn表示为O(nlogn)O(nlogn)O(nlogn)。 比较时间复杂度的时候,一般会忽略常数项、低阶项、高阶项的系数和log的底数。比如说O(2n+1)O(2n+1)O(2n+1)简化为O(n) ,O(2∗n3+5n2+5)O(2*n^3+5n^2+5)O(2∗n3+5n2+5)简化为O(n3)O(n^3)O(n3),O(log2n)O(log_2n)O(log2n)简化为O(logn)O(logn)O(logn)。文章开头的两串代码的复杂度则分别是O(n2)O(n^2)O(n2)和O(n3)O(n^3)O(n3)。 2个问题: 2、为什么O(logn)不区分对数的底数? 1、一般地,在比较时间复杂度时会采用以下几个方法进行简化再对比: 2、复杂度越小,则说明你的代码越好,复杂度的大小参考如下:
若存在函数f(n),使得当n趋近于无穷大时,T(n)/f(n)T(n)/f(n)T(n)/f(n) 的极限值为不等于零的常数,则称f(n)f(n)f(n)是T(n)T(n)T(n)的同数量级函数。记作T(n)=O(f(n))T(n)= O(f(n))T(n)=O(f(n)),称O(f(n))O(f(n))O(f(n))为算法的渐进时间复杂度,简称时间复杂度。
渐进时间复杂度用大写O来表示,所以也被称为大O表示法。,常见表示方法如O(1)O(1)O(1)、O(logn)O(logn)O(logn)、O(n)O(n)O(n)、O(nlogn)O(nlogn)O(nlogn)、O(n2)O(n^2)O(n2)、O(n3)O(n^3)O(n3)、O(2n)O(2^n)O(2n)等。
一般情况下,复杂度越小,则说明你的代码越好,复杂度的大小参考如下:
O(1)for i in ['abc','bcd','cde']:if i == 'cde':print(i)
n,遍历1次序列则为n,并列遍历2次则为2∗n2*n2∗n,嵌套遍历2次则为n2n^2n2。2.2 时间复杂度的简化
O(1)表示,如O(23)O(23)O(23),O(9999)O(9999)O(9999);O(logn)表示,如:log5nlog_5 nlog5n、log2nlog_2nlog2n;
1、为什么忽略常数项、低阶项和系数?2.3 常见函数的复杂度
常见函数 Big-O [n], index O(1) append() O(1) pop() O(n) insert() O(n) del O(n) iteration O(n) contain() O(n) [m,n] slice O(m) del slice O(n) set slice O(n+m) reverse O(n) concaterate O(m) sort O(n log n) multiply O(n*m) copy O(n) get items O(1) set items O(1) del items O(1) contains(n) O(1) iteration O(n) 三、小结
O(1)表示,如O(23)O(23)O(23),O(9999)O(9999)O(9999);O(logn)表示,如:log5nlog_5 nlog5n、log2nlog_2nlog2n;
O(1)
上一篇:@Accessors 注解作用
下一篇:Java“白皮书”的关键术语