算法基础
问题求解的关键
建模:
对输入参数和解给出形式化或半形式化的描述
设计算法:
采用什么算法设计技术
正确性--是否对所有的实例都得到正确的解
分析算法--效率
算法+数据结构=程序
好的算法
提高求解问题的效率
节省存储空间
算法的研究目标
问题->建模并寻找算法 算法设计技术
算法->算法的评价 算法分析方法
算法类-> 问题复杂度估计 问题复杂度分析
问题类-> 能够求解的边界 计算复杂性理论
算法
有限条指令的序列
这个指令序列确定了解决某个问题的一系列运算或操作
算法A解问题P
把问题P的任何势力作为算法A的输入
每步计算是确定性的
A能够在有限步停机输出该实例的正确的解
算法时间复杂度:针对指定基本运算,计数算法所做运算次数
基本运算:比较,加法,乘法,置指针,交换。。。。
输入规模:输入串编码的长度。 比如:数组元素多少,调度问题的任务个数
算法基本运算次数可表为输入规模的函数
给定问题和基本运算就决定了一个算法类
算法的两种时间复杂度
最坏情况下的时间复杂度W(n)
平均情况下的时间复杂度A(n)
分治策略的基本思想
分治策略
将原始问题划分或者归结为规模较小的子问题
递归或迭代求解每个子问题
将子问题的解综合得到原问题的解
注意:
子问题与原始问题性质完全一样
子问题之间可彼此独立地求解
递归停止时子问题可直接求解
分治算法的特点:
将元问题归约为规模小的子问题,子问题与原问题具有相同的性质;
子问题规模足够小时可直接求解;
算法可以递归也可以迭代实现;
算法的分析方法:递推方程
分治算法设计要点
原问题可以划分或者归约为规模较小的子问题
子问题与原问题具有相同的性质;
子问题的求解彼此独立
划分时子问题的规模尽可能均衡
子问题规模足够小时可直接求解
子问题的解综合得到原问题的解
算法实现:递归或迭代
分治算法之快速排序
基本思想
用首元素x作划分标准,将输入数组A划分成不超过x的元素构成的数组AL,大于x的元素构成的数组AR,其中AL,AR从左到右存放在数组A的位置。
递归地对子问题AL和AR进行排序,直到子问题规模为1时停止
伪码
算法 Quicksort(A,p,r)
输入: 数组A[P..r]
输出: 排好序的数组A
1. if p<r
2. then q <- Partition(A,p,r)
3. A[p] <-> A[q]
4. Quicksort(A,p,q-1)
5. Quicksort(A,q+1,r)
划分过程:
Partition(A,p,r)
1. x ← A[p]
2. i ← p
3. j ← r+1
4. while true do
5. repeat j ← j-1
6. until A[j] <=x //不超过首元素
7. repeat i ← i+1
8. until A[i] > x //比首元素大的
9. if i<j
10. then A[i] ←→ A[j]
11. else return j
时间复杂度
最坏情况:
W(n)=W(n-1)+n-1
W(1)=0
W(n)=n(n-1)/2最好划分
T(n)=2T(n/2)+n-1
T(1)=0
T(n)=θ(nlogn)
小结
快速排序算法
分治策略
子问题划分时由首元素决定
最坏情况下时间O(n的2次方)
平均情况下时间为O(nlogn)
幂乘算法及应用
幂乘问题
输入: a为给定实数,n为自然数
输出:a的n次方
传统算法:顺序相乘
a的n次方=(….(((a a)a)a)..)a
乘法次数:θ(n)
分治算法——划分
分治算法分析
以乘法作为基本运算
子问题规模,不超过n/2
两个规模近似n/2的子问题完全一样,只要计算1次
W(n)=W(n/2)+θ(1)
W(n)=θ(log n)
幂乘算法的应用
Fibonacci数列:1,1,2,3,5,8,13,21,….
增加F0=0,得到数列:0,1,1,2,3,5,8,13,21….
问题:已知F0=0,F1=1,给定n,计算Fn
通常算法:从F0,F1,….开始,根据递推公式:F(n)=F(n-1)+F(n-2)
陆续相加可得Fn,时间复杂度为θ(n)
Fibonacci数的性质
算法:
令矩阵 ,用乘幂算法计算
时间复杂度:
矩阵乘法次数 T(n)=θ(log n)
每次矩阵乘法需要8次元素相乘
总计元素相乘次数为θ(log n)
改进分治算法的途径1:减少子问题数
减少子问题个数的依据
分治算法的时间复杂度方程
W(n)=a*W(n/b)+d(n)
a:子问题数,n/b:子问题规模,
d(n):划分与综合工作量
当a较大,b较小,d(n)不大时,方程的解:
减少a是降低函数W(n)的阶的途径
利用子问题的依赖关系,使某些子问题的解通过组合其他字问题的解而得到
例子:矩阵相乘的问题
矩阵乘法的研究及应用
矩阵乘法问题的难度:
Coppersmith-Winograd 算法: 目前为止最好的上界
目前最好的下界:
应用科学计算、图像处理、数据挖掘等
回归、聚类、主成分分析、决策树等挖掘算法常涉及大规模矩阵运算
改进途径小结
适用于:子问题个数多,划分和综合工作量不太大,时间复杂度函数
利用自问题依赖关系,用某些子问题解的代数表达式表示另一些子问题的解,减少独立计算子问题个数。
综合解的工作量不影响W(n)的阶。
改进分治算法的途径2:增加预处理
例子:平面点对问题
输入:平面点集P中有n个点,n>1
输出:P中的两个点,其距离最小
蛮力算法:
C(n,2)个点对,计算最小距离,O(n的2次方)
分治策略:P划分为大小相等的PL和PR
算法伪码:
算法分析:
增加预处理:
原算法:
在每次划分时对子问题数组重新排序
改进算法:
在递归前对X,Y排序,作为预处理
划分时对排序的数组X,Y进行拆分,得到针对子问题PL的数组XL,YL及针对子问题PR的数组XR,YR
原问题规模为n,拆分的时间为O(n)
改进算法时间复杂度
改进分治算法的途径:小结
依据
W(n)=a*W(n/b)+f(n)
提高算法效率的方法:
减少子问题个数a:
增加预处理,减少f(n)
分治算法典型应用:
选第二大
输入:n个数的数组L
输出:第二大的数 second
通常算法:顺序比较
顺序比较找到最大max
从剩下n-1个数中找最大,就是第二大second
时间复杂度:W(n)=n-1+n-2=2n-3
提高效率的途径
成为第二大数的条件:仅在与最大数的比较中被淘汰
要确定第二大数,必须找到最大数
在确定最大数的过程中记录下被最大数直接淘汰的元素
在上述范围(被最大数直接淘汰的数)内的最大数就是第二大数
设计思想:用空间换时间
锦标赛算法
两两分组比较,大者进入下一轮,知道剩下1个元素max为止。
在每次比较中淘汰较小元素,将被淘汰元素记录在淘汰它的元素的链表上
检查max的链表,从中知道最大元素,即second
伪码
算法 FindSecond
输入:n个数的数组L,输出:second
k <- n //参与淘汰的元素数
将k个元素两两1组,分成[k/2]组
每组的2个数比较,找到较大数
将被淘汰数记入较大数的链表
=======一轮淘汰结束==============if k 为奇数 then k <- [k/2]+1
else k <- [k/2]
if k>1 then geto 2 //继续分组淘汰
max <- 最大数
second <- max 的链表中的最大
实例
时间复杂度分析
第一阶段元素数:n
比较次数:n-1
淘汰了n-1个元素
第二阶段:元素数[log n]
比较次数:[log n]-1
淘汰元素数为[log n]-1
时间复杂度是
W(n)=n-1+[log n]-1=n+[log n]-2
小结
小结:分治算法设计
将元问题归约为子问题:
直接划分注意尽量均衡
通过计算归约为特殊的子问题
子问题与原问题具有相同的性质
子问题之间独立计算
算法实现:
递归或迭代实现
注意递归执行的边界
小结:分治算法的分析及改进
时间复杂度分析:
给出关于时间复杂度函数的递推方程和初始值
求解方程
提高效率的途径:
减少子问题个数
预处理
小结: 重要的分治算法
检索算法:二分检索
排序算法:快速排序、二分归并排序
选择算法: 选最大与选最小、选第二大
快速傅里叶变换FFT算法
平面点集的凸包
更多关于hadoop,spark和机器学习文章请关注本文公众号: