在复赛中, 时间复杂度极为重要。 一道题目, 可以有多种算法完成, 但是, 选用省时的算法完成题目者获得的分数高。 本文整理了时间复杂度的知识。 希望获得高分的考生必须熟练掌握。
1、算法复杂度是什么?算法复杂度用来衡量算法的高效性,简单的说就是:
然而,运行时间和语言、机器、机器的状态、数据量的大小都有关系,不好横向比较,为此通常使用一个时间复杂度(相对度量)的概念来衡量算法有多快。 我们假设其它状态不变,仅当问题规模(数据大小)增长时,指令执行的次数也在增长,那么指令执行次数相对于问题规模来说,会构成一个函数T(n)。 例如-对于以下数组求和的算法1+2+3+…+n: int sum = 0; //指令数为1for(int i=0; i<n; i++) sum += n; //指令数为 ncout << n; //指令数为1 显然,总的指令数为T(n) = n + 2 2、大O函数假设一个算法的T(n) = 4n^3 - 2n + 5 当n越来越大时,对于T(n)增长的贡献来说,最高阶的n^3会占据主导地位,其它项可被忽略。 例如:
我们一般用大O函数来表示最主要的贡献部分:O(T(n)) = O(n^3),也即算法的时间复杂度。
3、常见大O函数
|
问题规模 | 算法复杂度 |
---|---|
>10^7 | O(logn) |
10^7 | O(n) |
10^5 ~5*10^5 | O(nlogn) |
1000 ~ 5000 | O(n^2) |
200 ~ 500 | O(n^3) |
20 ~ 24 | O(2^n) |
12 | O(n!) |
用上表做参考,可以用来:
在看到题目规模的时候,就知道大概需要选择什么算法了。
选择一个算法实现后,大概知道能通过多少测试点。
例如
:一个排序的题目,30%数据<1000,50%数据<3000,100%数据<300000,则:
如果想AC(100分),需要用O(nlogn)的算法,例如快速排序。
如果只会冒泡排序(O(n^2)),能拿到50分。
首先仔细阅读题目,通过小数据模拟理解题目,大致明白题目的输入->输出之间的逻辑。
要求
-在模拟、理解试题之后,:
能自己生成更多的输入和输出样例。
能设计朴素算法来解题。
实现一种朴素算法,通过所有的样例,先不考虑性能。
要求
:通过所有的测试样例,包括自己设计的样例,确保结果无误。
根据试题数据的范围,分析出复杂度分级,并对朴素算法进行优化,大致划分出不同优化方案对应的复杂度分级,以及得分范围。
要求:
根据自己的能力积累,尽可能设计出更大得分范围的优化算法。
用所有测试样例对优化算法进行测试,确保结果无误。
对拍是指用脚本来调用朴素算法和优化算法两个程序,对所有输入数据做运算,比较其输出是否一致。
对拍被用来确保优化程序不会出现结果错误的情况,因为优化程序往往用到比较复杂的思路,难免编码出错。
不会对拍的情况,也可以通过对不同的数据范围实现分支控制,比如小数据调用朴素算法,大数据调用对应的优化算法,各施其责。
为确保代码正确及性能测试通过,需要编写程序生成符合数据范围的各级测试数据,以便对优化算法进行性能测试,以及对拍所用。
复赛模拟
一、题目:最大公约数的时间复杂度
以下两种求最大公约数的方法:枚举法和辗转相除法,分别计算出时间复杂度来。
二、分析
算法描述:
取m和n的最小值,这里直接假设n<m了,下同。
循环i从n递减到1,如果i同时被n、m整除,就退出循环。
返回i
时间复杂度为O(n)
int f1(int m, int n) { int i; for(i=n; i>1; i--) if (m%i==0 && n%i==0) break; return i;}
算法描述:
计算r = m % n
循环:m = n, n = r, r = m%n
一直到r为0时循环结束,此时n即为最大公约数
其时间复杂度为O(logn)
int f2(int m, int n) { int r = m % n; while(r) { m = n; n = r; r = m%n; } return n;}
三、扩展理解
辗转相除法每一次迭代让 m = n, n = r,即 gcd(m, n) = gcd(n, m%n) = gcd(m%n, (m%n) %n) ...
设 m = n*a + r,由于0<= r < n,于是有
m > (a+1) * r > 2 * r
而gcd(m, n) = gcd(n, r) = gcd(r, r%n)
也就是说,经过两次迭代,至少把m缩小一倍
显然算法复杂度为O(logn)
四、案例代码
#include <iostream>
using namespace std;
int f1(int m, int n) {
int i;
for(i=n; i>1; i--)
if (m%i==0 && n%i==0)
break;
return i;
}
int f2(int m, int n) {
int r = m % n;
while(r) {
m = n;
n = r;
r = m%n;
}
return n;
}
int main() {
int m=40, n =24;
cout << f1(m, n) << endl;
return 0;
}
|