通信系统仿真

崔春雷

目录

  • 1 第一单元: MATLAB基础
    • 1.1 课程说明与资料
      • 1.1.1 作业参考答案
      • 1.1.2 移动22级作业答案
    • 1.2 MATLAB安装与运行环境
      • 1.2.1 MATLAB介绍
    • 1.3 基本数据类型:数值类型
    • 1.4 基本数据类型:字符类型
    • 1.5 数据类型转换与输出
    • 1.6 数组与矩阵基础
      • 1.6.1 矩阵运算进阶
    • 1.7 数组与矩阵常用函数
    • 1.8 matlab中的逻辑运算
    • 1.9 实验: MATLAB常用数学函数
      • 1.9.1 实验 作业答案
    • 1.10 元胞数组
    • 1.11 结构体数组
      • 1.11.1 结构体进阶
      • 1.11.2 元胞数组与结构体数组对比
      • 1.11.3 map 容器
    • 1.12 附录:MATLAB常用基础命令
    • 1.13 拓展内容:实时脚本
      • 1.13.1 实时脚本示例
    • 1.14 课程作业与答案
      • 1.14.1 《通信系统仿真》期末考试
  • 2 第二单元:Matlab 程序设计
    • 2.1 顺序结构程序
    • 2.2 分支结构—— if语句
    • 2.3 分支结构—— switch语句
    • 2.4 循环结构—— while语句
    • 2.5 循环结构—— for语句
    • 2.6 图像处理基础
    • 2.7 Matlab的函数
      • 2.7.1 函数内容的课外扩展
    • 2.8 本章实验:for循环的应用
      • 2.8.1 素数问题
        • 2.8.1.1 素数的螺旋线排列
      • 2.8.2 3X+1猜想
      • 2.8.3 7 行代码计算 π
    • 2.9 排序算法
      • 2.9.1 冒泡排序
      • 2.9.2 选择排序
      • 2.9.3 插入排序
      • 2.9.4 快速排序
      • 2.9.5 基数排序
      • 2.9.6 计数排序
      • 2.9.7 堆排序
    • 2.10 动态规划算法
      • 2.10.1 动态规划编程实例
      • 2.10.2 动态规划:01背包问题
      • 2.10.3 动态规划常见题目分析
      • 2.10.4 动态规划题目分析2
    • 2.11 常用算法简介
      • 2.11.1 剪枝算法
      • 2.11.2 二分查找
      • 2.11.3 递归算法
      • 2.11.4 回溯算法
        • 2.11.4.1 Leetcode回溯题目合集
        • 2.11.4.2 回溯算法总结
        • 2.11.4.3 回溯法解数独问题
        • 2.11.4.4 DFS与BFS
          • 2.11.4.4.1 DFS/BFS原理
          • 2.11.4.4.2 BFS的应用:Dijkstra算法
      • 2.11.5 n 皇后问题专题
      • 2.11.6 双指针算法
      • 2.11.7 数组模拟链表(约瑟夫环)
      • 2.11.8 Hash(哈希表)
      • 2.11.9 图论与路径规划
        • 2.11.9.1 迪杰斯特拉算法
        • 2.11.9.2 A*算法
          • 2.11.9.2.1 A*算法的MATLAB实现
        • 2.11.9.3 RRT路径规划算法
          • 2.11.9.3.1 RRT算法 MATLAB代码
          • 2.11.9.3.2 参考资料
      • 2.11.10 数据结构
        • 2.11.10.1 数据结构例题
      • 2.11.11 前缀和 差分 双指针
      • 2.11.12 位运算
      • 2.11.13 常用算法代码模板
    • 2.12 练习题库
    • 2.13 code
      • 2.13.1 简易计算器gui代码
      • 2.13.2 五子棋
      • 2.13.3 连连看小游戏
      • 2.13.4 递归算法与汉诺塔
      • 2.13.5 有理数的小数循环节
    • 2.14 MATLAB编程风格
      • 2.14.1 向量化编程专题
  • 3 第三单元:Matlab 图形图像处理
    • 3.1 二维图形绘图基础
    • 3.2 二维图形绘图进阶
    • 3.3 三维图形绘图
      • 3.3.1 MATLAB绘图小结
        • 3.3.1.1 用matlab绘制好看图像
    • 3.4 MATLAB高级绘图
    • 3.5 文件操作
    • 3.6 Matlab图像处理进阶
      • 3.6.1 补充:Matlab图像处理常用函数
      • 3.6.2 RGB/HSV/HSI颜色模型
      • 3.6.3 图片切换动画效果
      • 3.6.4 图像连通域标记
      • 3.6.5 图像旋转与插值
      • 3.6.6 图像的形态学
      • 3.6.7 空间滤波
        • 3.6.7.1 图像中常见的噪声类型与滤波方法
        • 3.6.7.2 matlab中的滤波函数
        • 3.6.7.3 BM3D 去噪算法
        • 3.6.7.4 双边滤波
      • 3.6.8 图像的频域处理
    • 3.7 本章总结
    • 3.8 实验 : matlab 绘图练习1
    • 3.9 实验: matlab 绘图练习2
    • 3.10 实验 :数学函数图像绘制
    • 3.11 实验:绘图综合练习
    • 3.12 实验:曲线拟合
    • 3.13 实验:牛顿法求解方程的根
    • 3.14 实验:信号的傅里叶变换
      • 3.14.1 傅里叶变换、小波变换、希尔伯特变换
      • 3.14.2 新建目录
    • 3.15 课外补充:图像处理基础1
    • 3.16 课外补充:图像处理基础2
    • 3.17 课外补充:图像处理基础3
    • 3.18 课外补充:PYTHON基础
  • 4 第五单元:MATLAB通信仿真
    • 4.1 现代通信系统的介绍
    • 4.2 模拟通信系统的仿真原理
    • 4.3 HDB3编解码的仿真实现
    • 4.4 SIMULINK和其模块简介
    • 4.5 数字通信系统的仿真原理
    • 4.6 模拟通信系统Simulink仿真
    • 4.7 数字通信系统Simulink仿真
    • 4.8 音频信号测处理与仿真
    • 4.9 图像数字水印技术
      • 4.9.1 三角函数到傅里叶变换再到语音识别与数字水印
    • 4.10 信息系统与算法
      • 4.10.1 递归算法
        • 4.10.1.1 递归与堆栈的关系
      • 4.10.2 哈希表
      • 4.10.3 双指针算法
        • 4.10.3.1 双指针算法实战
        • 4.10.3.2 双指针进阶:滑动窗口算法
      • 4.10.4 字符串匹配 KMP算法
        • 4.10.4.1 字符串匹配B-M算法
      • 4.10.5 快速傅里叶变换
      • 4.10.6 回溯算法
      • 4.10.7 动态规划
      • 4.10.8 分治算法
      • 4.10.9 Dijkstra算法
  • 5 第六单元: systemview通信仿真
    • 5.1 SystemView概述
    • 5.2 模拟通信系统 数字系统的仿真分析
    • 5.3 SystemView通信系统仿真进阶
    • 5.4 新建课程目录
  • 6 第四单元:MATLAB高级应用
    • 6.1 符号运算基础
      • 6.1.1 利用Matlab自动推导公式
    • 6.2 Matlab中的数值计算
      • 6.2.1 积分的计算
      • 6.2.2 龙格库塔:常微分方程的数值解法
      • 6.2.3 fmincon函数与非线性方程最小值
    • 6.3 统计、拟合、插值
      • 6.3.1 协方差与相关系数
    • 6.4 GUI设计初步
    • 6.5 matlab GUI界面编程
      • 6.5.1 gui实例
      • 6.5.2 gui编程中常用函数
      • 6.5.3 App Designer入门
    • 6.6 实验:GUI设计图像空间变换系统
    • 6.7 作业:利用GUI设计 计算器、信号发生器等
    • 6.8 MTALB数据导入方法
    • 6.9 课外补充:MATLAB的App会取代GUI吗?
    • 6.10 模拟退火算法matlab实现
    • 6.11 遗传算法的Matlab实现
      • 6.11.1 进化算法(Evolutionary Algorithm)及相关函数介绍
    • 6.12 粒子群算法 matlab实现
      • 6.12.1 粒子群算法及MATLAB实例仿真
    • 6.13 BP网络的应用
    • 6.14 matlab 结构体
    • 6.15 群智能算法合集
  • 7 拓展知识
    • 7.1 什么是算法的时间复杂度?
    • 7.2 Notepad++使用教程
    • 7.3 MATLAB常用函数总结
    • 7.4 MATLAB常用知识点总结
    • 7.5 MATLAB命令大全
    • 7.6 视频:MATLAB官方基础教程
    • 7.7 经典书籍:Matlab2012经典超强教程
    • 7.8 经典书籍:MATLAB揭秘(自学宝典)
    • 7.9 经典资料:MATLAB N个实用技巧
    • 7.10 Matlab编程小技巧
    • 7.11 寻优算法
      • 7.11.1 Dijkstra算法python实现
    • 7.12 PYTHON基础教程
      • 7.12.1 Python进阶
      • 7.12.2 Python小技巧
      • 7.12.3 Python总结
        • 7.12.3.1 Python循环语句总结
        • 7.12.3.2 24个顶级Python库
        • 7.12.3.3 魔法函数
      • 7.12.4 廖雪峰python
      • 7.12.5 正则表达式基础
      • 7.12.6 numpy
        • 7.12.6.1 101道Numpy习题
        • 7.12.6.2 Numpy简要语法教程
        • 7.12.6.3 Numpy实现全连接神经网络 (手写数字识别)
        • 7.12.6.4 图解NumPy
      • 7.12.7 matplotlib
        • 7.12.7.1 matplotlib练习50题
        • 7.12.7.2 Matplotlib速查表
        • 7.12.7.3 Matplotlib 实操指南
      • 7.12.8 Python3 模块 import
      • 7.12.9 Python 小项目
    • 7.13 参考资源:数据结构与算法
      • 7.13.1 十大经典排序算法总结
    • 7.14 机器学习概述
      • 7.14.1 反向传播算法
        • 7.14.1.1 反向传播的数学原理
      • 7.14.2 极大似然估计
        • 7.14.2.1 极大似然估计与最小二乘法
      • 7.14.3 Batch Normalization
        • 7.14.3.1 Batch Normalization&Dropout浅析
        • 7.14.3.2 ​BN层的梯度反向传播计算
        • 7.14.3.3 Batch Size的大小与神经网络的性能
        • 7.14.3.4 标准化和归一化
      • 7.14.4 主成分分析PCA与SVD奇异值分解
        • 7.14.4.1 岭回归 与 PCA
        • 7.14.4.2 PCA原理推导
        • 7.14.4.3 PCA原理新解
        • 7.14.4.4 svd
        • 7.14.4.5 PCA数学原理
      • 7.14.5 正则化
        • 7.14.5.1 L1、L2正则化和过拟合 总结
        • 7.14.5.2 L1 和 L2 正则化的直观解释
      • 7.14.6 SVM
        • 7.14.6.1 从零推导支持向量机(SVM)
        • 7.14.6.2 支持向量机(SVM)介绍
        • 7.14.6.3 SVM推导与实战
        • 7.14.6.4 支持向量机的直观理解
        • 7.14.6.5 浅显易懂的支持向量机SVM
      • 7.14.7 线性回归
      • 7.14.8 逻辑回归
      • 7.14.9 BP算法
        • 7.14.9.1 万能逼近——神经网络拟合任意函数原理
      • 7.14.10 激活与池化
        • 7.14.10.1 激活函数与损失函数 小结
      • 7.14.11 深度学习简述
        • 7.14.11.1 MATLAB2020深度学习实例
      • 7.14.12 损失函数与误差反向传播
        • 7.14.12.1 梯度下降与损失函数
      • 7.14.13 深度学习优化问题
      • 7.14.14 梯度下降法
        • 7.14.14.1 各类梯度下降算法的Python实现
        • 7.14.14.2 梯度下降的直观理解
        • 7.14.14.3 动量、RMSProp、Adam
      • 7.14.15 卷积的概念
        • 7.14.15.1 卷积的矩阵化算法
      • 7.14.16 局部连接
      • 7.14.17 RNN
      • 7.14.18 LSTM
      • 7.14.19 CNN-四大经典CNN技术浅析
      • 7.14.20 熵(Entropy)与交叉熵
      • 7.14.21 softmax函数详解
      • 7.14.22 自编码算法详细理解与代码实现
      • 7.14.23 pytorch
        • 7.14.23.1 ​PyTorch简介
          • 7.14.23.1.1 Pytorch快速入门资料
        • 7.14.23.2 CNN的PyTorch实现
        • 7.14.23.3 pytorch总结
        • 7.14.23.4 PyTorch trick 集锦
        • 7.14.23.5 在PyTorch上加载自定义数据集
        • 7.14.23.6 实战:Pytorch识别验证码
        • 7.14.23.7 实战:Transformer的最简洁pytorch实现
        • 7.14.23.8 使用PyTorch实现神经网络分类
      • 7.14.24 卷积神经网络CNN概述
        • 7.14.24.1 CNN 简易原理
        • 7.14.24.2 卷积神经网络CNN原理详解
        • 7.14.24.3 自己手写一个卷积神经网络
        • 7.14.24.4 CNN反向传播算法
        • 7.14.24.5 卷积计算、作用与思想
        • 7.14.24.6 用卷积神经网络CNN识别手写数字集
        • 7.14.24.7 卷积 池化 参数的计算
        • 7.14.24.8 im2col方法实现卷积算法
        • 7.14.24.9 卷积核的梯度计算
        • 7.14.24.10 卷积层反向传播推导及实现
        • 7.14.24.11 反向传输算法
          • 7.14.24.11.1 resnet残差网络
        • 7.14.24.12 CNN反向传播的MATLAB实现
      • 7.14.25 神经网络的调参技巧
      • 7.14.26 BP神经网络
        • 7.14.26.1 零开始搭建bp神经网络
        • 7.14.26.2 MATLAB自带的bp工具箱
        • 7.14.26.3 神经网络中偏置(bias)的作用
      • 7.14.27 聚类分析 k-means
        • 7.14.27.1 matlab做聚类分析(k-means)
        • 7.14.27.2 聚类模型探讨综述
        • 7.14.27.3 5种经典聚类算法
      • 7.14.28 深度学习的一些概念
      • 7.14.29 人工智能简述:AI的过去和现在
      • 7.14.30 k-NN(k近邻算法)
      • 7.14.31 神经网络中的优化器:BGD、SGD、MBGD、Momentum
      • 7.14.32 卷积神经网络的经典网络总结
        • 7.14.32.1 卷积神经网络中十大拍案叫绝的操作
      • 7.14.33 GAN 对抗样本攻击
      • 7.14.34 蒙特卡洛模拟
      • 7.14.35 dropout与随机部分连接
      • 7.14.36 Jupyter 等 IDE概览
      • 7.14.37 分类算法常用评价指标
      • 7.14.38 Inception 网络与不变性
      • 7.14.39 卷积神经网络的可视化
      • 7.14.40 隐马尔可夫模型HMM
        • 7.14.40.1 马尔科夫链
    • 7.15 MATLAB音频处理
      • 7.15.1 python处理音频信号
    • 7.16 图像处理
      • 7.16.1 图像处理中的指标
    • 7.17 代码集
    • 7.18 论文写作与阅读方法
      • 7.18.1 期刊投稿攻略
      • 7.18.2 论文排版教程
      • 7.18.3 SCI-HUB论文下载技巧
      • 7.18.4 几种论文写作神器,提高写作效率
      • 7.18.5 latex入门
      • 7.18.6 LaTeX教程
    • 7.19 机器学习常用的网站以及资源
      • 7.19.1 很详细的ML&DL学习博客
    • 7.20 SymPy 符号计算基本教程
  • 8 程序设计数学基础
    • 8.1 编程数学基础
      • 8.1.1 概率的历史
      • 8.1.2 概率
        • 8.1.2.1 常见概率分布
          • 8.1.2.1.1 二维正态分布
        • 8.1.2.2 蒙特卡罗方法
        • 8.1.2.3 置信区间
        • 8.1.2.4 协方差与相关系数
      • 8.1.3 矩阵 向量求导法则
      • 8.1.4 雅可比矩阵 海森矩阵
      • 8.1.5 矩阵的几种分解方式
      • 8.1.6 行列式和代数余子式
      • 8.1.7 向量
      • 8.1.8 矩阵的基本运算
      • 8.1.9 矩阵分析
      • 8.1.10 矩阵的LU分解
      • 8.1.11 矩阵奇异值分解(SVD)
        • 8.1.11.1 SVD分解2
        • 8.1.11.2 SVD分解逐步推导
        • 8.1.11.3 奇异值与特征值的意义
      • 8.1.12 随机向量
        • 8.1.12.1 随机过程简述
      • 8.1.13 投影矩阵和最小二乘
      • 8.1.14 知乎数学精选集
        • 8.1.14.1 高数问题集
      • 8.1.15 小波变换
      • 8.1.16 程序设计数学基础1:高等数学
      • 8.1.17 程序设计数学基础2:线性代数
      • 8.1.18 程序设计数学基础3:概率论和数理统计
      • 8.1.19 向量的距离与相似度计算
      • 8.1.20 复数
      • 8.1.21 高等数学——幂级数
      • 8.1.22 无穷小的本质
      • 8.1.23 数列极限和收敛性
      • 8.1.24 不定积分技巧总结
    • 8.2 有趣的数学题目
    • 8.3 高等数学
      • 8.3.1 泰勒级数
  • 9 路径规划与智能算法
    • 9.1 常见路径规划算法简介
    • 9.2 Dijkstra算法详细
  • 10 教学文档
    • 10.1 授课计划
    • 10.2 课程标准
