算法从入门到“放弃”(1)- 什么是算法?

举报
gentle_zhou 发表于 2023/12/27 22:58:41 2023/12/27
【摘要】 更有效率地解决问题的计算方法。面对一个问题,先想出所有的解决方法,在其中找出一个最有效最简洁的方法的过程就是算法。

写在开头

是什么让我萌生了写这系列文章的动力和想法呢?在大学课堂里学过入门算法,算法进阶等课程,还没接触前,就异常的兴奋,早就听说“算法”这种神秘的计算方法了,现在学校里教岂不美哉;等到学的时候,教授在上面讲,听得津津有味,感觉什么都懂了,回去自己写概念,实战的时候,又焉了;考试前,突击复习死记硬背了一遍,拿了个A,考完后,又基本都忘了。。。。
所以,面对这日后肯定经常使用的“工具”,不彻底理解肯定是不行的。唯有系统地全部梳理一遍才是良策。所以就有了写这系列文章的想法。
本系列文章都是自己在学习Thomas H. C 等作者所写的Introduction to Algorithms 第三版书,结合网上自己查阅的资料,所写下的一些感悟和想法。初入算法大门,希望大家不吝赐教。

image.png

什么是算法?

以下内容摘自维基百科,算法,Algorithm。

在数学和计算机科学中,一种算法是一个明确的关于如何解决一类问题的规范。算法可以执行计算,数据处理和自动推理任务。

作为一种有效的方法,算法可以在有限的空间和时间中表示,并在一个定义良好的正式语言中用于计算一个函数。从初始状态和初始输入开始(可能是空的),指令描述了一个计算,当执行时,通过一个有限的定义良好的连续状态,最终产生“输出”并在最终结束状态终止。从一个状态到另一个状态的转换并不一定是确定的; 一些算法,被称为随机算法,包含随机输入。

算法的概念已经存在了几个世纪,这个概念的使用可以归因于希腊数学家,例如Eratosthenes和欧几里德算法的筛子; 算法这个术语本身来自于9世纪的数学家muammad ibn msal’khwrizm。在1928年,大卫希尔伯特提出的解决“Entscheidungsproblem”(“决策问题”)的尝试,开始了对现代算法概念的部分形式化。随后的形式被框定为试图定义“有效的可计算性”或“有效方法”; 这些形式化包括了1930年、1934年和1935年的克莱伦递归函数、1936年的阿隆佐教堂的lambda微积分、1936年埃米尔波斯特的公式1和1936年的图灵机,以及1936年和1939年的图灵机。

自己的一些见解

就是更有效率地解决问题的计算方法。面对一个问题,先想出所有的解决方法,在其中找出一个最有效最简洁的方法的过程就是算法。

还记得课堂里有人问教授什么是算法,教授最后的解释就是一张图。

image.png
就是一个程序员不想解释他们做了什么的时候使用的名词 (笑)

本系列文章的前期准备

1. 如何评价算法的好坏?

最简单粗暴的方法,这个算法在限制的条件下是否解决了问题?
是,这是个可行的“好”算法,
不是,这是个不可行的差算法。
在这些可行的“好”算法中,是否有相对来说更好的?这就涉及到了比较,比较哪些东西呢?

平均时间复杂度;平均空间复杂度;最坏时间复杂度;最坏空间复杂度
输入与时间开销的分布关系;特定输入下的时间空间开销
所用的资源开销(如并行处理);算法运行的特殊要求(如硬件支持)
通过这些东西来综合判断,不能一概而论。

———— 引用自在网上看到的@黄磊 在怎么评价一个算法的效率?问题中的问答。

我们这里仅考虑理论方面的时间复杂度。(空间复杂度很多时候不怎么关注,因为现在计算机硬件发展得很,算法中的数据存储基本是没问题的;很多时候我们甚至会“牺牲空间来换取时间”)

2. 引入渐近符号 asymptotic notation

那么如何比较时间复杂度呢?
Introduction to Algorithms 第三版书中引入了渐近符号这个概念。

