【上交ACM-算法初级】算法评价与复杂度
算法评价与复杂度
在现实生活中,计算资源,包括CPU的计算速度和内存的大小,是有限的,而我们的等待时间也是有限的。所以,我们需要用更快(或内存利用率更高)的算法来应对时间紧张(或者内存紧张)的开发场景。
-
时间限制:一方面,CPU的运算速度是有限制的;另一方面,等待问题结果的时间也是有限的。所以,衡量算法运行时间的一个重要指标是时间复杂度。
例如,打开个人电脑的配置,可以看到cpu的主频。主频,表示CPU每秒钟产生脉冲信号的次数(也就是每秒钟的时钟周期个数)。
以2.1GHz为例,一秒钟该CPU可以产生2.1\times10^92.1×109次脉冲信号,如果一台计算机每个时钟周期可以完成1条指令,那么该计算机1s之内就可以运行2.1\times 10^92.1×109条指令。
-
空间限制:计算机内存的大小是有限制的。 所以,衡量算法内存消耗的一个重要指标是空间复杂度。
例如,一个二维数组
5000 \times 5000 \times 4 \div 1024 \div 1024 \approx 95\text{ MB}5000×5000×4÷1024÷1024≈95 MBint a[5000][5000]
所耗内存为: -
另外,在一些更有针对性的场景(如机器学习场景),在算法开发中,可能有更具体的需求,所以就需要设计更具体的指标(例如机器学习中的准确率、精确率和召回率等)。
时间复杂度举例
-
O(1)O(1)——常数条语句:交换两个元素
// 输入 int a = 4; int b = 6; // 计算 int t = a; a = b; b = t;
-
O(n)O(n)——单重循环求数组和
// 输入 int a[] = {2, 0, 1, 3, 5}; int n = 5; int sum = 0; for (int i = 0; i < n; ++i) sum += a[i];
-
O(n^2)O(n2)——双重循环求数组中相等元素对数
// 输入 int a[] = {1, 1, 3, 5, 5}; int n = 5; // 计算 int cnt = 0; for (int i = 0; i < n; ++i) for (int j = i + 1; j < n; ++j) cnt += (a[i] == a[j]);
-
O(2^nn)O(2nn)——枚举n个数字组成集合的所有子集,输出子集和。
// 输入 int a[] = {2, 1, 3, 6, 5}; int n = 5; // 计算 int tot = 1 << 5; // 相当于求2的5次方 for (int i = 0; i < tot; ++i) { // 变量i的二进制形式用于表示每个元素选(1)与不选(0)。 int sum = 0; for (int j = 0; j < n; ++j) if ((i >> j) & 1) sum += a[j]; // 检查i的第j位是否是1 cout << sum << endl; }
空间复杂度举例
同样,我们也可以将程序所消耗空间的大小表示成一个与输入规模有关的函数,该函数称为空间复杂度。
但空间复杂度情况数比时间复杂度少,常见的场景就是估算数组大小:
// 假设数据规模最大为N int a; // 常数空间复杂度 int a[N]; // 此时空间复杂度为 O(N) int a[N][N]; // 此时空间复杂度为 O(N^2)