• 04.算法


    04.算法

    算法概念与特征

    算法定义:解决问题的方法与步骤

    设计算法的目的:给出解决问题的逻辑描述,根据算法描述进行实际编程

    算法特征

    有穷性:算法在每种情况下都可以在有限步后终止

    确定性:算法步骤的顺序和内容没有二义性

    输入:算法有零个或多个输入

    输出:算法至少具有一个输出

    有效性:所有操作都具有明确含义,并能在有限时间内完成

    正确性:不是算法的特征,算法的正确性需要数学证明

    算法描述

    伪代码

    混合自然语言与计算机语言、数学语言的算法描述方法

    优点:方便,容易表达设计者思想,能够清楚描述算法流程,便于修改

    缺点:不美观,复杂算法不容易理解

    流程图(程序框图)

    使用图形表示算法执行逻辑

    优点:美观,算法表达清晰

    缺点:绘制复杂,不易修改,占用过多篇幅

    算法设计与实现

    构造算法解决问题

    按照自顶向下、逐步求精的方式进行

    使用程序设计语言编程实现

    典型示例

    一、判断给定的某个自然数n(大于2)是否为素数。

    算法逻辑

    输入:大于2的正整数n

    输出:该数是否为素数,若为素数返回true,否则返回false

    step1.设除数i为2

    step2.判断除数i是否已为n,若为真返回true,否则继续

    step3.判断n%i是否为0,若为0返回false,否则继续

    step4.将除数i递增,重复step2

    bool IsPrime(unsigned int n)
    {
        unsigned int i = 2;
        while(i < n)
        {
            if(n % i == 0)
            {
                return false;
                i++;
            }
        }
        return true;
    }
    

    实际上没有必要整除到n-1,而只需整除到√n,改进后

    bool IsPrime(unsigned int n)
    {
        unsigned int i = 2;
        while(i <= (unsigned int)sqrt(n))
        {
            if(n % i == 0)
                return false;
            i++;
        }
        return true;
    }
    

    注:Linux环境中g++编译使用<cmath>中函数时,应在g++命令后加-lm参数

    再次改进后

    bool IsPrime(unsigned int n)
    {
        unsigned int i = 3;
        if(n % 2 == 0)
            return false;
        while(i <= (unsigned int)sqrt(n))
    /*sqrt()方法的结果为浮点数,计算机内部存储浮点数有一定误差,若将11存为10.999...之类,在转为无符号整型时被截掉小数部分,变为10,导致结果出错。故需做如下改变*/
      //while(i <= (unsigned int)sqrt(n) + 1)
        {
            if(n % i == 0)
                return false;
            i += 2;
        }
        return true;
    }
    

    实际上,以上算法还可以再做改进。因为sqrt(n)在算法中只起一个边界的作用,只需计算一次就够了,没有必要每次循环都计算一次。因此,可在算法开始处定义t = (unsigned int)sqrt(n) + 1;

    二、求两个正整数x与y的最大公约数

    穷举法

    unsigned int gcd(unsigned int x, unsigned int y)
    {
        unsigned int t;
        t = x < y ? x : y;
        while(X % t != 0 || y % t != 0)
            t--;
        return t;
    }
    

    欧氏算法(辗转相除法)

    step1.x整除以y,记余数为r

    step2.若r为0,则最大公约数即为y,算法结束

    step3.否则将y作为新x,将r作为新y,重复step1step2。

    unsigned int gcd(unsigned int x, unsigned int y)
    {
        unsigned int r;
        while(true)
        {
            r = x % y;
            if(r == 0)
                return y;
            x = y;
            y = r;
        }
    }
    

    算法选择

    算法选择的权衡指标

    正确性:算法是否完全正确

    效率:在某些场合,对程序效率的追求具有重要意义

    可理解性:算法是否容易理解,也是必须考虑的

    算法评估

    衡量算法的好坏,主要是效率

    递归算法

    递归问题的引入

    递推公式:数学上非常常见

    例一:阶乘函数:1! = 1, n! = n * (n - 1)!

    例二:斐波那契数列函数:f(1) = f(2) = 1, f(n) = f(n - 1) + f(n - 2)

    递推函数一定是分段函数,具有初始表达式

    递推函数的计算逻辑:逐步简化问题规模

    递推的工作步骤

    递推过程:逐步分解问题,使其更简单

    回归过程:根据简单情形组装最后的答案

    例子的不同实现

    一、阶乘问题

    循环实现

    unsigned int GetFactorial(unsigned int n)
    {
        unsigned int result = 1, i = 0;
        while(++i <= n)
            result *= i;
        return result;
    }
    

    递归实现

    unsigned int GetFactorial(unsigned int n)
    {
        unsigned int result;
        if(n == 1)
            result = 1;
        else
            result = n * GetFactorial(n - 1);
        retrn result;
    }
    

    二、斐波那契数列函数

    循环实现

    unsigned int GetFibonacci(unsigned int n)
    {
        unsigned int i, f1, f2, f3;
        if(n == 1 || n == 2)
            return 1;
        f1 = 1;
        f2 = 1;
        for(i = 3;i <= n;i++)
        {
            f3 = f2 + f1;
            f1 = f2;
            f2 = f3;
        }
        return f3;
    }
    

    递归实现

    unsigned int GetFibonacci(unsigned int n)
    {
        if(n == 1 || n == 2)
            return 1;
        else
            return GetFibonacci(n - 1) + GetFibonacci(n - 2);
    }
    

    循环与递归的比较

    循环使用显式的循环结构重复执行代码段,递归使用重复的函数调用执行代码段

    循环在满足其终止条件时终止执行,而递归则在问题简化到最简单情形时终止执行

    循环的重复是在当前迭代结束时进行,而递归的重复则是在遇到对同名函数的调用时进行

    循环和递归都可能隐藏程序错误,循环的条件测试可能永远为真,递归可能永远简化不到最简单情形

    理论上,任何递归程序都可以使用循环迭代的方法解决

    递归函数的代码更加短小精悍;递归程序更易理解

    容错

    容错的定义:允许错误的发生

    错误的处理

    很少见的特殊情况或普通错误:忽略该错误,不对程序运行结果产生影响

    用户输入错误:通知用户错误性质,提醒用户更正输入。

    致命错误:通知用户错误的性质,停止执行

    典型容错手段

    数据有效性检查

    程序流程的提前终止

    算法复杂度

    引入算法复杂度的目的

    度量算法的效率与性能

    大O表达式

    算法效率与性能的近似表示(定性描述)

    算法执行时间与问题规模的关系

    表示原则

    忽略所有对变化趋势影响较小的项,例如多项式忽略高阶项之外的所有项

    忽略所有与问题规模无关的常数,例如多项式的系数

    标准算法复杂度类型

    O(1):常数级,表示算法执行时间与问题规模无关

    O(log(n)):对数级,表示算法执行时间与问题规模的对数成正比

    O(sqrt(n)):平方根级,表示算法执行时间与问题规模的平方根成正比

    O(n):线性级,表示算法执行时间与问题规模成正比

    O(n*log(n)): n*log()级,表示算法执行时间与问题规模的n*log(n)成正比

    O(n²):平方级,表示算法执行时间与问题规模的平方成正比

    ......

  • 相关阅读:
    python--向钉钉群发送消息(亲测可用)
    python--注册钉钉回调事件(亲测可用)
    Gradle
    SpringBoot 中thymeleaf模板引擎的使用
    SpringBoot对静态资源的映射规则
    SpringBoot的日志使用
    rsync实现文件同步
    python学习: 优秀Python学习资源收集汇总--转
    Python高级特性: 12步轻松搞定Python装饰器
    Python常见问题
  • 原文地址:https://www.cnblogs.com/bear-Zhao/p/13624175.html
Copyright © 2020-2023  润新知