算法设计与分析 - 绪论

算法绪论(Algorithm)

一、算法初步

1 算法的五大特性
  • 输入:一个算法有零个或者多个输入
  • 输出:一个算法有一个或多个输出
  • 有穷性:一个算法必须在执行有限步数之后结束,且每一步需要在有穷时间内完成
  • 确定性:每条指令必须有确切的含义,对于相同的输入只能得到相同的输出
  • 可行性:算法可执行
2 算法的四个要求
  • 正确性
  • 可读性
  • 健壮性
  • 高效率与低存储量
3 算法的描述方法
  1. 自然语言
  2. 流程图
  3. 程序设计语言
  4. 伪代码----算法语言
4 算法设计的一般步骤

理解问题–>预测所有可能输入–>确定 解的类型、数据结构、算法设计技术–>设计并描述算法–>跟踪–>分析效率–>循环分析直到满意

5 算法执行时间相关因素

主要为:

  • 算法选用的策略

  • 问题的规模

基本操作:指该操作重复执行次数和算法的运行时间成正比。

二、非递归算法分析:

对非递归算法时间复杂性的分析,关键是建立一个代表算法运算时间的表达式,然后用渐进符号表示这个求和表达式

1 仅依赖于“问题规模”的时间复杂度

例1: 交换i和j的内容:

Temp = i; i = j; j = Temp;
//算法时间复杂度为O(1)。

结论:如果算法的执行时间不随问题规模n的增加而增长,即使算法中有上千条语句,其执行时间只不过是一个较大的常数

例2:变量计数:

int x = 0, y = 0;
for(int k = 1; k <= n; k++){
	x++;
}
for(int i = 0; i <= n; i++){
	for(int j = 0; j <= n; j++){
    y++;
  }
}
//算法时间复杂度为O(n^2)
x = 1;
for(i = 1; i <= n; i++)
  for(j = 1; j <= i; j++)
    for(k = 1; k <= j; k++)
      x++;//频度最大的语句
//算法时间复杂度为O(n^3)

结论:当有若干循环语句是,时间复杂度是由嵌套层数最多的循环语句中最内层语句的频度f(n)决定的。

2 算法时间复杂度与输入实例的初始状态有关

例3: k-选择算法 (现在还不懂这是个什么算法,后续学了来这里填坑,也欢迎大家指点!)

此时需要分析T(n,k),分别计算最好,最坏情况下的时间复杂度,若需计算平均时间复杂度需要“加权”

3 Conclusion:

非递归算法分析的一般步骤

  • 确定算法问题规模;
  • 找出关键操作;
  • 分析该关键操作是否仅仅依赖于问题规模;
    • 若是,直接建立代表关键操作执行次数的求和表达式,并求解、用渐进符号表示。
    • 若不是,分别对该算法的最好,最坏和平均情况的时间复杂度进行分析。

KEY:建立代表关键操作执行次数的求和表达式,并求解、用渐进符号表示。

三、递归算法分析(!!!)

分析方法:

KEY: 根据递归过程建立递推关系式。

1 猜测技术

对递推关系式估计一个上限,然后数学归纳法证明其正确

