文章目录
本身也不是专门做算法的,也觉得算法在于开阔思维以及增加对解决问题有多一份的思路,根据自己的理解去做的笔记,会有一些原文,还会有自己的一部分理解,未必是对的,即使书中是对的,如果不能理解提出来也会大有裨益。尽信书不如无书,信书也要理解书。这里的内容多根据 算法图解而来。
一、 算法简介
大部分的基础算法性概念在上一篇学习我的第一本算法书已经有过描述,因此这里对基础性概念不做过多描述。一般而言,应选择效率最高的算法,以最大限度地减少运行时间或占用空间。对于有序排列的数据进行数据查找时,如果换言之,最多需要猜测的次数与列表长度相同,这被称为线性时间;二分查找法的运行时间为对数时间。
1.1 大O运行时间表示法
1.1.1 运行时间的增速
线性时间和对数时间不同,简单查找和二分查找的运行时间的增速不同,随着元素的数量增多,两者的所需的额外时间差异很大。大O表示法计算的是操作数。比如 O(n)+O(1)的情况,会留下大的舍弃小的;一方面是最糟糕情况的取值,另一方面是取最大影响,舍去小的影响;因此上面的的公司得出的时间复杂度为O(N)。
1.1.2 常见的大O运行时间
-
O(log n),也叫对数时间,这样的算法包括二分查找。
-
O(n),也叫线性时间,这样的算法包括简单查找。
-
O(n*logn),这样的算法包括第4章将介绍的快速排序——一种速度较快的排序算法。
-
O(n2),这样的算法包括第2章将介绍的选择排序——一种速度较慢的排序算法。
-
O(n! ),这样的算法包括接下来将介绍的旅行商问题的解决方案——一种非常慢的算法。
O(n!)是一种非常耗时的算法,主要解决计算机科学领域著名的旅行商问题,非常聪明的人也认为没有改进空间,详细介绍可见参考文献一【1】
1.2 选择排序
数组是重要的概念,存储数据的概念类似于内存,计算机就像是很多抽屉的集合体,每个抽屉都有地址。商场门口的寄存柜,丰巢柜都能形象的用来类比内存,柜子的编号就相当于内存地址,我们在取存储物时是把小票上的条码信息进行扫描,然后就能打开对应的柜子,取出数据。
1.2.1 数组和链表的对比
数组和链表具体的详细概念与图示可参见算法基础–《我的第一本算法书》–(算法学习一) ,在链表中添加元素很容易:只需将其放入内存,并将其地址存储到前一个元素中。缺点是读取链表的最后一个元素时,不能直接读取,因为链表没有地址概念,必须逐次访问,直到访问最后一个元素。需要同时读取所有元素时,链表的效率很高:你读取第一个元素,根据其中的地址再读取第二个元素,以此类推。但如果你需要跳跃,链表的效率真的很低。
数组根据地址直接查找数据,查找的效率就很高了。可以根据插入读取操作的比例以及随机访问顺序访问比例确定使用哪种数据结构。
1.3 递归
递归算法实现,一定要有 ”归“ 的步骤。也就是说递归算法,在分解问题到不能再分解的步骤时,要让递归有退出的条件,否则就会陷入死循环,最终导致内存不足引发栈溢出异常。
每个递归函数都有两部分:基线条件(base case)和递归条件(recursive case)。递归条件指的是函数调用自己,而基线条件则指的是函数不再调用自己,从而避免形成无限循环。
1.4 快速排序
分而治之(divide and conquer, D&C)——一种著名的递归式问题解决方法。D&C的工作原理:
(1) 找出简单的基线条件;
(2) 确定如何缩小问题的规模,使其符合基线条件。
1.4.1 平均情况和最糟情况
有时候,常量的影响可能很大,对快速查找和合并查找来说就是如此。快速查找的常量比合并查找小,因此如果它们的运行时间都为O(nlogn),快速查找的速度将更快,相同复杂度的时候,常量就起作用了,不同复杂度的时候,常量作用很小,可以忽略不计。
1.5 散列表
对于数组,如果我们知道要查找数据的地址,再去寻找对应数据的时间复杂度为O(1),可是如果并不知道数据的地址的话,就不能够快速找出数据,时间复杂度就会相应提升。
散列表和散列函数对应的是哈希表和哈希函数。【2】
当我们访问网站时,网址都必须转换为IP地址,网址长度格式不统一,但对应的IP地址格式是一致的,这个过程被称为DNS解析(DNS resolution),散列表可以提供这种功能。
缓存就是将常用的数据存储起来,这些数据的平时访问量大,访问频繁。这就相当于让机器人去做重复性,劳动力强的工作,解放生产力,让人去出来高精尖的工作。
对于散列冲突,计算到了相同的地址,其他常用方法之一:双重散列(Double Hashing)。意思就是不仅使用一个散列函数,而使用一组散列函数hash1(key), hash2(key), hash3(key)等等。先用第一个散列函数, 如果计算得到的存储位置已经被占用,再用第二个散列函数,依次类推,直到找到空闲的存储位置为止。
散列函数需要的三个必要特征,相同的输入产生相同的输出,不同的输入尽可能产生不同输出(冲突率低),越相似的输入产生的输出差异越大,散列函数的结果必须是均匀分布的,这很重要。
1.6 最短路径问题
查找最短路径问题一般有广度优先算法和深度优先算法。具体的原理有过解释。这本书有更生动的解释,在你的人际关系中,你打算寻找一个芒果经销商,搜索朋友中是否有芒果经销商,没有的话再去搜索朋友的朋友有没有芒果经销商,朋友是一度关系,朋友的朋友是二度关系。六度人脉关系理论【3】(Six Degrees of Separation),是指地球上所有的人都可以通过六层以内的熟人链和任何其他人联系起来。通俗地讲:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过六个人你就能够认识任何一个陌生人。”
广度优先搜索:如果在你看来,一度关系胜过二度关系,二度关系胜过三度关系,以此类推。因此,应先在一度关系中搜索,确定其中没有芒果销售商后,才在二度关系中搜索。广度优先搜索就是这样做的。
要计算非加权图中的最短路径,可使用广度优先搜索。要计算加权图中的最短路径,可使用狄克斯特拉算法。狄克斯特拉算法参见参考文献四。
二、贪婪算法
用专业术语说,就是你每步都选择局部最优解,最终得到的就是全局最优解。局部最优解可以逼近全局最优解,但是并不绝对。优势是快,缺点是可能有预判,局部最优解逼近全局最优解,属于近似算法。有些问题整体考虑似乎很简单,但实现的复杂度却很高,可能可以考虑使用贪心算法来逼近全局最优解。此时贪婪算法正好可派上用场,因为它们实现起来很容易,得到的结果又与正确结果相当接近。但仅当每个子问题都是离散的,即不依赖于其他子问题时,动态规划才管用。
三、K最近邻算法(K-nearest neighbours)
特征提取确定用户的相似程度,根据参数为每一个数据确定一组坐标值,k-means算法两大功能分类和回归,分类就是以已知数据特征距离计算未知数据的相似度,距离越小越相似,回归就是预测,以已知分类数据的平均值预测分类后数据的近似值。对提取出的特征进行数值化,通过n维空间的欧氏距离或余弦距离或其它距离对指定点和已有点的距离进行计算,找到k近邻进行分类。根据k近邻的待预测字段均值对指定点的结果进行回归。
分类:编组
回归:预测结果
3.1 一些机器学习的概念,knn的应用
OCR:光学字符识别,拍摄印刷页面的图片,自动识别出其中的文字。
特征提取步骤:(1)浏览大量数字图像,提取特征。
(2)遇到新图像,提取图像特征,找到最近的数字。一般OCR算法提取线段、点和曲线等特征。