大话数据结构系列之算法初探(二)

算法定义

算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作


算法的特性

输入与输出

  • 算法具有零个或多个输入
  • 算法至少有一个或多个输出

有穷性

指算法在执行有限的步骤之后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成

确定性

算法的每一步骤都具有确定的含义,不会出现二义性

可行性

算法的每一步都是可行的,也就是说,每一步骤都能够通过执行有限次数完成
(备注:特为解释,目前计算机界也存在那种没有实现的极为复杂的算法,只是限于当前的编程方法、工具和大脑的限制,只存在于理论中)


算法设计的要求

广义正确性

指算法的输入至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求、能够得到问题的正确答案

狭义正确性(递增)

  • 算法程序没有语法错误
  • 算法程序对于合法的输入数据能够产生满足要求的输出结果
  • 算法程序对于非法的输入数据能够得出满足规格说明的结果
  • 算法程序对于精心选择的,甚至刁难的测试数据都有满足要求的输出结果
    备注:一般以第三阶段作为依据标准

可读性

算法设计的另一目的是为了便于阅读、理解和交流
可读性高有助于人们理解算法、晦涩难懂的算法往往隐含错误,不易被发现,并且难于调试和修改

健壮性

当输入数据不合法时,算法也能做出相关处理,而不是产生异常或莫名其妙的结果

时间效率高和存储量低

尽量满足时间效率高和存储低的需求(在21世纪的一般情况下,都是通过空间来换时间)
人们都希望花最少的钱,用最短的时间,办最大的事儿,算法也一样。


算法效率的度量方法

事后统计法

定义:主要通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低

缺陷:

  • 时间的比较依赖计算机硬件和软件等环境因素,有时会掩盖算法本身的优劣
    举例:现在一台四核处理器的计算机,跟当年的286,等机器相比,在处理算法的运算速度上,是不能相提并论的
  • 所用的操作系统、编译器、运行框架等软件的不同
  • 同一台机器,CPU使用率和内存占用情况不一样
  • 算法的测试数据设计困难,并且程序的运行时间往往与测试数据的规模有很大关系,我们以什么规模的测试数据进行比较算法的优劣亦是问题

结论:基于以上缺陷,我们不予采纳。
原文写于 2011-6,随着时代的进步,之前不好实现的点,现在可能已经实现。且现阶段的很多项目在上线前需要预估该程序的伸缩性, 顾补充以下方法可以作为相应借鉴

JOL
1、官网:–http://openjdk.java.net/projects/code-tools/jol/
定位:分析对象在JVM的大小和分布

2、对比:
人工可以按照Java基础数据类型大小及内容大小估算出缓存对象的大概堆占用,但是麻烦还不准。
OpenJDK,提供了JOL包,可以帮我们在运行时计算某个对象的大小,是非常好的工具

JJMH(Java基准测试工具套件)

参考资料:–http://irfen.me/java-jmh-simple-microbenchmark/

1、什么是JMH
MH 是 Java Microbenchmark Harness 的缩写。中文意思大致是 “JAVA 微基准测试套件”。基准测试是指通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量的和可对比的测试。

2、为什么要用JMH
在实际开发中,大多都是用main自己写一个demo进行测试,但是这种测试方式是不科学的,尤其是在对比方法的效率时会存在很大的误差,和当时的运行环境和执行条件有很大的影响。而使用JMH就可避免此类问题,JMH可以设置预执行,预执行的结果不记录最终结果,只是为了运行环境更稳当,然后再执行n次取其平均时间,保证了测试结果的准确性。

3、JMH典型的运用场景
想准确的知道某个方法需要执行多长时间,以及执行时间和输入之间的相关性;
对比接口不同实现在给定条件下的吞吐量;
查看多少百分比的请求在多长时间内完成;

待实践:非常好用的计算耗时的工具类—— org.apache.commons.lang.time.DurationFormatUtils.java
待补充:实际使用方式后续提供

