算法设计的任务就是:针对一个具体的问题,利用特定的方法和步骤来获取最佳结果。
刚开始学编程的人总是会陷入这样的误区,以为学会了一门语言就学会了编程,总会学各种各样的语言。实际上,语言只是一个工具,解决具体的问题必须依赖于算法,而算法从本质上讲是数学方法的表达。通过一定的数学知识来解答。一个好的系统分析师或设计师,或许他们可以不会任何一种语言,但如果他们能通过数学公式或图形等准确的表达出解决问题的方法和步骤,他们照样可以成为这个行业的佼佼者,拿高工资。
一个算法的5个重要特征:
*有穷性:一个算法必须在有穷步之后结束,且每一步必须在有穷的时间内完成。
*确定性:算法中的每一条指令必须有确切的含义,不能产生二义性。在任何条件下,算法只有唯一的一条执行路径,即对于相同的输入只能得出相同的输出。
*可行性:一个算法必须是可行的,即算法中的操作都必须通过已实现的基本运算执行有限次来实现。
*输入:一个算法可以有0个或多个输入。
*输出:一个算法必须有1个或多个输出。
算法设计要求:正确性、可读性、健壮性、高效率低地存储。
迭代法和递推法类似,也是递增求解,不同的是:在递推法中,每一步得到的解是相对于对应问题规模的完整解;在迭代法中,中间步骤得到的解一般只是“近似解”,并不代表问题的解。
看一道迭代法的应用例子:求解一个数的开方。√¯a;
分析:设√¯a = x,则 x2 - 1 = a -1
变换得: (x-1)(x+1) = a - 1
x = 1 + (a-1)/(1+x) ~~~~~~~这就是迭代公式
程序实现:
#include <stdio.h> #include <MATH.H> #define epsilon 1e-10 float Sqrt(float a) { float x,x0; x = 1; do{ x0 = x; x = 1 + (a-1)/(x+1); }while(fabs(x-x0) > epsilon); return x; } void main() { float num; float result1; float result2; printf("请输入一个浮点数:"); scanf("%f",&num); result1 = Sqrt(num); result2 = (float)sqrt(num); printf("库函数的计算结果:%f\n自己编写的函数计算结果%f\n",result2,result1); }
关于更多迭代法的使用,可参见我的博客《编写自己的Math函数库 》
从已知条件出发,利用特定的关系得出中间推论,逐步递推,直至得到结果为止。
1)顺推法
所谓顺推法,就是从已知条件出发,逐步推算出要解决问题的方法。例如:斐波那契数。
算法实现:
#include <stdio.h> #define NUM 13 void main() { int i; long fib[NUM] = {1,1}; for(i = 2; i < NUM; i++) { fib[i] = fib[i-1] + fib[i-2]; } for(i = 0; i < NUM; i++) { printf("%d\n",fib[i]); } }
2)逆推法
所谓逆推法,就是从已知的结果出发,用迭代表达式逐步推算出问题开始的条件。例如:
父亲准备为小龙的4年大学生活一次性在银行存一笔钱,使用整存零取的方式,控制小龙每月月底只能取1000元准备下一个月使用。假设银行一年整存零取的利率为1.71%,请编程实现父亲至少一次性存入多少钱才够小龙4年的生活费。
#include "stdio.h"
#define FETCH 1000
#define RATE 0.0171
void main()
{
double corpus[49];
int i;
corpus[48] = (double)FETCH;
for(i = 47;i > 0; i--)
{
corpus[i] = (corpus[i+1]+FETCH)/(1+RATE/12);
}
for(i = 48; i > 0; i--)
{
printf("第%d月末本利合计:%.2f\n",i,corpus[i]);
}
}
计算机最大的优势就是遍历和循环,一些人工计算很麻烦的问题,利用计算机穷举法就可以很好地解决。例如:百钱买鸡。
#include "stdio.h"
void BuyChicken()
{
int x,y,z;
for(x = 0; x <= 20; x++)
{
for(y = 0; y <= 33; y++)
{
z = 100 - x - y;
if(z % 3 == 0 && x*5 + y*3 + z/3 == 100)
{
printf("公鸡:%d,母鸡:%d,小鸡:%d\n",x,y,z);
}
}
}
}
void main()
{
BuyChicken();
}
算法实现:
递归算法的思想就是通过对问题的分解,使其能够分解为与原问题相似的子问题的过程,最后得出结果。例如:求阶乘、汉诺塔等。
求阶乘算法实现:
int fact(int n)
{
if(n <= 1)
{
return 1;
}
else
{
return n*fact(n-1);
}
}
对于一个规模为n的问题,若该问题可以很容易的解决,则直接解决,否则将其分解为M个规模较小的子问题,这些子问题相互独立,并且与原问题有相同的形式。
贪婪算法是一种不追求最优解,只希望得到较为满意的方法。例如:换零钱。
也称试探法,基本思想:为了求解问题的解,先选择某一种试探,在试探过程中,一但发现以前的选择假设有错误,则会退到上一步,如此反复进行,直至得到最终解。
例如:迷宫问题。
通过计算机来模拟对应的一些数学模型。例如:模拟随机抛硬币的结果。
基本思想是将大问题分解乘小问题,为了节约重复求解子问题的时间,引入一个数组,不管他们是否对最终解有用,把所有子问题的解保存在该数组中,这就是动态规划所采用的基本思想。
1)时间复杂度:通过统计算法中基本操作重复执行的次数来近似的得到算法的执行效率,用O(n)表示。
常见的渐近时间复杂度效率比较:
O(1) < O(log2n) < O(n) < O(nlog2n) < O(n2) < O(n3) < O(2n)
2)空间复杂度:程序在运行过程中所需占用的最大内存空间。
3)程序的可读性:程序的可读性有时比高效性更重要,因为可读性增强,易于调试和维护。