Matlab的函数





                                MATLAB函数



 MATLAB 程序,大致分为两类:M 脚本文件 (M-Script) 和 M 函数 (M-function), 它们均是普通的 ASCII 码构成的文件。

脚本和函数都允许您通过将命令序列存储在程序文件中来重用它们。函数提供的灵活性更大,主要因为您可以传递输入值并返回输出值。此外,函数能够避免在基础工作区中存储临时变量,并且运行速度比脚本更快。


脚本与函数区别:

例:脚本

在名为 triarea.m 的文件中创建一个脚本以计算三角形的面积:

b = 5;
h = 3;
a = 0.5*(b.*h)

保存文件后,您可以从命令行中调用该脚本:

triarea
a =
   7.5000


例:函数

function a = triarea(b,h)
        a = 0.5*(b.*h);
end

保存该文件后,您可以从命令行调用具有不同的基值和高度值的函数,不用修改脚本:

a1 = triarea(1,5)
a1 =
   2.5000



1.M 函数

一个函数是一组在一起执行任务的语句。 在MATLAB中,函数在单独的文件中定义。文件的名称和函数的名称应该是一样的。函数在自己的工作空间内的变量上运行,这个变量也称为本地工作空间,与在MATLAB命令提示符下访问的工作区(称为基本工作区)不同。


  MATLAB 的 M 函数是由 function 语句引导的,函数可以接受多个输入参数,并可能返回多个输出参数(也可以不返回输出参数)

