算法设计和分析1( 算法问题求解基础)

chapter1 算法问题求解基础

1.1算法概述

1.什么是算法

  1. 算法—用计算机实现的问题求解方法。
  2. 5个特征
    (1)输入:0或多个
    (2)输出:至少一个
    (3)确定性:算法每一条指令都有明确的定义
    (4)能行性:算法每一条指令必须足够基本,可以用=已实现的基本运算执行有限次来实现。
    (5)有穷性:有限步骤后终止
  3. 描述方法
    大白话、流程图、伪代码、程序设计语言

例1 计算两个整数的最大公约数(辗转相除法==欧几里得算法、连续整数检测算法)
用于计算两个整数m和n(0<=m<n)的最大公约数gcd(m,n).其计算过程是重复下列等式,直到n mod m=0.
gcd(m,n)=gcd(n mod m,m)
例如gcd(24,60)=gcd(12,24)=gcd(0,12)=12

程序1-1 欧几里得递归算法 (递归调用函数)

#include<iostream>
#include<algorithm>
using namespace std;

//欧几里得递归算法

void Swap(int &a,int &b)//用于交换两个数的函数,其实有内置的swap函数,注意要变量的引用
{
    int c=a;
    a=b;
    b=c;
}
int RGcd(int m,int n)//0<=m<n
{
    if(m==0)return n;
    return  RGcd(n%m,m);
}
int Gcd(int m,int n)
{
    if(m>n)Swap(m,n);
    return RGcd(m,n);
}
int main()
{
    int m,n;
    cin>>m>>n;
    cout<<Gcd(m,n);
    return 0;

}