We will use asymptotic notation primarily to describe the running times of algorithms. It actually applies to functions.

我们将使用渐近符号来主要表达算法的运行时间,这是运用在其中函数上的。
我们先假设有一个函数 g(n)。

2.1 Ө 符号

theta 符号
在这我们用Ө(g(n)) 来表示一组函数:Ө(g(n)) = {f(n): 任何大于0的常数 c1, c2, 和 n0 满足 0<=c1g(n)<=f(n)<=c2g(n),对于所有的 n>=n0}.
我们说g(n) 是f(n) 的渐近紧范围 (asymptotically tight bound)。
因为Ө(g(n)) 的定义要求每个符合Ө(g(n)) 要求的f(n) 得是渐近不是负数的 (asymptotically non-negative),也就是说,无论n 有多大,f(n) 都不会是负数。
函数g(n) 本身必须是渐近不是负数 (asymptotically non-negative) 或则集合Ө(g(n)) 是空集。

2.2 O 符号

O 大写符号
在这我们用O(g(n)) 来表示一组函数:
O(g(n)) = {f(n): 任何大于0的常数 c 和 n0 满足 0<=f(n)<=cg(n),对于所有的 n>=n0}.
我们说g(n) 是f(n) 的渐近上范围 (asymptotically upper bound)。
注意:f(n)=Ө(g(n)) 说明了f(n)=O(g(n)) ,因为Ө 符号是个比O 符号强的符号;所以,我们可以说Ө(g(n)) ⊆ O(g(n)),Ө(g(n))是O(g(n)) 的子集。

2.3 Ω 符号

omega 大写符号
在这我们用Ω(g(n)) 来表示一组函数:
Ω(g(n)) = {f(n): 任何大于0的常数 c 和 n0 满足 0<=cg(n)<=f(n),对于所有的 n>=n0}.
我们说g(n) 是f(n) 的渐近下范围 (asymptotically lower bound)。
这里说个理论,如果对任何两个函数f(n) 和 g(n),仅且仅只有当f(n)=O(g(n)) 和f(n)=Ω(g(n)),我们会有Ө(g(n))。

2.4 o 符号

o 小写符号
在这我们用o(g(n)) 来表示一组函数:
o(g(n)) = {f(n): 任何大于0的常数 c,存在大于0的 n0 满足 0<=f(n)<cg(n),对于所有的 n>=n0}.
我们说g(n) 是f(n) 的上范围但不是渐近紧 (upper bound that is not asymptotically tight)。
而且,在o 符号中,当n 越来越接近无穷大,函数f(n) 和g(n) 的关系会变得越来越无关紧要;也就是说,lim x → ∞ (f(n)/g(n)) = 0.

2.5 ω 符号

omega 小写符号
在这我们用ω(g(n)) 来表示一组函数:
ω(g(n)) = {f(n): 任何大于0的常数 c,存在大于0的 n0 满足 0<=cg(n)<f(n),对于所有的 n>=n0}.
我们说g(n) 是f(n) 的下范围但不是渐近紧 (lower bound that is not asymptotically tight)。
而且,在o 符号中,当n 越来越接近无穷大,函数f(n) 和g(n) 的关系会变得越来越重要;也就是说,lim x → ∞ (f(n)/g(n)) = ∞.

3. 测试算法时间的程序

下面这段伪代码是以纳秒为单位计算的,可以在实际中相对准确地验证我们的理论时间。

// Java代码,测试程序运行的时间
// 伪代码 
long startTime=System.nanoTime();   // 获取开始的时间 
doSth();  // 算法内容 
long endTime=System.nanoTime(); // 获取结束的时间 
System.out.println("Running time of algorithm: " + (end-start) + "ns"); 

在下一篇文章中,将着重讲的是算法的重要基本内容,分而治之(divide-and-conquer)和3种解决递归问题的方法。

文章来源

来自2018年写的系列文章:https://zhuanlan.zhihu.com/p/40151536。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。