其基本格式:function [返回变量列表] = 函数名 (输入变量列表) 

function   [out1,out2 ..., outN] = myfun(in1,in2,in3, ...,inN)
%函数使用说明
.... ....%do something
.... ....%do something
end
可以将
保存在以下位置:
(1)只包含函数定义的函数文件中。文件的名称须与文件中第一个函数的名称一致。
(2)包含命令和函数定义的脚本文件中。函数必须位于该文件的末尾。脚本文件不能与文件中的
    函数具有相同的名称。R2016b 或更高版本的脚本中支持函数。

关于

为提高可读性,可使用 end 关键字来表示文件中每个函数的末尾,以下情况下需要 end 关键字:
文件中有任意函数包含嵌套函数。
该函数是函数文件中的局部函数,并且文件中有局部函数使用end关键字。
该函数是脚本文件内的局部函数。





示例:建立一个函数计算圆的周长和面积

第1步:建立函数



第2步:函数的调用




练习1:建立一个名为sjxjs的函数,计算三角形的周长和面积.

调用方法为:   [S,L]=sjxjs(inputArg1,inputArg2,inputArg3)

其中inputArg1,inputArg2,inputArg3为三角形的三个边长,返回的参数S,L为三角形的面积和周长。


function  [S,L] = sjxjs(a,b,c)

% sjxjs()函数的作用是计算三角形面积与周长,

% 输入参数inputArg1,inputArg2,inputArg3分别是三个边长

% 输出参数S和L为三角形的面积和周长


if (a+b)>c&(a+c)>b&(b+c)>a

    

    fprintf('这是一个合理的三角形\n')

    p=(a+b+c)/2;

    L=(a+b+c);

    S=sqrt(p*(p-a)*(p-b)*(p-c));

  

    fprintf('这个三角形周长为:%.3f   , 面积为:%.3f  \n',L,S)   

    

else

    

    fprintf('输入的三个边长构不成三角形,请重新输入 \n')

    

end


end




练习2 设计一个专门用于生成n*m的希尔伯特矩阵的函数:myxebt()

创建名为myxebt.m的函数文件,从左上角菜单中点击新建->函数,

并在其中键入以下代码 

function  H= myxebt(x1,x2)
%本函数用于创建n行m列的希尔伯特矩阵。
%函数形式为H= myxebt(x1,x2),其中x1表示要创建的矩阵的行数,x2表示列数
%函数返回值H为创建好的希尔伯特矩阵
%版权信息:GDCP,2021_05_24

 for n=1:x1
       for m=1:x2
             H(n,m)=1/(n+m-1);
       end
  end
end


函数语句之后的注释行提供了帮助文本(函数的说明书)。

当键入 help myxebt 时,这些信息被打印 




现在,我们来使用这个函数 -






练习3:制作一个名为yyecfc()的函数,用来解一元二次方程的两个根x1,x2,其调用格式为:[x1,x2] = yyecfc(a,b,c)

并调用这个函数来解以下几个方程。


function [x1,x2] = yyecfc(a,b,c)

%这个函数用来计算一元二次方程的根

% a,b,c为一元二次方程的系数,x1,x2为根


x1 =  (-b+sqrt(b^2-4*a*c))/(2*a);

x2 =  (-b-sqrt(b^2-4*a*c))/(2*a);


end








练习4:没有任何输入和输出参数的函数

如,设计一个清屏、关闭各种窗口的小函数,命名为ccc.m然后保存在工作路径下,之后在matlab命令行窗口之间输入:ccc,然后回车,就可以执行清屏...

function ccc()
clc
close all
fclose all




问:Matlab的function写完后,最后加不加end?


答:加不加都可以。最好加就统一加,不加就都不加。

如果你的函数内还嵌套有子函数,或者顺序的写了别的调用的子函数,此时最后要加end

https://www.ilovematlab.cn/thread-60628-1-1.html




2.主函数和子函数的概念


主函数和子函数

每个函数文件包含主要出现的必需的主函数,以及主函数之后的任意数量的可选子函数。

在Matlab中,可以把多个函数的定义放在一个函数文件中,这些函数中,第一个出现的为主函数,其他的函数均为子函数,需要注意的是,子函数只能被同一个函数文件中的函数调用。在保存函数文件的时候,函数文件名一般保持和主函数名相同,且外部程序只能对主函数进行调用。

可以从命令行或其他函数的文件外部调用主函数,但不能从命令行或函数文件外的其他函数调用子函数。

子函数仅对函数文件中的主函数和其他子函数可见。





例1:采用子函数方式,制作一个成绩查询程序



function  chengji(x1,x2)       %主函数相当于工程总负责人

%chengji(x1,x2) 中,第一个参数x1为平时成绩,第二个参数x2为期末成绩

%用三个子函数,分别实现:分数检测、总分的计算、输出


[X_ps,X_qm]=jiance(x1,x2);

S_zf=jisuanzf(X_ps,X_qm);

shuchu(X_ps , X_qm , S_zf); 


end




function [X_ps,X_qm]=jiance(x1,x2)    %各个子函数就是职能不同的分包商

%子函数1,专业负责检查输入是否有错

        X_ps=x1;

        X_qm=x2;

        while    X_ps>100 | X_ps<0 | X_qm>100 | X_qm<0

                     fprintf('输入有误,输入的分数应该在0-100之间 \n');

                     X_ps=input('请重新输入你的 , 平时分:');

                     X_qm=input('请重新输入你的 , 期末考试分:');

        end

end




function  S_zf=jisuanzf(x1,x2) 

%子函数2,专业负责计算总分

            X_ps=x1;

           X_qm=x2;

           S_zf=0.4*X_ps+0.6*100*(X_qm/100)^0.5;           

end




function   shuchu(x1,x2,x3)

%子函数3,专业负责打印输出

       X_ps=x1;

       X_qm=x2;

       S_zf=x3;

      if   S_zf>=90

                fprintf('您的平时分为:%5.2f  卷面分为:%5.2f    总分为:%5.2f  成绩优秀. \n' ,X_ps ,X_qm , S_zf);

        elseif  S_zf>=70&S_zf<90

                fprintf('您的平时分为:%5.2f  卷面分为:%5.2f    总分为:%5.2f    成绩良好. \n',X_ps ,X_qm , S_zf);

        elseif  S_zf>=60&S_zf<70

                fprintf('您的平时分为:%5.2f   卷面分为:%5.2f   总分为:%5.2f   成绩及格. \n',X_ps ,X_qm , S_zf);

        elseif  S_zf<60

                fprintf('您的平时分为:%5.2f  卷面分为:%5.2f   总分为:%5.2f  等着补考吧.. \n',X_ps ,X_qm , S_zf);

        end

end







例2

下面编写一个名为quadratic的函数来计算二次方程的根。该函数需要三个输入参数:二次系数,线性系数和常数项,计算并会返回根。

函数文件quadratic包含主函数quadratic和次函数和子函数disc(它用于计算判别式)。


function [x1,x2] = quadratic(a,b,c)    %主函数
%this function returns the roots of  a quadratic equation.
% It takes 3 input arguments which are the co-efficients.

    disc(a,b,c); 
    x1 = (-b + d) / (2*a);
    x2 = (-b - d) / (2*a);

end   
             

           
function dis = 
disc(a,b,c)             
%子函数 ,用于计算判别式。
%sub-function :calculates the discriminant
     dis = sqrt(b^2
 - 4*a*c);
end 
     



可以从命令提示符调用上述函数 


>> quadratic(2,4,-4)
ans =
         0.7321



练习:设计一个名为mymax( )的函数,调用格式为 y=mymax(A ),输出y为输入矩阵A的最大值。

function   m=mymax(A)


[row,col]=size(A);  %测量矩阵A的行数和列数

m=-Inf;                 %初始化最大值m, 也可以写为 m=A(1,1);

for k1=1:row         %遍历行

    for k2=1:col      %遍历列

          if  A(k1,k2)>=m    %如果当前元素大于m

              m=A(k1,k2);      %则把m更新为当前元素

          end

    end

end


end



%如果要求不但返回最大值,而且还要返回最大值所在的行数和列数


