算法
- 计算 = 信息处理
借助某种工具,遵照一定规则,以明确而机械的形式进行 - 所谓算法,即特定计算机模型下,旨在解决特定问题的指令序列
- 输入 待处理的信息(问题)
- 输出 经处理的信息(答案)
- 正确性 的确可以解决指定的问题
- 确定性 任一算法都可以描述为一个由基本操作组成的序列
- 可行性 每一基本操作都可实现,切在常数时间内完成
- 有穷性 对任何输入,经过又穷次基本操作都可以得到输出
序列Hailstone(n)
好算法
- 正确
- 健壮
- 可读
- 效率 (速度尽可能快;存储空间尽可能少)
To measure is to know.
If you can not measure it,
you can not improve it.
- Lord Kelvin
算法分析
两个主要方面:
- 正确性;
- 成本:运行时间+所需空间
RAM:Random Access Machine
- 寄存器顺序编号,总数没有限制(R[0],R[1]…)
- 每一基本操作仅需常数时间
- 与TM 模型一样看,RAM模型也是一般计算机的简化与抽象,使我们可以独立于具体的平台,对算法的效率做出可信的比较与评判
- 在这些模型中 算法的运行时间+无穷大 算法需哟啊执行的基本操作次数 T(n) = 算法为求解规模为n的问题,所需执行的基本操作次数
大O记号
Mathematics is more in need
of good notations than
of new theorems.
- Alan Turing
好读书不求甚解
每有会意,便欣然忘食
-- 陶渊明
常数(constant function)
这类算法的效率最高
算法分析
He calculated just as men breathe
as eagles sustain themselves in the air.
- Francois Arago
两个主要任务
- 正确性
- 复杂度
复杂度分析的主要方法
- 迭代: 级数求和
- 递归:递归跟踪+地推放出
- 猜测+验证
级数
- 算数级数:与末项平方同阶
- 幂方级数:比幂次高出一阶
- 几何级数:与末项同阶
- 收敛级数: 常数
- 调和级数:1+1/2+…+1/n=O(log n)
- 对数级数:log1+log2+…+logn=O(log n)
相关书籍:Concrete Mathematics
起泡排序
//n 为在某个位置之前排序(不包含),相当于arr数组的长度
public void vuvvlesort(int[] arr,int n){
for(boolean sorted = false; sorted = !sorted; n--){
for(int i = 1; i < n; i++){
if(arr[i-1]>arr[i]){
swap(arr,arr[i-1],arr[i]);//交换两个元素位置函数,需要自己实现
}
}
}
}
封底估算(Back-Of-The-Envelope Calculation)
某位物理学家当时在测量原子弹爆炸威力时,在安全范围内将一张纸撕成很多个碎片,在爆炸时将手里的纸扔下,通过计算纸张落下的位置来 计算原子弹爆炸的威力,与当时计算机计算出来的数值仅仅差距为2倍,换算成O的算法,同阶!!!
小思考:如果让你测量地球周长,如何进行呢?
迭代乃人工,递归方神通(To iterate is human,to recurse,divine.)
凡治众如治寡,分数是也(The control of a large force is the same principle as the control of a few men:it is merely a question of dividing up their numbers)
简而治之
分而治之
- 二分递归
数组求和:二分递归
sum(int[] arr,int lo,int hi){
if(lo == hi) return arr[lo];
int mi = (lo + hi) >> 1;
return sum(arr, lo, mi) + sum(arr, mi + 1, hi);
}
public int[] max2(int[] arr, int lo, int hi){
int[] result=new int[2];
if (lo + 2 == hi) {
return arr[lo] >= arr[lo+1] ? new int[]{ arr[lo], arr[lo + 1] } : new int[]{arr[lo + 1], arr[lo]};
}
if (lo + 3 == hi) {
if (arr[lo] >= arr[lo + 1]) {
return arr[lo + 1] > arr[lo + 2] ? new int[]{arr[lo], arr[lo + 1]}
: arr[lo] >= arr[lo + 2] ? new int[]{arr[lo], arr[lo + 2]} : new int[]{arr[lo + 2], arr[lo]};
} else {
return arr[lo] > arr[lo + 2] ? new int[]{arr[lo+1], arr[lo]}
: arr[lo + 1] >= arr[lo + 2] ? new int[]{arr[lo + 1], arr[lo + 2]}: new int[]{arr[lo + 2], arr[lo + 1]};
}
}
int mi = (lo + hi) >> 1;
int[] los = max2(arr, lo, mi);
int[] his = max2(arr, mi, hi);
if (los[0] >= his[0]) {
return los[1] >= his[0] ? new int[]{los[0], los[1]} : new int[]{los[0], his[0]};
} else {
return his[1] >= los[0] ? new int[]{his[0], his[1]} : new int[]{his[0], los[0]};
}
}
private void swap(int[] arr, int a, int b) {
arr[a] = arr[a] ^ arr[b];
arr[b] = arr[a] ^ arr[b];
arr[a] = arr[a] ^ arr[b];
}
动态规划
Make it work,
make it right,
make it fast.
- Kent Beck
思考计算:斐波那契数列
public int f(int n){
if(n == 0){
return 0;
}else if(n == 1){
return 1;
}
return f(n - 1) + f(n - 2);
}
这个算法时间复杂度特别高,大概求50左右的数字会感觉到明显延迟,而且之后每多一个会呈现几何倍数增长,算法时间复杂度O(n^2)
改进:
public int f(int n){
int f=0;
int g=0;
while(0 < n--){
g += f;
f = g - f;
}
return g;
}
子序列(Subsequence):
求两个字符串的最大公共子序列
等待更新...
递归版本(O(2^n))
动态规划版本(O(m*n))
向量
抽象数据类型&数据结构
- 抽象数据类型:数据模型+定义在该模型上的一组操作
- 数据结构:基于某种特定语言,实现ADT的一整套算法
linear array
- 静态空间管理 -> 动态空间管理
蝉的哲学:
身体每经过一段时间的生长,以至无法为外壳容纳
即蜕去原先的外壳,代之以… - 容量加倍策略
Increment
分析方法:
- 平均分析
- 分摊分析