事前分析估算方法

定义:在计算机程序编制前,依据统计方法对算法进行估算
影响因素
1、算法采用的策略、方法
2、编译产生的代码质量
3、问题的输入规模
4、机器执行指令的速度

分析
抛开计算机硬件、软件有关的因素,一个程序的运行时间,依赖于 算法的好坏问题的输入规模,具体推导请继续往下。

函数的渐进增长
给定两个函数 f(n) 和 g(n),如果存在一个整数 N,使得对于所有的 n > N,f(n) 总是比 g(n) 大,那么,我们说 f(n) 的增长渐进快于 g(n)。
人话
某个算法,随着 n 的增大,它会越来越优于另一算法,或者越来越差于另一算法。
备注:一般使用渐进增长概念为标准,来判断算法的优劣性


算法时间复杂度

定义
在进行算法分析时,语句总的执行次数 T(n) 是关于问题规模 n 的函数,进而分析 T(n) 随 n 的变化情况并确定 T(n) 的数量级。算法的时间复杂度,也就是算法的时间亮度,记作:T(n) = O(f(n))。它表示随问题规模 n 的增大,算法执行时间的增长率和 f(n) 的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中 f(n) 是问题规模 n 的某个函数。

曲线图
在这里插入图片描述

推导大O阶方法

  • 用常数 1 取代运行时间中的所有加法常数。
  • 在修改后的运行次数函数中,只保留最高阶项。
  • 如果最高阶项存在且不是 1,则去除与这个项相乘的常数。
  • 得到的结果就是大 O 阶(具体方法见网上众多基础推导)

相关定义
对数:
如果a的x次方等于N(a>0,且a不等于1),那么数x叫做以a为底N的对数(logarithm),记作x=logaN。其中,a叫做对数的底数,N叫做真数。
二分法的对数推导

等差数列:
等差数列是指从第二项起,每一项与它的前一项的差等于同一个常数的一种数列,常用A、P表示。这个常数叫做等差数列的公差,公差常用字母d表示。 [1]
例如:1,3,5,7,9……2n-1。通项公式为:an=a1+(n-1)d。首项a1=1,公差d=2。前n项和公式为:Sn=a1n+[n*(n-1)d]/2或Sn=[n(a1+an)]/2。注意:以上n均属于正整数。


常见的时间复杂度

展示:O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)//2的n方<O(n!)<O(n^n)//n的n方

分析

  • O(logn)<O(n)
    再比如O(logn),当数据增大n倍时,耗时增大logn倍(这里的log是以2为底的,比如,当数据增大256倍时,耗时只增大8倍,是比线性还要低的时间复杂度)。
    二分查找 O(logn)的算法,每找一次排除一半的可能,256个数据中查找只要找8次就可以找到目标。

  • O(n)<O(nlogn)
    O(nlogn)同理,就是n乘以logn,当数据增大256倍时,耗时增大256*8=2048倍。这个复杂度高于线性低于平方。归并排序就是O(nlogn)的时间复杂度。

  • O(n)<O(n^2)
    再比如时间复杂度O(n^2),就代表数据量增大n倍时,耗时增大n的平方倍,这是比线性更高的时间复杂度。
    比如冒泡排序,就是典型的O(n^2)的算法,对n个数排序,需要扫描n×n次。

  • O(n!)的含义待定


最坏情况与平均情况

最坏情况:是运行时间的一种保证,就是运行时间不会再坏了。

平均情况:是所有情况中最有意义的,因为它是期望的运行时间

备注:在计算时间复杂度时,一般都指最坏情况


算法空间复杂度

算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记作:S(n) = O(f(n)),其中,n 为问题的规模,f(n) 为语句关于 n 所占存储空间的函数。
我们在写代码时,完全可以用空间来换取时间

发布了222 篇原创文章 · 获赞 54 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/weixin_39966065/article/details/103990829