程序1-2 欧几里得迭代算法(循环

#include<bits/stdc++.h>
using namespace std;

int Gcd(int m,int n)
{
    if(m==0)return n;
    if(n==0)return m;
    if(m>n)swap(m,n);//n中保存大的数
    while(m>0)
    {
        int rem=n%m;
        n=m;
        m=rem;
    }
    return n;
}
int main()
{
    int m,n;
    cin>>m>>n;
    cout<<Gcd(m,n);
    return 0;

}

连续整数检测算法
最大公约数定义:能够同时整除它们的最大正整数。因此最大公约数小于或等于两数中的较小者。t=min{m,n},然后检查t能否整除m和n ,若能,即为解;否则t–,继续检测

#include<bits/stdc++.h>
using namespace std;

int Gcd(int m,int n)
{
    if(m==0)return n;
    if(n==0)return m;
    int t=m>n?n:m;
    while(m%t ||n%t)
        t--;
    return t;
}
int main()
{
    int m,n;
    cin>>m>>n;
    cout<<Gcd(m,n);
    return 0;

}

2.为什么学习算法

重要重要重要
一个受过良好的计算机科学知识训练的人知道如何处理算法,即构造算法、操纵算法、理解算法和分析算法。算法远不只是为了编写好的计算程序,它是一种具有一般意义的智能工具,必定有助于对其他学科的理解。

1.2 问题求解方法

软件开发的过程----使用计算机求解问题的过程。
计算机解题的核心任务----设计算法

1.问题和问题求解

问题----与希望的目标不一致

2.问题求解过程

  1. 理解问题
  2. 设计方案
  • 何处着手,类似问题
  • 一些特殊示例进行分析
  1. 实现方案
  2. 回顾复查
  • 评估:改进、简化和推广

3.系统生命周期

系统生命周期:软件工程将软件开发和维护过程分成若干阶段。分析(做什么)、设计(如何做)、编码、测试和维护

1.3 算法设计和分析

1.算法问题求解过程

精确算法、启发式算法(可能更有效,但未必是最优解)

2.如何设计算法

学习基本算法
如合并排序和快速排序都可以视为分治法产生的排序算法,但二者是不同的算法。

3.如何表示算法

自然语言、流程图、伪代码、程序设计语言等,(下面主要用c/c++来描述)

4.如何确认算法

算法确认:对于所有的合法术后如,都能在有限时间输出预期结果。
算法证明:使用数学方法

5.如何分析算法

执行时间和所需空间

1.4 递归和归纳

重复性计算
(1)递归
(2)迭代
归纳法和递归关系密切,可以用归纳法证明递归算法的正确性。

1.递归

递归定义:直接或间接引用自身的定义方法。包括基础情况和递归部分

例1-1 斐波那契数列

F0=0,F1=1;
Fn=Fn-1+Fn-2;

需要注意的是,递归实现的斐波那契数列在计算过程中会存在大量的重复计算,效率较低。如果需要计算较大的斐波那契数列,建议使用迭代或动态规划的方法,可以避免重复计算,提高效率。

long Fib(int n)
{
    if(n<=1)return n;
    else return Fib(n-1)+Fib(n-2);
}

2.递归算法示例

例1-2 逆序输出整数的各位数 设有正整数n=12345,现希望逆序输出54321.
设k位整数为d1d2…dk,要得到dkdk-1…d1可以分两步:
(1)首先输出dk
(2)然后输出由前k-1位组成的正整数d1d2…dk-1

得到下面的递归程序

#include<bits/stdc++.h>
using namespace std;

void PrintDigit(unsigned int n)
{
    cout<<n%10;
    if(n>=10)PrintDigit(n/10);
}
int main()
{
    unsigned int n;
    cin>>n;
    PrintDigit(n);
    return 0;
}

例1-3 汉诺塔问题
假定有三个塔座:x,y,z,在塔座上有n个直径不同的圆盘,它们按直径大小从小到大编号为1,2,…,n。现要求将x塔座上的n个圆盘移动到y上,并按相同的顺序叠排。移动时:
(1)每次只能移动一个圆盘
(2)圆盘可以加到塔座x、y、z中任意一个之上
(3)任何时刻都不能将一个较大的圆盘放在较小的圆盘之上

假定圆盘从小到大编号为1-n,移动圆盘的算法可以粗略地描述如下:
(1)以y为中介,将前n-1个圆盘从x移到z上
(2)将第n个圆盘移到y上
(3)以x为中介,将z上的n-1个圆盘移到y上

#include<bits/stdc++.h>
using namespace std;
enum tower{A='X',B='Y',C='Z'};
void Move(int n,tower x,tower y)
{
    //将第n个圆盘从x移到y的顶部
    cout<<"The disk "<<n<<" is moved from "<<char(x)<<" to top of tower "<<char(y)<<endl;
}
void Hanoi(int n,tower x,tower y,tower z)//将n个圆盘从x->y,z作为中介
{
    if(n){
        Hanoi(n-1,x,z,y);
        Move(n,x,y);
        Hanoi(n-1,z,y,x);
    }
}
int main()
{
   Hanoi(3,A,B,C);
    return 0;

}

输出如下

The disk 1 is moved from X to top of tower Y
The disk 2 is moved from X to top of tower Z
The disk 1 is moved from Y to top of tower Z
The disk 3 is moved from X to top of tower Y
The disk 1 is moved from Z to top of tower X
The disk 2 is moved from Z to top of tower Y
The disk 1 is moved from X to top of tower Y

例1-4 产生各种可能的排列
给定n个自然数,{0,1,2,…n-1}的集合。设计一个算法,输出该集合所有可能的排列(permutation).n个自然数的集合有n!个不同的排列。

介绍一种求此问题的简单递归算法。

由四个自然数组成的排列通过以下方式构造:
(1)以0 开头,紧随其后为{1,2,3}的各种排列
(2)以1 开头,紧随其后为{0,2,3}的各种排列
(3)以2 开头,紧随其后为{0,1,3}的各种排列
(4)以3 开头,紧随其后为{0,1,2}的各种排列
语句(1)中“紧随其后为{1,2,3}的各种排列”实质上是求比原始问题少一个数的排列生成问题。相当于原题目,这是一个同类子问题,但规模小一些。意味着可用递归求解

template <class T>

void Perm(T a[],int k,int n)
{
    if(k=n-1)
    {
        for(int i=0;i<n;i++)//输出一种排列
            cout<<a[i]<<" ";
    }
    else
        for(int i=k;i<n;i++)
    {
        T t=a[k];a[k]=a[i];a[i]=t;//产生{a[k],...a[n-1]}各种排列
        Perm(a,k+1,n);
        t=a[k];a[k]=a[i];s[i]=t;//产生{a[k+1],...a[n-1]}各种排列
    }
}

本章小结

递归是强有力的算法算法结构。

习题

1.写一个递归算法和一个迭代算法计算二项式系数。 Cnm=Cn-1m+Cn-1m-1=n!/m!(n-m)!

递归法

#include<bits/stdc++.h>
using namespace std;
int Combine(int m,int n)
{
    if(m==0||m==n)return 1;//注意递归出口,是一个确定的值
    else return Combine(m,n-1)+Combine(m-1,n-1);
}

int main()
{
   int m,n;
   cin>>m>>n;
   cout<<Combine(m,n);
    return 0;

}

迭代法是不是用一个数组保存结果呀,然后依次计算。

2.S是有n个元素的集合,S的幂集是S的所有可能的子集组成的集合。例如,S={a,b,c},S的幂集={{},{a},{b},{c},{a,b},{a,c},{b,c},{a,b,c}}.写一个递归函数,以S为输入,输出S的幂集。

相关推荐

  1. 算法设计分析1算法问题求解基础

    2024-04-06 18:58:01       41 阅读
  2. 基于萤火虫算法求解订单分批问题

    2024-04-06 18:58:01       59 阅读
  3. 基于遗传算法求解旅行商问题(附Matlab代码)

    2024-04-06 18:58:01       64 阅读
  4. 基于量子免疫克隆算法求解背包问题 MATLAB 代码

    2024-04-06 18:58:01       54 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-06 18:58:01       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-06 18:58:01       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-06 18:58:01       87 阅读
  4. Python语言-面向对象

    2024-04-06 18:58:01       96 阅读

热门阅读

  1. Go语言时间编程

    2024-04-06 18:58:01       139 阅读
  2. python 三位数字黑洞

    2024-04-06 18:58:01       40 阅读
  3. C++继承

    C++继承

    2024-04-06 18:58:01      42 阅读