例4:分析二路归并排序算法的时间复杂性
T ( n ) = { 1 n = 2 2 T ( n / 2 ) + n n > 2 T(n) = \begin{cases}1 \quad n=2\\2T(n/2)+n \quad n>2\end{cases} T(n)={1n=22T(n/2)+nn>2
说明:在长度为n的情况下,算法代价是序列长度为n/2时代价的两倍加上n。

分三种猜测情况进行归纳证明(P14页证明):

1.O(n^2) 2.O(cn) 3.O(nlogn)

2 扩展递归技术

扩展意思是:将递推关系式等式右边的项根据递推式子进行替换

扩展递归就是:扩展+扩展+扩展+…直到可求

  • 迭代法

    例5:求N!

    //分析: 构造算法中的两个步骤:
    //			(1) N! = N*(N-1)!
    //			(2) 0! = 1, 1! = 1
    int fact(int n){
      if(n == 0 || n == 1)
        return 1;
      else return n*fact(n-1);
    }
    

    分析:递归方程为:T(n) = T(n-1) + O(1),其中O(1)为进行一次乘法操作。

    计算:
    T ( n ) = T ( n − 1 ) + O ( 1 ) = T ( n − 2 ) + O ( 1 ) + O ( 1 ) = T ( n − 3 ) + O ( 1 ) + O ( 1 ) + O ( 1 ) = . . . = T ( n − ( n − 1 ) ) + ( n − 1 ) ∗ O ( 1 ) = n ∗ O ( 1 ) = O ( n ) \begin{aligned} T(n) &= T(n-1) + O(1) \\ &= T(n-2) + O(1) + O(1)\\ &= T(n-3) + O(1) + O(1) + O(1)\\ &=...\\ &= T(n-(n-1)) + (n-1)*O(1)\\ &= n*O(1) = O(n)\\ \end{aligned} T(n)=T(n1)+O(1)=T(n2)+O(1)+O(1)=T(n3)+O(1)+O(1)+O(1)=...=T(n(n1))+(n1)O(1)=nO(1)=O(n)
    例6: Hanoi塔问题:

    Hanoi(A, C, n) //n个盘子A到C
    
    	if n = 1 then move(A, C)
    
    	else Hanoi(A, B, n-1)
    
    		move(A, C)
    
    		Hanoi(B, C, n-1)
    

    则n个盘子工作量为:

    ​ T(n) = 2 T(n-1) + 1

    ​ T(1) = 1

    计算得 T(n) = 2^n - 1

  • 换元迭代法

    例6:分析下列递推式的时间复杂度:
    T ( n ) = { 7 n = 1 2 T ( n / 2 ) + 5 n 2 n > 1 T(n) = \begin{cases} 7 \quad n=1\\ 2T(n/2)+5n^2 \quad n>1 \end{cases} T(n)={7n=12T(n/2)+5n2n>1
    分析:本题使用换元迭代法,设n = 2^k

    计算:
    T ( n ) = 2 T ( n / 2 ) + 5 n 2 = 2 ( 2 T ( n / 4 ) + 5 ( n / 2 ) 2 ) + 5 n 2 = 2 ( 2 ( 2 T ( n / 8 ) + 5 ( n / 4 ) 2 ) + 5 ( n / 2 ) 2 ) + 5 n 2 = 2 k T ( 1 ) + 2 k − 1 5 ( n 2 k − 1 ) 2 + . . . + 2 ∗ 5 ( n 2 ) 2 + 5 n 2 = 7 n + 5 n 2 ∑ i = 0 k − 1 1 2 i = 7 n + 5 n 2 ( 2 − 1 2 k − 1 ) = 10 n 2 − 3 n ≤ 10 n 2 = O ( n 2 ) \begin{aligned} T(n) &= 2T(n/2) + 5n^2 \\ &=2(2T(n/4) + 5(n/2)^2) + 5n^2\\ &=2(2(2T(n/8)+5(n/4)^2)+5(n/2)^2) + 5n^2\\ &=2^kT(1)+2^{k-1}5(\frac{n}{2^{k-1}})^2+...+2*5(\frac{n}{2})^2 + 5n^2\\ &=7n+5n^2\sum_{i=0}^{k-1}\frac{1}{2^i}\\ &=7n+5n^2(2-\frac{1}{2^{k-1}})\\ &=10n^2-3n\\ &\leq10n^2=O(n^2)\end{aligned} T(n)=2T(n/2)+5n2=2(2T(n/4)+5(n/2)2)+5n2=2(2(2T(n/8)+5(n/4)2)+5(n/2)2)+5n2=2kT(1)+2k15(2k1n)2+...+25(2n)2+5n2=7n+5n2i=0k12i1=7n+5n2(22k11)=10n23n10n2=O(n2)
    (小小的记录:Markdown语法仅支持KaTex(LaTex的渲染器),故align不可操作,可使用aligned)

  • 差消法化简高阶递推方程

    这个暂时挖个坑,学明白了来填!

3 通用分治递推法

这个公式咋来的咱也不知道,之后学到补上!
T ( n ) = { c n = 1 a T ( n / b ) + c n k n > 1 T(n) = \begin{cases}c \quad n=1\\aT(n/b)+cn^k \quad n>1\end{cases} T(n)={cn=1aT(n/b)+cnkn>1
利用各个参数可得下列定理:
T ( n ) = { O ( n log ⁡ b a ) a > b k O ( n k log ⁡ b n ) a = b k O ( n k ) a < b k T(n)= \begin{cases}O(n^{\log_b^a}) \quad a>b^k\\O(n^k\log_b^n) \quad a=b^k\\O(n^k) \quad a<b^k\end{cases} T(n)=O(nlogba)a>bkO(nklogbn)a=bkO(nk)a<bk

四、常用算法时间复杂度排序(!!!)

我用3D计算器画了几根图像,应该能说明大部分问题:

算法时间复杂度排序

同时,以下九种算法时间复杂度最常用,其关系为:

O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n)

五、算法的后验分析(Posteriori)

后验分析指时间分析,转换成程序并上机实验

一般步骤:
  1. 明确实验目的
  2. 决定度量算法效率方法
  3. 决定输入样本,生成实验数据(样本规模、范围、格式)
  4. 运行程序,记录实验数据
  5. 分析数据
扩展递归技术是解决递推关系式问题的一种方法,它通过记忆化搜索的方式优化递归函数的性能,避免重复计算相同问题,从而提高效率。解决递推关系式时,我们通常会遇到两个问题:求解特定项的值和生成项的序列。 以下是使用扩展递归技术求解递推关系式的一般步骤: 1. 定义递归函数:根据递推关系式定义一个递归函数,函数的参数通常包括当前要计算项的下标或值以及任何必要的参数。 2. 使用记忆化:创建一个数据结构(例如数组或哈希表)来存储已经计算过的项的值。在递归函数中,每次计算之前先检查所需项是否已经计算过,如果是,则直接返回存储的值,避免重复计算。 3. 递归终止条件:确保递归有明确的终止条件,这些条件通常是递推关系式中的基本情况。 4. 计算递推关系式:在递归函数中实现递推关系式,确保每一项的计算都遵循递推关系式定义的规则。 例如,假设有一个递推关系式 f(n) = f(n-1) + f(n-2),这是一个斐波那契数列的定义。使用扩展递归技术求解的伪代码如下: ```pseudo def extended_recursive_fib(n, memo): if n in memo: # 检查记忆化存储中是否已有结果 return memo[n] if n <= 1: return n memo[n] = extended_recursive_fib(n-1, memo) + extended_recursive_fib(n-2, memo) # 计算并存储结果 return memo[n] memo = {} # 初始化记忆化存储结构 result = extended_recursive_fib(n, memo) # 调用递归函数并传入记忆化存储结构 ``` 在这个例子中,我们使用了`memo`这个字典来存储已经计算过的斐波那契数列的值,从而避免了重复计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值