function    [m,x,y]=mymax(A)

[row,col]=size(A);  %测量矩阵A的行数和列数

m=A(1,1);              %初始化最大值m为A中的第1行第1列的元素;

x=1;                       %初始化最大值m对应的行x的值为第1行

y=1;                       %初始化最大值m对应的列y的值为第1列


for k1=1:row          %遍历行

    for k2=1:col       %遍历列

          if  A(k1,k2)>=m    %如果当前元素大于m

              m=A(k1,k2);      %则把m更新为当前元素

              x=k1;                %则把x更新为当前元素对应的行数k1

              y=k2;                %则把y更新为当前元素对应的列数k2

          end

    end

end

end




3.嵌套函数

嵌套函数

可以在一个函数的主体内定义另一个函数。这样的函数被称为嵌套函数。嵌套函数包含任何其他函数的部分或全部组件。


function  chengji(x1,x2)          

%chengji(x1,x2) 中,第一个参数x1为平时成绩,第二个参数x2为期末成绩

[X_ps,X_qm]=jiance(x1,x2);

S_zf=jisuanzf(X_ps,X_qm);

shuchu(X_ps , X_qm , S_zf);


   function [X_ps,X_qm]=jiance(x1,x2)    %各个子函数就是职能不同的分包商

       %内部子函数1,专业负责检查输入是否有错

       X_ps=x1;

       X_qm=x2;

       while    X_ps>100 | X_ps<0 | X_qm>100 | X_qm<0

           fprintf('输入有误,输入的分数应该在0-100之间 \n');

           X_ps=input('请重新输入你的 , 平时分:');

           X_qm=input('请重新输入你的 , 期末考试分:');

       end

   end


   function  S_zf=jisuanzf(x1,x2)

       %内部子函数2,专业负责计算总分

       X_ps=x1;

       X_qm=x2;

       S_zf=0.4*X_ps+0.6*100*(X_qm/100)^0.5;

   end


   function   shuchu(x1,x2,x3)

       %内部子函数3,专业负责打印输出

       X_ps=x1;

       X_qm=x2;

       S_zf=x3;

       if   S_zf>=90

           fprintf('您的平时分为:%5.2f  卷面分为:%5.2f    总分为:%5.2f  成绩优秀. \n' ,X_ps ,X_qm , S_zf);

       elseif  S_zf>=70&S_zf<90

           fprintf('您的平时分为:%5.2f  卷面分为:%5.2f    总分为:%5.2f    成绩良好. \n',X_ps ,X_qm , S_zf);

       elseif  S_zf>=60&S_zf<70

           fprintf('您的平时分为:%5.2f   卷面分为:%5.2f   总分为:%5.2f   成绩及格. \n',X_ps ,X_qm , S_zf);

       elseif  S_zf<60

           fprintf('您的平时分为:%5.2f  卷面分为:%5.2f   总分为:%5.2f  等着补考吧.. \n',X_ps ,X_qm , S_zf);

       end

   end


end





4、matlab函数中的形参与实参

学过其他编程语言的同学在对待函数时,往往需要急切地弄清楚什么时候函数传递形参,什么时候传递实参呢?

先上结论:MATLAB函数永远传递形参!

没有理解的同学可以通过下面的例子感受一下。

假设我现在有一个1*5的矩阵,我现在想让这个矩阵的第一个元素加1,那么有什么办法呢?最直观的想法是这样的:

%%% 文件add_one.m %%%
function add_one(mat)
    mat(1, 1) = mat(1, 1) + 1;
end

表面上看来,这个函数对于任何传递进来的矩阵mat,都将第一个元素进行了加1操作。我们对其进行一下测试:

%%% 文件Script5_3.m %%%
mat = [1, 2, 3, 4, 5];
add_one(mat);
mat

% 以下是命令行输出结果

mat =

    1     2     3     4     5

我们惊讶地发现,mat的值并没有发生任何变化!事实上,无论我们在函数内对传入参数进行什么操作,都不会影响到参数在主程序中的原本值,这是因为MATLAB使用的是“形参传递”。在这个过程中,实际上在MATLAB内部发生了如下的事情:

1 主程序调用函数,此时需要传递参数mat=[1,2,3,4,5]
2 主程序将所有需要传递的参数的值复制了一份,生成了参数副本mat_copy=[1,2,3,4,5]
3 主程序将计算中的所有变量暂存,进入函数执行模式
4 函数利用mat_copy进行计算,当函数试图修改mat的值时,实际在修改mat_copy的值,使得mat_copy=[2,2,3,4,5]
5 函数计算完成,告知主程序计算完成,并将计算结果返回主程序
6 主程序将暂存变量恢复,mat的值仍为[1,2,3,4,5]

因此,无论我们怎么做,主程序中的值都无法在函数中进行修改。那我们怎样实现上述需求呢?一种方式是可以将mat_copy的值直接返回给主程序,主程序再将mat的值替换为函数返回值。如下:

%%% 文件add_one.m %%%
function mat = add_one(mat)
   mat(1, 1) = mat(1, 1) + 1;
end

%%% 文件Script5_3.m %%%
mat = [1, 2, 3, 4, 5];
mat = add_one(mat)

% 以下是命令行输出结果

mat =

    2     2     3     4     5

这样做或许显得不美观,但也不失为一种好方法。

还有一种方法则是利用全局变量的方法。



5.全局变量


全局变量

在介绍全局变量之前,我们需要先了解一下变量空间。变量空间是指MATLAB计算过程中当前状态可以使用的所有变量。例如在主程序中可以访问在主程序中定义的变量,但是在函数(function)运行过程中就无法使用主程序定义的变量,因此我们说它们属于不同的变量空间。

一般而言,MATLAB的变量空间可以有多个,其中所有的脚本m文件共享一个变量空间,例如先执行Script2_1.m,再执行Script2_2.m时仍然可以访问在之前的脚本中已经定义的函数;而不同的函数(function)文件属于不同的变量空间,且不同于脚本m文件的变量空间,

因此任何函数都不能调用在其他函数中或者主程序中定义的变量。


之所以传递到函数中的参数不能修改,是因为主程序中传进去的参数是属于主程序的变量空间,当函数开始执行时,主程序的变量就被暂存起来无法改动了。那我们自然想到,能不能利用什么方法,使得一个变量不只是属于主程序呢?答案就是全局变量。

全局变量不属于任何一个程序模块,在任何时候都可以被修改。

我们使用如下的方式定义或使用一个全局变量:

global   GLOBAL_VAR;   %建议全局变量的名称使用大写字母,以区别于其他变量。

注意:使用全局变量前,要先进行声明,以告知编译器:这是一个全局变量,请不要在局部变量里面寻找。



例1:


x=1:10;

global exponential     %声明全局变量exponential(指数)

exponential=2;           %设置全局变量的值,后边function里面的这个变量会跟着同步取值

y=Exponentiation(x)   %调用Exponentiation函数


function y = Exponentiation(x)   %定义一个名为Exponentiation(幂运算)的函数

  global exponential          %声明全局变量exponential(指数)

  y = x.^exponential;         %对输入的x进行操作,幂指数由全局变量给定。

end






使用全局变量非常方便,但是需要注意的是,使用全局变量与函数化、模块化的概念完全相悖,大量使用全局变量将使得程序的逻辑控制变得复杂,可读性变差。因此建议大家尽量不要使用全局变量,以免生出“一个月前写的程序自己都看不懂”的悲剧。


如,上例完全可以不用全局变量,改为:




6.return语句

return 强制 MATLAB在到达调用脚本或函数的末尾前将控制权交还给调用程序。

return常用在自定义function里边,通常与if…else…一起用,如果满足if了,可以用return提前返回,不必再执行if…else…后的语句。

注意:在条件块(例如 if 或 switch)或循环控制语句(例如 for 或 while)使用 return 时需要小心。当 MATLAB 到达 return 语句时,它并不仅是退出循环,还退出脚本或函数,并将控制权交还给调用程序或命令提示符。

function d = det(A)
       if isempty(A)
          d = 1;
          return

       else
         ...
       end
       ...


当然,普通的脚本里(非function)也可以用,如使用多重for循环来搜索某个答案的时候,如果希望搜索到一个答案就跳出所有循环,就可以采用:

for ...

     for...

          for...

                 if   符合某某条件 

                       return

                  end





8.匿名函数内联函数


7.1 匿名函数


提起函数式编程,就不得不提到大名鼎鼎的Lambda函数。在MATLAB中,同样对Lambda函数提供了支持。为方便不了解函数式编程的同学,下面将统一称之为“匿名函数”。

首先回想我们使用函数的流程。我们需要确定函数的输入与输出、函数名,新建函数m文件,编写函数体,然后在主程序中对函数进行调用。但很多时候,我们编写的函数可能往往只有一行或更少,这种情况下单独编写一个函数m文件就显得非常麻烦。甚至有些时候,这个函数我们只需要用一次,能不能不取名呢?匿名函数(Lambda函数)就很好地解决了这个问题。

匿名函数的声明如下:

fhandle=@(arglist) expression

其中fhandle就是调用该函数的函数句柄(function handle),
相当于C语言中的
函数指针arglist是参数列表,多个参数使用逗号
分隔,
Expression则是该函数的表达式


匿名函数的形式十分简洁,将整个函数的定义缩减到一行内。

例:% 定义add函数的Lambda形式

lambda_add = @(a, b)a + b
% 调用函数
lambda_add(3, 4)

% 以下是命令行输出结果
ans =
    7
   

例:

在这个例子中,编写一个名为power的匿名函数,它将使用两个数字作为输入,并将第一个数字返回到第二个数字的幂值。

创建脚本文件并在其中键入以下代码 

power = @(x, n) x.^n;
result1 = power(7, 3)
result2 = 
power(49, 0.5)
result3 = 
power(10, -10)
result4 = 
power (4.5, 1.5)


当运行该文件,得到以下结果 

result1 =  343
result2 =  7
result3 =  1.0000e-10
result4 =  9.5459



匿名函数可以使用工作空间的变量,例如创建函数f(x,y)=x^2+y^3:


>> p=2;

>> q=3;

>> f=@(x,y) x^p+y^q

f =

    @(x,y)x^p+y^q


计算f(2,3):

>> f(2,3)

ans =

    31


如果修改p或者q的值,例如将q改为2:

q =

     2


>> f(2,3)

ans =

    31


计算结果并没有改变,这是因为,该函数句柄保存的是函数在创建时的快照,而不是动态的访问其中的变量,如果希望获取新值,需要重新创建一次该函数,完整的方法应该是这样的:


>> q=2;

>> f=@(x,y) x^p+y^q


f =

    @(x,y)x^p+y^q


>> f(2,3)

ans =

    13



匿名函数应用实例:


%例1

fun1= @(x) x.^2;

fun2= @(x) [x.^0.5 ,  x.^2 ,  sin(x) ];    %可以同时写多个函数,水平排列

fun3= @(x) [x.^0.5 ; x.^2  ;  sin(x) ];    %可以同时写多个函数,垂直排列


a1=fun1(2)                             %调用函数

a2=fun2(2)

a3=fun3(2)


q1=fun1( [2:4] )                      %用数组作为参数调用函数

q2=fun2( [2:4] )

q3=fun3( [2:4] )




%例2

x=[0:0.001:6];

f1= @(x) sin(80*x).*exp(-x);

y=f1(x);  

plot(x,y)


练习:使用Lambda函数来解一元二次方程。


yyec=@(a,b,c)[(-b+sqrt(b^2-4*a*c))/(2*a),(-b-sqrt(b^2-4*a*c))/(2*a)];


ans1=yyec(1,2,-7)

ans2=yyec(3,-12,8)

ans3=yyec(-2,0,9)







匿名函数的表达式中也可以有参数的传递,比如:

>> a=1:5; 

>> b=5:-1:1; 

>> c=0.1:0.1:0.5; 

>> f=@(x,y)x.^2+y.^2+c; 

>> f(a,b) 

ans =    26.1000   20.2000   18.3000   20.4000   26.5000

c作为表达式中的参数,进行了数据传递。

上面都是单重匿名函数,也可以构造多重匿名函数,如:

>> f=@(x,y)@(a) x^2+y^+a; 

>> f1=f(2,3) 

f1 = @(a)x^2+y^+a 

>> f2=f1(4) 

f2 =   85

每个@后的参数从它后面开始起作用,一直到表达式的最后。


1、A handle is a pointer to function(句柄是指向函数的指针

2、Can be used to pass functions to other functions(句柄是指向函数的指针,可用于将函数传递给其他函数)

示例代码:

function [y] = xy_plot(input,x)
%xy_plot receives the handle of a function and plots that
%funtion of x

    y = input(x);
    plot(x,y,'r--');
    xlabel('x');
     ylabel('function(x)');
end

%%需要调用时用以下函数
xy_plot(@sin,0:0.01:2*pi)




 内联函数


       内联(inline)函数是MATLAB 7以前经常使用的一种构造函数对象的方法。在命令窗口、程序或函数中创建局部函数时,通过使用inline构造函数,而不用将其储存为一个M文件,同时又可以像使用一般函数那样调用它。


MATLAB中的内联函数借鉴了C语言中的内联函数,在C语言中,内联函数是通过编译器控制来实现的,它只在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的时间和空间开销。在MATLAB中也有类似的性质。由于内联函数是储存于内存中而不是在M文件中,省去了文件访问的时间,加快了程序的运行效率。


虽然内联函数有M文件不具备的一些优势,但是由于内联函数的使用,也会受到一些制约。首先,不能在内联函数中调用另一个inline函数;另外,只能由一个MATLAB表达式组成,并且只能返回一个变量。是要注意 内联函数这种函数比较多的时候会占用比较多的内存空间。


创建一个内联函数非常简单,就是使用inline方法,例如:


>> f=inline('t^2-3*t-4')

f =

     Inline function:

     f(t) = t^2-3*t-4


MATLAB会通过检查字符串来推断自变量,例如上面的函数中t就是自变量,如果没有找到,将会使用x作为缺省的自变量,例如常数函数:


>> g=inline('3')

g =

     Inline function:

     g(x) = 3


另外,对于inline也支持多元函数:

>> h=inline('x+y')


总结:匿名函数的作用(好处):匿名函数的作用:主要实现自己定义matlab中的函数,从而扩大函数的使用功能。

函数简介:匿名函数不以文件形式驻留在文件夹上;他的生成方式最简捷,可在指令窗或任何函数体内通过指令直接生成。

和内联函数(inline)相比,匿名函数的优越性在于可以直接使用workspace中的变量,不必申明,非常适合嵌入到M文件中





匿名函数的补充知识:

(1)函数句柄的概念 

https://ww2.mathworks.cn/help/matlab/function-handles.html

可用于间接调用函数的变量

函数句柄是一种表示函数的 MATLAB 数据类型。函数句柄的典型用法是将一个函数传递给另一个函数。例如,您可以将函数句柄用作基于某个值范围计算数学表达式的函数的输入参数。

函数句柄可以表示命名函数或匿名函数。要创建函数句柄,请使用 @ 运算符。例如,创建用于计算表达式 x2 – y2 的匿名函数的句柄:

f = @(x,y) (x.^2 - y.^2);

有关详细信息,请参阅创建函数句柄

函数

function_handle函数的句柄
feval计算函数
func2str基于函数句柄构造字符向量
str2func根据字符向量构造函数句柄
localfunctionsMATLAB 文件中所有局部函数的函数句柄
functions关于函数句柄的信息


(2)Matlab中函数句柄@的作用及介绍

问:f=@(x)acos(x)表示什么意思?其中@代表什么?

答:表示f为函数句柄,@是定义句柄的运算符。f=@(x)acos(x) 相当于建立了一个函数文件:

% f.m

function  y=f(x)

y=acos(x);


若有下列语句:xsqual=@(x)1/2.*(x==-1/2)+1.*(x>-1/28&x<1/2)+1.2.*(x==-1/2);


则相当于建立了一个函数文件:


% xsqual.m


function y=xsqual(x)


y=1/2.*(x==-1/2)+1.*(x>-1/28&x<1/2)+1.2.*(x==-1/2);


详细说明:


1、函数句柄/function_handle(@):是一种间接调用函数的方式。

2、语法:handle=@functionname  or handle=@(arglist)anonymous_function

3、描述:函数句柄(function handle)是一种能够提供函数间接调用的matlab value。你可以通过传递句柄来调用各种其他功能。你也可以将句柄存储到数据结构中备用(例如Handle Graphic 回调)。句柄是matlab的标准数据类型之一。

      当创建句柄时,你所指定的函数必须在matlab搜索路径之中,并且必须在创建语句的scope之中。例如,只要在定义子函数的文件之中,你句可以为这个子函数创建句柄。这些条件不适用于evaluate函数句柄。例如,你可以在一个单独(out-scope)通过句柄执行一个子函数,这要求句柄是在in-scope创建的。

       handle=@(arglist)anonymous_function用来创建匿名函数(anonymous function)并返回该匿名函数的句柄。括号右边的函数体是单个的matlab语句(statement)或者matlab命令。arglist是一个用逗号“,”分隔的输入变量列表。该函数通过句柄handle执行。

备注:函数句柄是标准的matlab数据类型。因此,你可以像matlab其他数据类型一样进行操作。


4、函数句柄的好处

       ①提高运行速度。因为matlab对函数的调用每次都是要搜索所有的路径,从set path中我们可以看到,路径是非常的多的,所以如果一个函数在你的程序中需要经常用到的话,使用函数句柄,对你的速度会有提高的。

       ②使用可以与变量一样方便。比如说,我再这个目录运行后,创建了本目录的一个函数句柄,当我转到其他的目录下的时候,创建的函数句柄还是可以直接调用的,而不需要把那个函数文件拷贝过来。因为你创建的function handles中,已经包含了路径,


1、Lambda 表达式是面向对象平台中函数式编程原则的实现
2、Lambda 表达式用于
直接在代码中表达函数,而无需面向对象的包装器来支持它们(从语言语法的角度来看)。在方法上,可以看作是匿名方法。
3、由于 lambda 遵循函数的语义,就像在
函数式编程语言中一样, 因此它们获得了可以从不可变的、一致的函数中获得的所有并行性和并发性优势。
4、Lambda 表达式
可以在任何需要函数式接口的代码中使用,这实际上意味着,在内部,lambda 表达式是函数式接口的实现,因此是语言中的一等公民。它们可以被分配/存储,作为参数传递等。
5、如果匿名类是只包含一个方法的接口的实现,则 Lambdas 可以替换匿名类,然后匿名类可以替换为一个 lambda,这会形成干净且不那么冗长的代码。

链接:https://www.zhihu.com/question/20125256/answer/2327384711






函数句柄通过@符号创建,语法为:

fhandle = @functionname

其中,functionname 为函数名,fhandle 就是为该函数创建的句柄。

函数句柄也可以通过创建匿名函数的方式创建,语法为:

fhandle = @(arglist)expr

其中,expr 为函数体,arglist 为逗号分隔开的输入变量列表。比如,expr = @(x) x.^2 创建了用于计算输入变量平方的匿名函数。

如果输入变量为空,则 arglist 为空。t = @()datestr(now) 匿名函数的输入变量为空。

可以通过函数句柄实现对函数的间接调用,其调用语法为:

fhandle(arg1, arg2, …, argN)    %其中fhandle为函数句柄

可以使用单元数组同时为多个函数创建各自的句柄,

例如,trigFun = {@sin, @cos, @tan},定义了元胞数组 trigFun,它包含 3 个函数的句柄。

执行 plot(trigFun{2}(-pi:0.01:pi)) ,就是利用句柄调用了第二个函数,即 cos 函数。




————————————————


9.函数的递归调用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
例题:
在这里插入图片描述
在这里插入图片描述


练习1:利用函数递归调用,解决斐波那契数列问题

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、55,89……

斐波那契数列递推定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(≥ 3,∈ N*)


function F= Fibo(n)

if n==1

    F=1;

end


if n==2

    F=1;

end


if n>=3

    F=Fibo(n-1)+Fibo(n-2);

end


end




%用for循环来实现


n=20;

F=zeros(1,n);

F(1)=1;

F(2)=1;

for  k=3:n

       F(k)=F(k-1)+F(k-2);

end

F


    

 练习2:汉诺塔问题

汉诺塔(Hanoi Tower),是一种经典的数学问题和递归算法示例。

这个问题最初是由法国数学家爱德华·卢卡斯(Édouard Lucas)在1883年提出的,而“汉诺塔”这个名称则是来源于越南河内的一个传说。

汉诺塔的问题描述如下:有三根柱子,标记为A、B和C,A柱上穿有n个大小不同的圆盘,最上面的圆盘最小,依次往下递增。初始状态下,所有圆盘都按照大小从小到大的顺序堆叠在A柱上。目标是将所有圆盘移动到C柱上,并保持原有的大小顺序,同时遵守以下规则:一次只能移动一个圆盘。移动过程中,任何时刻大圆盘都不能放在小圆盘的上面。


这个问题的经典解法是使用递归算法基本思路是将问题划分为三个子问题:将n-1个圆盘从A移动到B,将最大的圆盘从A移动到C,最后将n-1个圆盘从B移动到C。这一过程可以递归地应用于子问题,直到圆盘数量为1时,可以直接移动。递归的结束条件是当只有一个圆盘时,直接将它从起始柱移动到目标柱。n阶汉诺塔问题最少共需要2的n次方减1步


汉诺塔问题不仅是一个有趣的数学谜题,而且它还展示了递归算法在解决问题时的简洁性和高效性。这个问题也经常被用作计算机科学和算法设计的教学案例。

参考:https://www.zhihu.com/question/421523155/answer/2314542265


请编写一个名为 hanno的函数,给出初始num个盘子在A柱,最终移动到C柱的全部流程。


hanno(3,'A','B', 'C')


function hanno(num,source,middle, target)

%num表示盘子的数量,字符source表示原来的柱子,字符middle表示中间的柱子,字符target表示目标柱子

  if  (num == 1)      %只有一个盘子时,总是把它从source 移动到 target

      fprintf('移动%s最上面那个盘子到%s上;\n', source, target);

      return;             %返回

  end


   hanno(num - 1, source, target, middle);   %例:只有2个盘子时,就让hanno帮忙先把1号盘从A移到B


   hanno(num - 1, source, middle, target);   %再让hanno帮忙先把2号盘从A移到C


   hanno(num - 1, middle, source, target);   %再让hanno帮忙先把1号盘从B移到C


end








练习3:把上节课绘制彩色随机图的代码改造成函数,并调用这个函数。


function  randcolor(k1,k2,n,m)

% randcolor的作用是生产随机彩图

% 调用方式为randcolor(k1,k2,n,m),其中k1彩图的行数,k2彩图的列数,n每个单元格的像素值,m彩图的刷新次数

        rgb=zeros(k1*n,k2*n,3);     %rgb的初始化

        for t=1:m                   %刷新m次,每次都重新生成新的随即彩图

            for ii=1:k1             %ii代表行

                 for jj=1:k2        %JJ代表列    

                      rgb((ii-1)*n+1:ii*n , (jj-1)*n+1:jj*n , : ) = ones(n ,n ,3).*rand(1,1,3);


                 end

            end

            imshow(rgb)        %显示彩图rgb

            hold on               %保持彩图窗口不动

            pause(2)              %持续2秒  


        end

end





=====================



作业:



作业1:设计一个函数,调用此函数可以返回矩阵(或者数组)中最大值以及最小值元素和平均值等。

输入参数A为输入的一个矩阵或者数组。

返回参数ma,mi为找到的矩阵A中的最大值与最小值。

返回参数ma,mi,ma_R,ma_C,mi_R,mi_C为最大值与最小值所对应的行号和列号。

返回参数pjz为矩阵A中的平均值。

要求:不能用matlab自带的max/min等现成的函数,要自己用for循环、if判断、>=,等方式寻找最大与最小值。


function  [ma,mi,ma_R,ma_C,mi_R,mi_C,pjz] = mymaxmin(A)

%此函数的功能是找到矩阵中的最大值及最小值,以及这两个最值对应的坐标。

[n,m]=size(A)


........

end




测试效果如下:

用正态分布的随机矩阵测试:




答案提示:


function  [ma,mi,ma_R,ma_C,mi_R,mi_C,pjz] = mymaxmin(A)

%此函数的功能是找到矩阵中的最大值及最小值,以及这两个最值对应的坐标。


[m,n]=size(A);

ma=-inf;

mi=inf;

total=0;

for i=1:m

     for j=1:n

         

         ???

     end

end


pjz=total/(m*n);


end







作业2:试着看看能否自己设计一个函数,比如命名为myfind.m, 实现matlab自带的find函数的类似功能?


function  [ A_k A_k_R , A_k_C ] = myfind(A , k , w )

%此函数的功能是找到矩阵A中的大于k(当W=1时),或者小于k(当W=0时)的元素

.......

end



输入参数A为输入的一个矩阵或者数组。

输入参数W只能为1或者0;W=1时找到矩阵A中的大于等于k的所有元素,W=0时找到矩阵A中的小于等于k的所有元素。

返回参数 A_k , A_k_R , A_k_C3个数组,分别记录按照要求找到的元素,以及这些元素对应的行号和列号 。

要求:

(1)不能用matlab自带的find等现成的函数,要自己用for循环、if判断、>=,等方式寻找最大与最小值。

(2)如果对方输入的参数w 不是0或者1,则输出报警,并返回空数组。

(3)如果没有找人任何符合要求的元素,则打印输出一句话“很抱歉,没有找到符合条件的元素。”


函数测试效果如下:




答案提示:


function  [ A_k , A_k_R , A_k_C ] = myfind(A , k , w )

%此函数的功能是找到矩阵A中的大于k(当W=1时),或者小于k(当W=0时)的元素。


if     ??

       fprintf('输入有误,w的值必须是0或者1 \n');

       A_k=[];   A_k_R=[];  A_k_C=[];

       return

end



[m,n]=size(A);

A_k=[ ];


if  w==1

    fprintf('即将为您寻找矩阵%c %c %f  的所有元素。 \n' ,'A' ,'>',k);    

    Num=0;

    for ii=1:m

        for jj=1:n

            

            ??

            

        end

    end

    

end



if w==0

    fprintf('即将为您寻找矩阵%c %c %f  的所有元素。 \n' ,'A' ,'<',k);

    Num=0;

    for ii=1:m

        for jj=1:n

            

              ??

            

        end

    end

end


end




作业3:编写一个名为is_prim()的函数。此函数可以实现的功能为:对输入的一个大于1的整数进行判断其是否是素数,如果是素数则返回1,否则返回0;如果输入N是小于2的正整数,或者N是非整数,则返回y=[ ]

函数调用的效果如下图:




作业4:编写一个名为函数zxgbs(X1,X2)的函数,如下:

function  y = zxgbs(X1,X2)

....

....


end

实现求最小公倍数的功能,即调用该函数,输入两个正整数x1,x2,则返回这两个正整数的最小公倍数。如:zxgbs(10,15),则返回y=30;

提示:两个正整数的最小公倍数,等于,这两个正整数的乘积,除以这两个正整数的最大公约数。最大公约数可以使用for循环来寻找。





作业5:编写一个彩色图像的马赛克处理函数

调用格式为 p_msk = mymsk( pic , w ) pic为输入的原始彩色图片,W为马赛克的宽度,p_msk为函数处理后输出的马赛克处理后的图片

mymsk函数内部处理过程:

步骤1:把图片的行数和列数截取为100的整数倍,得到pic_new,比如:如果原始图片大小为630*540,则pic_new为600*500。

步骤2:把输入的彩色图片pic_new的r,g,b三个通道抽出,分别给pic_new_r, pic_new_g, pic_new_b,

步骤3:分别把pic_new_r, pic_new_g, pic_new_b这三个灰度图,按照w*w像素区块进行马赛克化处理,得到三个马赛克化后的图

步骤4:再把步骤3得到的三个通道的马赛克图利用cat()函数重新拼接成一个彩色图p_msk


在主程序中调用后的效果如下图:






作业6:素数问题综合练习

(1)编写函数求解给定整数N之内的所有素数;

要求:采用主函数与子函数的模式,子函数is_prime(K)用来判断k是否是素数,如果是,则返回1,否则返回0;主函数则用来遍历2-N之间的每一个整数,如果该数是素数则记录到数组中,并且计数器加1,最终函数返回记录了N之内的所有素数的数组,以及素数的个数。

以下为(1)的提示性代码,供参考;

function [prime,num] = prime_N(N)      %主函数prime_N()

%此函数用于找出给定的N之内的所有素数

% 函数[prime,num] = prime_N(N) 中,返回的数组prime记录了N之内的所有素数,

%num记录了N之内的素数的总数。

num=0;           %Num记录发现的素数的个数,初始值为0

%采用for循环,挨个判断2-N之间的每一个数是否为素数。

%判断某个数是否为素数的任务通过调用子函数is_prime() 完成。


end


function y = is_prime(N)                 %子函数is_prime()

%子函数 is_prime(N) 用于判断给定的数N是否是素数

%如果给定的N是素数,则返回1,否则返回0

flag=0;                %用flag来记录N的因子的个数,初始值为0

m=fix(sqrt(N));    %寻找N的因子时,只需要试到fix(sqrt(N))

%for循环来历遍2-m之间的所有数,如果N能被2-m之间的某个数整除,说明找到了一个因子,则因子记录器flag加1,并跳出循环

%如果flag==0 ,说明前面没有找到N的因子,既N是素数,返回1,否则返回0

end



(2)编写一段代码,通过调用prime_N()函数,找出1000之内的所有孪生素数对,并输出这些孪生素数对。该问的提示性代码:

N=1000;

[prime,num] = prime_N(N) 

total=0;

for k=1:num-1

     if  ??

         total=total+1;

         fprintf('找到一对孪生素数:%d  , %d  \n' ,prime(k), prime(k+1) );

     end

end

fprintf('%d 之内共找到%d对孪生素数 \n' ,N, total );




最终调用效果如下(参考):








作业7:编写程序,输入一个大于2的正整数,则输出该正整数的3x+1序列。

方法1:采用普通脚本程序形式。

用input函数得到一个正整数,然后求出该数的3x+1序列,然后按照每行10个数的形式输出屏幕,再输出该3x+1序列的长度、最大值、最大值与初始值的比值。

效果如下:



方法2:采用函数形式。编写一个名为 my3x1( ) 的函数,输入正整数N,输出该数的3x+1序列的数组x,序列的长度L(既数组m的长度),序列中的最大值m_max,序列中最大值与初始值的比值。

function [m,L,m_max,Ratio]=my3x1(N)

........

........

end


调用该函数,效果如下:






作业8:编写一个中值滤波函数:zzlb() , 用于滤除图像的椒盐噪声

%以下代码为提示性代码:

function  pic_new=zzlb(pic_old)             

% 本函数可以对充满椒盐噪声的图像进行滤波,返回滤除椒盐噪声后的干净图像

%函数pic_new=zzlb(pic_old)中,输入参数pic_old为含有椒盐噪声的原始图像(uint8格式)

%函数返回值pic_new则为消除了椒盐噪声后的干净图像

[n,m,d]=size(pic_old)     %获取图像pic_old的维度


%判断d等1还是等于3?如果等于1,则pic_old为灰度图,执行以下操作

%采用双层for循环,对pic-old进行中值滤波,得到滤波后的干净图像pic_new

..........

..........


     for i=2:n-1          %在p_noise的2到row-1行,2到col-1列的区域

             for j=2:m-1

..........

..........

..........


%如果d=3,则表示pic_old为彩色图,这时,需要对RBG每一个通道都进行上述类似的中值滤波,

%得到3个滤波后的2维数组,再合并成一个3维数组,即彩色的pic_new

..........

..........

..........


end


调用和使用zzlb函数效果如下图




作业所用含椒盐噪声图像如下:














                     中值滤波与椒盐噪声基础知识



(1)椒盐噪声(salt-and-pepper noise)椒盐噪声是由图像传感器,传输信道,解码处理等产生的黑白相间的亮暗点噪声。所谓椒盐,椒就是黑,盐就是白,椒盐噪声就是在图像上随机出现黑色(0)与白色(255)的像素。椒盐噪声是一种因为信号脉冲强度引起的噪声,产生该噪声的算法也比较简单。





(2)中值滤波

    什么是中值?在一连串数字{9,6,7,1,4}中,数字6就是这串数字的中值(中位数)。由此我们可以应用到图像处理中。

    中值滤波:简单来说,假如怀疑某点被椒盐噪声破坏了(灰度值变成了0或255),那么就是用这个点的周边的像素的中位值来代替当前像素点的值;即,将滤波器范围内的像素的灰度值,进行排序,选出中央值作为这个像素的灰度值

     具体过程:一般采用3*3的区域(这样恰好当前点位于九宫格的中心)在图像上按照从左到右从上到下的方式扫过(历遍)整个图像;扫描时,中心点每次停到一个位置就检测一下该点是否是潜在的噪声点?如果是就做以下操作:把当前点作为中心点,构建一个3*3的临时小矩阵,对矩阵里面的9个像素的灰度值进行排序,找出中位数,最后将这个3*3区域的中心点的灰度值用刚才找到的中位数来更新。

         

      

其中3*3区域(九宫格)内的中位数g的产生过程如下:





(3)椒盐噪声的中值滤波:对于椒盐噪声,因为噪点的值往往很极端,要么值为255的白点,要么是值为0的黑点,所以执行中位数滤波时,并不是对每一个像素点都要进行中位数滤波,只有满足当前点为255或0时,才执行前述的中位数滤波。这样可以在滤波的同时,减小因为滤波引起的图像模糊。

效果图:




(4)图像处理时的边缘的填充问题:

在对图像应用滤波器进行过滤时,边界填充问题是一个需要处理的问题。


一般来说,有3种处理的方法。

1. 不做边界处理

不对图像的边界作任何处理,在对图像进行滤波时,滤波器没有作用到图像的四周,因此图像的四周没有发生改变。

 

2. 填充0

对图像的边界做扩展,在扩展边界中填充0,对于边长为2k+1的方形滤波器,扩展的边界大小为k,若原来的图像为[m, n],则扩展后图像变为[m+2k, n+2k]。进行滤波之后,图像会出现一条黑色的边框。

 

3. 填充最近像素值

扩展与 填充0 的扩展类似,只不过填充0的扩展是在扩展部分填充0,而这个方法是填充距离最近的像素的值。







================================

================================









课外补充:



思考:如何设置函数的参数为可变数目的参数?



MATLAB中的nargin与varargin


(1)nargin的用法:

nargin:number of function input arguments,指的是一个函数的输入变量的个数。

用法:nargin或着nargin(fx), 其中fx指的是一个函数名或着函数句柄。

当一个函数的参数中含有varargin变量时,这时候返回值为负。

下面举例说明:

用于nargin:

function c = addme(a, b)
switch nargin
case 2
 c= a + b;
case 1
 c = a + a ;
otherwise
  c = 0;
end


返回的结果为 a + b。

用到nargin(fx):

fx = 'addme';
nargin(fx)
ans = 
    2

当含有 varargin参数时,

function mynewplot(x, y, varargin)
 
fx = 'mynewplot';
nargin(fx)
ans = 
-3

(2)varargin的用法:

varargin:Variable-length input arguments list. 即指的是变输入参数列表;

varargin 就是一个输入 变量,它可以是任意个不定个数的输入参数。要求:第一,指明它用小写的字母表示;第二,在函数的参数列表中,varargin放在最后面。    当函数执行时,varargin就是一个1*n的cell数组。下面我们举例说明:

第一个例子:

function varlist( vargarin)
fprintf(' Number of arguments: %d\n',nargin);
celldisp(varargin)

varlist( ones(3), 'some text', pi)
Number of arguments:3
varargin{1} = 
    1    1    1
    1    1    1
    1    1    1
varargin{2} = 
some text
varargin{3} = 
3.1416


第二个例子:


function varlist2(x, y, varargin)
fprintf('Total number of inputs = %d\n', nargin);
nVarargs = length(varargin);
fprintf('Inputs in varargin( %d ) :\n', nVarargs);
for k = 1 : nVarargs
fprintf('    %d ', varargin{k} )
end



varlist2( 10, 20, 30, 40, 50)
Total number of inputs = 5
Inputs in varargin(3):
30
40
50



例1:利用nargin、varargin函数来改进冒泡排序算法,使得输入参数可变。


function  B = mysort(A , varargin)

%这是一个冒泡排序算法,对输入的数组A进行排序

%order=1,表示按照从小到大进行排序,如果order=0,则从大到小排序

n=length(A);     %测量输入数组A的长度

if  nargin==1   %当输入参数数量为1时,order=1
   order=1;
else        %否则,order的值为输入的第二个参数(可变参数varargin)的内容

   order=varargin{1};
end


if  order==1

    for ii=1:n-1           % ii表示趟数

         for  jj=1:n-ii     % 开始两两比较,n-ii表示每一趟内部需要进行的两两比较的总数目

               if  A(jj)>=A(jj+1)      %如果左边元素大于右边元素,则交换两者位置。

                   p=A(jj);

                   A(jj)=A(jj+1);

                   A(jj+1)=p;

               end

         end

    end

end



if  order==0

    for ii=1:n-1                     % ii表示趟数

         for  jj=n:-1:ii+1          % 开始两两比较,n-ii表示每一趟内部需要进行的两两比较的总数目

               if  A(jj)>=A(jj-1)  %如果左边元素大于右边元素,则交换两者位置。

                   p=A(jj);

                   A(jj)=A(jj-1);

                   A(jj-1)=p;

               end

         end

    end

end


B = A;


end  



例2:假设我们想生成一个 nxm 阶的 Hilbert 矩阵, 它的第 i 行第 j 列的元素值为 1/(i+j-1)。我们想在编写的函数中实现下面几点:

  • 如果只给出一个输入参数,则会自动生成一个方阵,即令 m=n

  • 在函数中给出合适的帮助信息,包括基本功能、调用方式和参数说明

  • 检测输入和返回变量的个数,如果有错误则给出错误信息

  如果调用时不要求返回变量,则将显示结果矩阵。其实在编写程序时养成一个好的习惯,无论对程序设计者还是对程序的维护者、使用者都是大有裨益的。

  采用 MATLAB 函数编写格式和上述要求,我们可以编写出一个函数

function A=myhilb(n, m)


%MYHILB a demonstrative M-function.
%A=MYHILB(N, M) generates an N by M Hilbert matrix A.
%A=MYHILB(N) generates an N by N square Hilbert matrix.
%MYHILB(N,M) displays ONLY the Hilbert matrix, but do not return any
%matrix back to the calling function.


if nargout>1,

    error('Too many output arguments.');

end


if nargin==1,

     m=n;

elseif nargin==0 | nargin>2

     error('Wrong number of iutput arguments.');

end 


A1=zeros(n,m);


for i=1: n   

    for j=1:m

        A1(i,j)=1/(i+j-1);

    end

end

这样规范编写的函数用 help 命令可以显示出其帮助信息:

>> help myhilb
MYHILB a demonstrative M-function.
A=MYHILB(N, M) generates an N by M Hilbert matrix A.
A=MYHILB(N) generates an N by N square Hilbert matrix.
MYHILB(N,M) displays ONLY the Hilbert matrix, but do not return any
matrix back to the calling function.

  有了函数之后,可以采用下面的各种方法来调用它,并产生出所需的结果。

>> A=myhilb(3,4)
A =
1.0000 0.5000 0.3333 0.2500
0.5000 0.3333 0.2500 0.2000
0.3333 0.2500 0.2000 0.1667
>> A=myhilb(4)
A =
1.0000 0.5000 0.3333 0.2500
0.5000 0.3333 0.2500 0.2000
0.3333 0.2500 0.2000 0.1667
0.2500 0.2000 0.1667 0.1429
>> myhilb(4)
1.0000 0.5000 0.3333 0.2500
0.5000 0.3333 0.2500 0.2000
0.3333 0.2500 0.2000 0.1667
0.2500 0.2000 0.1667 0.1429



数组的全排列实现:

全排列是将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个。现以{1, 2, 3, 4, 5}为例说明如何编写全排列的递归算法。

1、首先看最后两个数4, 5。 它们的全排列为4 5和5 4, 即以4开头的5的全排列和以5开头的4的全排列。

由于一个数的全排列就是其本身,从而得到以上结果。

2、再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。

即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合.

从而可以推断,设一组数p = {r1, r2, r3, ... ,rn}, 全排列为perm(p),pn = p - {rn}。

因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。当n = 1时perm(p} = r1。

为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。


%全排列

clear

x=[1 2 3 4];

N=length(x);

myperm(x,1,N)


function myperm(s,n1,n2)

if n1==n2

   fprintf('str is -------:%s\n',num2str(s))

else

   for k=n1:n2       

       temp=s(n1);

       s(n1)=s(k);

       s(k)=temp;

       myperm(s,n1+1,n2);

        

   end

end

end


function qpl= quanpailie(nums)

%UNTITLED2 此处显示有关此函数的摘要

%   此处显示详细说明

global temp;

global qpl;

if length(temp)==length(nums)

    qpl=[qpl;temp]

    temp=[];

else

    for i=1:length(nums)

        if sum(temp==nums(i))==1

            continue

        else

           temp=[temp,nums(i)]

        end

        temp=quanpailie(nums);

    end

    

end


end



对递归的理解的要点主要在于放弃!


放弃你对于理解和跟踪递归全程的企图,只理解递归两层之间的交接,以及递归终结的条件。

想象你来到某个热带丛林,意外发现了十层之高的汉诺塔。正当你苦苦思索如何搬动它时,林中出来一个土著,毛遂自荐要帮你搬塔。他名叫二傻,戴着一个草帽,草帽上有一个2字,号称会把一到二号盘搬到任意柱。

你灵机一动,问道:“你该不会有个兄弟叫三傻吧?”
“对对,老爷你咋知道的?他会搬一到三号盘。“
”那你去把他叫来,我不需要你了。“
于是三傻来了,他也带着个草帽,上面有个3字。

你说:”三傻,你帮我把头三个盘子移到c柱吧。“
三傻沉吟了一会,走进树林,你听见他大叫:”二傻,出来帮我把头两个盘子搬到C!“

由于天气炎热你开始打瞌睡。朦胧中你没看见二傻是怎么工作的,二傻干完以后,走入林中大叫一声:“老三,我干完了!”

三傻出来,把三号盘从A搬到B,然后又去叫二傻:“老二,帮我把头两个盘子搬回A!”

余下的我就不多说了,总之三傻其实只搬三号盘,其他叫二傻出来干。最后一步是三傻把三号盘搬到C,然后呼叫二傻来把头两个盘子搬回C

事情完了之后你把三傻叫来,对他说:“其实你不知道怎么具体一步一步把三个盘子搬到C,是吧?”

三傻不解地说:“我不是把任务干完了?”

你说:“可你其实叫你兄弟二傻干了大部分工作呀?”

三傻说:“我外包给他和你屁相干?”

你问到:“二傻是不是也外包给了谁?“

三傻笑了:“这跟我有屁相干?”

你苦苦思索了一夜,第二天,你走入林中大叫:“十傻,你在哪?”

一个头上带着10号草帽的人,十傻,应声而出:“老爷,你有什么事?”

“我要你帮把1到10号盘子搬到C柱“

“好的,老爷。“十傻转身就向林内走。

“慢着,你该不是回去叫你兄弟九傻吧“

“老爷你怎么知道的?“

“所以你使唤他把头九个盘子搬过来搬过去,你只要搬几次十号盘就好了,对吗?“

“对呀!“

“你知不知道他是怎么干的?“

“这和我有屁相干?“

你叹了一口气,决定放弃。十傻开始干活。树林里充满了此起彼伏的叫声:“九傻,来一下!“ “老八,到你了!““五傻!。。。“”三傻!。。。“”大傻!“

你注意到大傻从不叫人,但是大傻的工作也最简单,他只是把一号盘搬来搬去。

若干年后,工作结束了。十傻来到你面前。你问十傻:“是谁教给你们这么干活的?“

十傻说:“我爸爸。他给我留了这张纸条。”

他从口袋里掏出一张小纸条,上面写着:“照你帽子的号码搬盘子到目标柱。如果有盘子压住你,叫你上面一位哥哥把他搬走。如果有盘子占住你要去的柱子,叫你哥哥把它搬到不碍事的地方。等你的盘子搬到了目标,叫你哥哥把该压在你上面的盘子搬回到你上头。“

你不解地问:“那大傻没有哥哥怎么办?“

十傻笑了:“他只管一号盘,所以永远不会碰到那两个‘如果’,也没有盘子该压在一号上啊。”

但这时他忽然变了颜色,好像泄漏了巨大的机密。他惊慌地看了你一眼,飞快地逃入树林。

第二天,你到树林里去搜寻这十兄弟。他们已经不知去向。你找到了一个小屋,只容一个人居住,但是屋里有十顶草帽,写着一到十号的号码。