Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

信号与线性系统学习笔记

发表于 2019-07-05 | 更新于 2019-07-08

信号与线性系统是学习自动控制理论的前置专业基础课,可惜当年就学得似懂非懂,过了这么多年,知识早就忘了,现在捡起来温习一下,算是温故而知新吧。

信号与系统基础

连续的或离散的动态系统,按其基本特性可分为线性的与非线性的;时变的与时不变的;因果的与非因果的;稳定与非稳定的。

连续系统的时域分析

对单输入与单输出的线性时不变连续系统,通常适用n阶常系数线性微分方程表示:

$$y^{(n)}(t)+a_{n-1}y^{(n-1)}(t)+…+a_1y^{(1)}(t)+a_0y(t) \
=b_mf^{(m)}(t)+b_{m-1}f^{(m-1)}(t)+…+b_1f^{(1)}(t)+b_0f(t) \tag{1}$$

或缩写为

$$\sum_{i=0}^{n}a_iy^{(i)}(t)=\sum_{j=0}^{m}b_jy^{(j)}(t)$$

其中$f(t)$为激励,$y(t)$为响应,$a_i(i=0,1,…,n)$和$b_j(j=0,1,…,n)$均为常数,$a_n=1$。

该微分方程的全解由齐次解$y_h(t)$和特解$y_p(t)$组成,即

$$y(t)=y_h(t)+y_p(t)$$

参考链接

  1. 1. 信号与线性系统—究竟在学啥?,by Dean Rossi.
  2. 2. 信号与线性系统——信号的分解,by Dean Rossi.
  3. 3. 信号与线性系统——啥?卷积?,by Dean Rossi.
  4. 4. 信号与线性系统——你好,傅里叶,by Dean Rossi.
  5. 5. 信号与线性系统——F.T&L.T,by Dean Rossi.
  6. 7. 信号与线性系统——系统的分析方法,by Dean Rossi.

傅里叶级数和变换学习笔记

发表于 2019-07-01 | 更新于 2019-10-04

在数学中,傅里叶级数(Fourier series)能将任何周期函数或周期信号分解成一个(可能由无穷个元素组成的)简单振荡函数的集合,即正弦函数和余弦函数(或者,等价地使用复指数)。傅里叶变换(Fourier transform)是一种线性积分变换,用于信号在时域(或空域)和频域之间的变换,在物理学和工程学中有许多应用。傅里叶变换就像化学分析,确定物质的基本成分;信号来自自然界,也可对其进行分析,确定其基本成分。

傅里叶级数公式

傅里叶级数的公式:

$$
f(t) =\frac{a_{0}}{2}+a_{1}cos(\omega t)+b_{1}sin(\omega t) \
+a_{2}cos(2\omega t)+b_{2}sin(2\omega t) +…\
=\frac{a_{0}}{2}+\sum_{n=1}^{\infty}{[a_{n}cos(n\omega t)+b_{n}sin(n\omega t)]} \tag{1}
$$

其中:
$$
a_{n}=\frac{2}{T}\int_{t_{0}}^{t_{0}+T}f(t)cos(n\omega t)dt \tag{2}
$$
$$
b_{n}=\frac{2}{T}\int_{t_{0}}^{t_{0}+T}f(t)sin(n\omega t)dt \tag{3}
$$

傅里叶级数的收敛性

若傅里叶级数不收敛于$f(t)$,则不能在两者之间画等号。关于傅里叶级数的收敛性,最常用的为狄利克雷条件:

对于一个周期为$2\pi$的函数$f(x)$,如果它满足:

(1)在一个周期内连续或只有有限个第一类间断点;

(2)在一个周期内只有有限个极值点。

那么$f(x)$的傅里叶级数收敛于$\frac{f(x+0)+f(x-0)}{2}$。

狄利克雷条件只是傅里叶级数收敛的充分条件,而非必要条件,级数收敛不代表该条件成立。

下面给出一个周期函数的傅里叶级数的计算示例。

周期为$2\pi$的函数$f(x)$,在$(-\pi,\pi)$上$f(x)=x$,求$f(x)$的傅里叶级数。

$$a_n=\frac{1}{\pi}\int_{-\pi}^{\pi}x\mathrm{cos}nx\mathrm{d}x=0$$

$$b_n=\frac{1}{\pi}\int_{-\pi}^{\pi}x\mathrm{sin}nx\mathrm{d}x=(-1)^{n+1}\frac{2}{n}$$

狄利克雷条件显然成立,所以

$$f(x)=\sum^{\infty}_{n=1}(-1)^{n+1}\frac{2}{n}\mathrm{sin}nx$$

傅里叶级数的指数形式

令$i$表示虚数单位,傅里叶级数的指数形式为:

$$f(t)=\sum^{\infty}_{n=-\infty}c_ne^{in\omega t}$$

其中,

$$c_n=\frac{1}{T}\int^{T}_{0}f(t)e^{-in\omega t}\mathrm{d}t$$

指数形式与三角形式是相等的,推导如下:

$$\begin{aligned}
&\quad\sum^{\infty}{n=-\infty}c_ne^{in\omega t}\
&=c_0+\sum^{\infty}
{n=1}(c_ne^{in\omega t}+c_{-n}e^{-in\omega t}) \
&=c_0+\sum^{\infty}{n=1}[(c_n+c{-n})\mathrm{cos}n\omega t+i(c_n-c_{-n})\mathrm{sin}n\omega t] \
&=\frac{a_0}{2}+\sum_{n=1}^{\infty}(a_n \mathrm{cos}n \omega t+b_n \mathrm{sin}n \omega t)
\end{aligned}
$$

傅里叶变换

傅里叶变换是傅里叶级数在非周期函数上的推广。对非周期函数$f(x)$,其周期$T\rightarrow\infty$。因为$\omega_0=\frac{2\pi}{T}$,则$\omega_0\rightarrow0$。

观察傅里叶级数的指数形式

$$f(t)=\sum^{\infty}_{n=-\infty}c_ne^{in\omega t} \tag{1}$$

其中,

$$c_n=\frac{1}{T}\int^{T}_{0}f(t)e^{-in\omega t}\mathrm{d}t \tag{2}$$

当 $\omega_0\rightarrow0$ 时, $n\omega_0$ 从原本的离散变化变成了连续变化, $c_n$ 也就可以表示为关于 $n\omega_0$ 的函数 $F(n\omega_0)$ 。

傅里叶级数中公式(2)的积分的上下限不一定是$0$到$T$,只需要$f(t)$的一个周期就可以了。即对于任意的$x_0$, 公式(2)可表示为:

$$c_n=\frac{1}{T}\int^{x_0+T}_{x_0}f(t)e^{-in\omega_0 t}\mathrm{d}t \tag{3}$$

这个积分需要积一整个周期,而此时的周期为无穷大,也就是整个定义域上都需要积,所以要从$-\infty$积分到$\infty$。

只需要让上式中的 $T\rightarrow\infty$ , $\omega_0\rightarrow0$ ,便可以得到 $F(n\omega_0)$ 的表达式。不妨令 $\omega=n\omega_0$ ,就得到了

$$F(\omega)=\frac{1}{T}\int^{\infty}_{-\infty}f(t)e^{-i\omega t}\mathrm{d}t \tag{4}$$

由于$\frac{1}{T}\rightarrow0$,我们先丢弃$\frac{1}{T}$,之后用到$F(\omega)$在乘回来,于是令:

$$F(\omega)=\int^{\infty}_{-\infty}f(t)e^{-i\omega t}\mathrm{d}t \tag{5}$$

将公式(5)代入公式(1),并代入$\frac{1}{T}=\frac{\omega_0}{2\pi}$,则有:

$$f(t)=\frac{1}{2\pi}\sum^{\infty}_{n=-\infty}F(n\omega_0)e^{in\omega_0 t}\omega_0 \tag{6}$$

因为 $\omega=n\omega_0$ ,每次 $\omega$ 的增量 $d\omega$ 都是由于 $n$ 变为 $n+1$ 造成的,所以

$$\mathrm{d}\omega=(n+1)\omega_0-n\omega_0=\omega_0$$

同时 $n\omega_0$ 连续变化,原本的离散意义下的求和就该变为连续意义下的积分,于是公式(6)变形为:

$$f(t)=\frac{1}{2\pi}\int_{-\infty}^{\infty}F(\omega)e^{i\omega t}\mathrm{d}\omega \tag{7}$$

至此得到傅里叶变换的两个公式:

$$F(\omega)=ℱ[f(t)]=\int^{\infty}_{-\infty}f(t)e^{-i\omega t}\mathrm{d}t$$

$$f(t)=ℱ^{-1}[F(\omega)]=\frac{1}{2\pi}\int_{-\infty}^{\infty}F(\omega)e^{i\omega t}\mathrm{d}\omega$$

傅里叶变换条件

由于傅里叶变换是从傅里叶级数推导得来的,所以还是狄利克雷条件,不过此时还要加上第三条, $f(t)$ 在一个周期内绝对可积。

这一个条件在 $f(t)$ 为周期函数时,可以由前两个条件推出来,因为周期和函数值均为有限值,所以在一个周期内一定绝对可积。但是推广到傅里叶变换后,这个推导就不成立了,需要单独判定第三个条件。

参考链接

  1. 傅里叶分析之掐死教程,by Heinrich.
  2. 傅里叶级数和傅里叶变换,by 木不shi丁.
  3. 傅里叶级数和傅里叶变换是什么关系?,by 马同学.
  4. 傅里叶级数与傅里叶变换推导,by 令狐哦打.
  5. 傅里叶级数,by wikipedia.
  6. 傅里叶变换,by wikipedia.
  7. 傅里叶系列(一)傅里叶级数的推导,by ElPsyCongree.
  8. 傅里叶变换(一) 傅里叶级数,by misaka.
  9. 傅里叶变换交互式入门,by Jez Swanson.

使用python3+PyQt5+PyCharm桌面GUI开发

发表于 2019-06-29 | 更新于 2021-03-07

下面简单介绍基于Python3、PyQt5和PyCharm进行桌面GUI开发。

环境配置

安装python3

PyQt5所支持的python版本是从3.5开始的,因此安装的Python3版本必须大于3.5。

安装PyQt5

1
2
pip3 install PyQt5
pip3 install PyQt5-tools

安装PyCharm

安装完PyCharm后,需要配置Qt Designer和PyUIC,前者用于设计UI,后者用于将UI转成Py文件。

具体步骤请参考程序员之路:python3+PyQt5+pycharm桌面GUI开发。

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'clearWaterPrintGui.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.label_DirPath = QtWidgets.QLabel(Form)
self.label_DirPath.setGeometry(QtCore.QRect(16, 52, 72, 15))
self.label_DirPath.setObjectName("label_DirPath")
self.label_ExtName = QtWidgets.QLabel(Form)
self.label_ExtName.setGeometry(QtCore.QRect(31, 112, 72, 15))
self.label_ExtName.setObjectName("label_ExtName")
self.buttonBox = QtWidgets.QDialogButtonBox(Form)
self.buttonBox.setGeometry(QtCore.QRect(190, 260, 193, 28))
self.buttonBox.setInputMethodHints(QtCore.Qt.ImhNone)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.lineEdit_DirPath = QtWidgets.QLineEdit(Form)
self.lineEdit_DirPath.setGeometry(QtCore.QRect(100, 50, 281, 21))
self.lineEdit_DirPath.setObjectName("lineEdit_DirPath")
self.lineEdit_ExtName = QtWidgets.QLineEdit(Form)
self.lineEdit_ExtName.setGeometry(QtCore.QRect(100, 112, 281, 21))
self.lineEdit_ExtName.setObjectName("lineEdit_ExtName")

self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)

def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.label_DirPath.setText(_translate("Form", "文件路径:"))
self.label_ExtName.setText(_translate("Form", "扩展名:"))

# 下面代码是手动添加的
if __name__=="__main__":
import sys
from PyQt5.QtGui import QIcon
app=QtWidgets.QApplication(sys.argv)
widget=QtWidgets.QWidget()
ui=Ui_Form()
ui.setupUi(widget)
# widget.setWindowIcon(QIcon('web.png'))#增加icon图标,如果没有图片可以没有这句
widget.show()
sys.exit(app.exec_())

参考链接

  1. 程序员之路:python3+PyQt5+pycharm桌面GUI开发,by 莫水千流.
  2. 使用PyQt快速开发GUI应用,by Prayer.
  3. PyQt,by wikipedia.
  4. Qt Widgets、QML、Qt Quick的区别,by 云水.
  5. Q_OBJECT宏的作用,by 沈子恒.

Python程序打包成exe

发表于 2019-06-29

大部分人是普通人,不知道如何运行Python脚本程序,因此有必要将Python脚本程序打包成可执行文件,免去安装Python环境,提高Python脚本程序的可用性。

打包方法

Python脚本程序的发布有三种方法:

  • .py文件:对于开源项目或者源码没那么重要的,直接提供源码,需要使用者自行安装Python并且安装依赖的各种库。
  • .pyc文件:有些公司或个人因为机密或者各种原因,不愿意源码被运行者看到,可以使用pyc文件发布,pyc文件是Python解释器可以识别的二进制码,故发布后也是跨平台的,需要使用者安装相应版本的Python和依赖库。
  • 可执行文件:对于非码农用户,最简单的方式就是提供一个可执行文件,只需要把用法告诉他即可。比较麻烦的是需要针对不同平台需要打包不同的可执行文件(Windows, Linux, Mac,…)。

下面介绍将Python脚本程序打包成可执行程序的各种工具。

表1 各种打包工具的对比
Solution Windows Linux OS X Python 3 One file mode Zipfile import Eggs pkg_resources support
bbFreeze yes yes yes no no yes yes yes
py2exe yes no no yes yes yes no no
pyInstaller yes yes yes yes yes no yes no
cx_Freeze yes yes yes yes no yes yes no
py2app no no yes yes no yes yes yes

从上述对Python脚本程序打包工具对比可知,推荐使用pyInstaller。

PyInstaller打包示例

安装PyInstaller

1
pip install pyinstaller

打包Python脚本

打包Python脚本成单独的 .exe 文件。

1
pyinstaller -F yourprogram.py

参考链接

  1. 如何将 Python 程序打包成 .exe 文件?,by 刘哈哈.
  2. Python程序打包成exe可执行文件,by 知行流浪.

Python命令行参数解析示例

发表于 2019-06-29

最新需要一个小程序实现解析命令行参数、遍历指定文件夹,处理指定扩展名的文件。于是简单用python实现一下。

库选择

参数解析

sys.argv

解析Python中命令行参数的最传统的方法是通过sys.argv。但这种方法比较古老,灵活性很差,同时解析出来的参数都是str类型。但在编写简单脚本,参数较少且固定时比较方便。

getopt模块

getopt模块是专门处理命令行参数的模块,用于获取命令行选项和参数,也就是sys.argv。命令行选项使得程序的参数更加灵活。支持短选项模式(-)和长选项模式(–)。

optparse模块

optparse,功能强大,易于使用,可以方便地生成标准的、符合Unix/Posix 规范的命令行说明。但在Python2.7后就已经弃用不再维护。

argparse模块

argparse模块是Python内置的参数解析模块,使用起来比较简单且功能强大。

ArgumentParser类创建时的参数如下:

  • prog - 程序的名字(默认:sys.argv[0])
  • usage - 描述程序用法的字符串(默认:从解析器的参数生成)
  • description - 参数帮助信息之前的文本(默认:空)
  • epilog - 参数帮助信息之后的文本(默认:空)
  • parents - ArgumentParser 对象的一个列表,这些对象的参数应该包括进去
  • formatter_class - 定制化帮助信息的类
  • prefix_chars - 可选参数的前缀字符集(默认:‘-‘)
  • fromfile_prefix_chars - 额外的参数应该读取的文件的前缀字符集(默认:None)
  • argument_default - 参数的全局默认值(默认:None)
  • conflict_handler - 解决冲突的可选参数的策略(通常没有必要)
  • add_help - 给解析器添加-h/–help 选项(默认:True)

add_argument函数的参数如下:

  • name or flags - 选项字符串的名字或者列表,例如foo 或者-f, –foo。
  • action - 在命令行遇到该参数时采取的基本动作类型。
  • nargs - 应该读取的命令行参数数目。
  • const - 某些action和nargs选项要求的常数值。
  • default - 如果命令行中没有出现该参数时的默认值。
  • type - 命令行参数应该被转换成的类型。
  • choices - 参数可允许的值的一个容器。
  • required - 该命令行选项是否可以省略(只针对可选参数)。
  • help - 参数的简短描述。
  • metavar - 参数在帮助信息中的名字。
  • dest - 给parse_args()返回的对象要添加的属性名称。

参数解析模块比较

  • getopt,只能简单的处理命令行参数,无法解析一个参数多个值的情况,如 –file file1 file2 file3。
  • optparse,功能强大,易于使用,可以方便地生成标准的、符合Unix/Posix 规范的命令行说明。但在Python2.7后就已经弃用不再维护。
  • argparse,使其更加容易的编写用户友好的命令行接口。它所需的程序进程了参数定义,argparse将更好的解析sys.argv。同时argparse模块还能自动生成帮助及用户输入错误参数时的提示信息。

文件夹遍历

文件夹遍历有两种方法:

  • 使用os.walk
1
2
3
4
5
6
7
8
9
# -*- coding: utf-8 -*- 
import os
def Test1(rootDir):
list_dirs = os.walk(rootDir)
for root, dirs, files in list_dirs:
for d in dirs:
print os.path.join(root, d)
for f in files:
print os.path.join(root, f)
  • 使用os.listdir
1
2
3
4
5
6
7
8
# -*- coding: utf-8 -*- 
import os
def Test2(rootDir):
for lists in os.listdir(rootDir):
path = os.path.join(rootDir, lists)
print path
if os.path.isdir(path):
Test2(path)

文件处理

Python内置了读写文件的函数,用法和C是兼容的。本节介绍内容大致有:文件的打开/关闭、文件对象、文件的读写等。

代码示例

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# -*- coding: utf-8 -*- 

import os
import argparse

# 遍历文件夹
def traverse(pathName,extName):
extNameList=extName.split(' ')
print(extNameList)
list_dirs = os.walk(pathName)
for root, dirs, files in list_dirs:
for f in files:
targetExt=os.path.splitext(f)[-1]
if targetExt.lower() in extNameList:
clearWaterPrint(os.path.join(root, f))

def clearWaterPrint(fileName):
print(fileName)
fileOrgin=open(fileName,'rb')
fileTarget=open(fileName+'.exe','wb')
fileTarget.write(fileOrgin.read())
fileOrgin.close()
os.remove(fileName)
fileTarget.close()
os.rename(fileName+'.exe',fileName)

# Driver Code
if __name__ == '__main__':
# 首先创建一个ArgumentParser对象
parser = argparse.ArgumentParser(description='Process the water print!')
# 添加--path设置文件目录
parser.add_argument('-p','--path', type = str,dest='pathName', help='give the path of directory',
default = './')
# 添加--ext设置文件目录
parser.add_argument('-e','--ext', type = str,dest='extName', help='give the extension name of file',
default = '.doc .docx .wav .txt .xml .dot .html .jpg .png',nargs = '*')
#返回一个命名空间,如果想要使用变量,可用args.attr
args = parser.parse_args()

# Calling traverse() function
traverse(args.pathName,args.extName)

使用方法如下:

1
2
python3 clearWaterPrint.py -h
python3 clearWaterPrint.py -p /home/test -e .doc .ppt

参考链接

  1. Python的命令行参数解析,by Tyan.
  2. Python中最好用的命令行参数解析工具,by Mingle Wong.
  3. Python遍历文件夹的两种方法比较,by likecao.
  4. 读写字节数据,by python3-cookbook.
  5. 读写二进制文件,by funhacks.

C++标准演化简介

发表于 2019-06-26

最近阅读采用C++编写的MAVROS源码,遇到很多C++语言的新特性,理解起来很费劲,因此,特地分析一下C++标准演变过程,学习其进化过程中引入的新特性,提高C++源码阅读效率。

C++标准演变

2017年12月05日,ISO C++ 委员会正式发布了 C++ 17 标准,官方名称为 ISO/IEC 14882:2017。之前发布的C++标准有C++14、C++11、C++03、C++98。

C++98

C++98是第一个C++标准。它分为两个部分:核心语言和C++标准程序库;后者包含了大部分标准模板库和C标准程序库的稍加修改版本。存在许多不属于标准部分的C++程序库,且使用外部链接,程序库甚至可以用C撰写。

C++标准程序库充分吸收了C标准程序库,并佐以少许的修改,使其与C++良好的运作。另一个大型的程序库部分,是以标准模板库(STL)为基础,STL于1994年2月正式成为ANSI/ISO C++。它提供了实用的工具,如容器类(如:Array和Vector),迭代器(广义指针)提供容器以类似数组的访问方式,以及泛型算法进行搜索和排序的运算。此外还提供了(multi)map和(multi)set,它们都共享相似的成员函数。因此,以下成为可能,使用模板撰写泛型算法,它可以和任何容器或在任何以迭代器定义的序列上运作。如同C,使用#include指令包含标准表头,即可访问程序库里的功能。C++提供69个标准表头,其中19个不再赞成使用。

使用标准模板库(例如:使用std::vector或std::string来取代C风格的数组或字符数组)有助于导向更安全和更灵活的软件。

在STL在纳入C++标准以前,是来自HP和后来的SGI的第三方程式库,标准中并未称之为“STL”,它只是标准库中的一部分,但仍有许多人使用这个名称,以别于其它的标准库(输入/输出流、国际化、诊断、C程序库子集,等等)。 另外,如std::basic_string此类标准委员会添加的接口,有时也被误认为STL;实际上它们并不存在于原始的SGI STL中,在标准化后SGI STL才从标准库吸收加入其中。

C++03

C++03 是 C++ 语言国际标准的一个版本,正式名称是 ISO/IEC 14882:2003。该标准由国际标准化组织(ISO)和国际电工委员会(IEC)共同制定。

C++03 取代了 C++ 标准的前一个版本 C++98,后被 C++11 所取代。C++03 主要是在前一个版本的基础上针对实现方的一些问题进行了修复,从而在各个实现间达到一致、保持了可移植性。该版本共涉及 92 项核心语言缺陷报告、125 项库缺陷报告,所提供的新特性只有一项:值初始化(英语:value initialization)。

C++03 的第 69 号库缺陷报告非常值得一提,为了解决该问题,标准中加入了“std::vector 中的元素必须连续存储”的要求。

C++11

C++11,先前被称作C++0x,即ISO/IEC 14882:2011,是C++编程语言的一个标准。它取代第二版标准ISO/IEC 14882:2003(第一版ISO/IEC 14882:1998公开于1998年,第二版于2003年更新,分别通称C++98以及C++03,两者差异很小),且已被C++14取代。相比于C++03,C++11标准包含核心语言的新机能,而且扩展C++标准程序库,并入了大部分的C++ Technical Report 1程序库(数学的特殊函数除外)。 ISO/IEC JTC1/SC22/WG21 C++标准委员会计划在2010年8月之前完成对最终委员会草案的投票,以及于2011年3月召开的标准会议完成国际标准的最终草案。然而,WG21预期ISO将要花费六个月到一年的时间才能正式发布新的C++标准。为了能够如期完成,委员会决定致力于直至2006年为止的提案,忽略新的提案。最终于2011年8月12日公布,并于2011年9月出版。

2012年2月28日的国际标准草案是最接近于C++11标准的草案,差异仅有编辑上的修正。

像C++这样的编程语言,透过一种演化的的过程来发展其定义。这个过程不可避免地将引发与现有代码的兼容问题,在C++的发展过程中偶尔会发生。不过根据比雅尼·斯特劳斯特鲁普(C++的创始人并且是委员会的一员)表示,新的标准将几乎100%兼容于现有标准。

C++14

C++14是C++的现行标准的非正式名称,正式名称为”International Standard ISO/IEC 14882:2014(E) Programming Language C++”。C++14旨在作为C++11的一个小扩展,主要提供漏洞修复和小的改进。C++14标准的委员会草案(Committee Draft)N3690于2013年5月15日发表。工作草案(Working Draft)N3936已于2014年3月2日完成。最终的投票期结束于2014年8月15日,结果(一致通过)已于8月18日公布。

C++17

C++17又称C++1z,是C++的现行标准的非正式名称,正式名称为”International Standard ISO/IEC Programming Language C++”。C++17旨在作为大型扩展,最终的投票期将于2017年结束。

当前不少著名C++编译器已支持C++17仍未定案的草案(draft),例如最新的GCC6已支持C++ concept的C++事务型内存(Transactional Memory),Visual Studio与Clang当前都提供了modules。

C++新语言特性

Lambda函数与表示式

在标准C++,特别是当使用C++标准程序库算法函数诸如sort和find,用户经常希望能够在算法函数调用的附近定义一个临时的述部函数(又称谓词函数,predicate function)。由于语言本身允许在函数内部定义类别,可以考虑使用函数对象,然而这通常既麻烦又冗赘,也阻碍了代码的流程。此外,标准C++不允许定义于函数内部的类别被用于模板,所以前述的作法是不可行的。

C++11对lambda(即匿名函数)的支持可以解决上述问题。

一个lambda函数可以用如下的方式定义:

1
[](int x, int y) { return x + y; }

这个不具名函数的回返类型是decltype(x+y)。只有在lambda函数匹配”return expression”的形式下,它的回返类型才能被忽略。在前述的情况下,lambda函数仅能为一个述句。

在一个更为复杂的例子中,回返类型可以被明确的指定如下:

1
[](int x, int y) -> int { int z = x + y; return z + x; }

本例中,一个临时的参数z被创建用来存储中间结果。如同一般的函数,z的值不会保留到下一次该不具名函数再次被调用时。

如果lambda函数没有传回值(例如void),其回返类型可被完全忽略。

定义在与lambda函数相同作用域的参数引用也可以被使用。这种的参数集合一般被称作closure(闭包)。

1
2
3
4
5
6
[]      // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。

参考链接

  1. C++,by wikipedia.
  2. C++ 的历史,by cppreference.
  3. C++03,by wikipedia.
  4. C++11,by wikipedia.
  5. C++14,by wikipedia.
  6. C++17,by wikipedia.

有趣的数学游戏

发表于 2019-06-23 | 更新于 2019-06-24

记录一些有趣的数学游戏,与小伙伴分享。

三门问题

问题描述

三门问题(Monty Hall problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let’s Make a Deal。

问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)。参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门会否增加参赛者赢得汽车的机率?

问题解答

漫画:反直觉的 “三门问题”

海盗分金币问题

问题描述

经济学上有个“海盗分金”模型:是说5个海盗抢得100枚金币,他们按抽签的顺序依次提方案:首先由1号提出分配方案,然后5人表决,投票要超过半数同意方案才被通过,否则他将被扔入大海喂鲨鱼,依此类推。

“海盗分金”其实是一个高度简化和抽象的模型,体现了博弈的思想。在“海盗分金”模型中,任何“分配者”想让自己的方案获得通过的关键是事先考虑清楚“挑战者”的分配方案是什么,并用最小的代价获取最大收益,拉拢“挑战者”分配方案中最不得意的人们。

问题解答

漫画:有趣的海盗问题

参考链接

  1. 漫画:反直觉的 “三门问题”,by 小灰.
  2. 漫画:有趣的海盗问题,by 小灰.

gdb调试入门

发表于 2019-06-23

GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。 对于一名Linux下工作的c++程序员,gdb是必不可少的工具。

启动gdb

对C/C++程序的调试,需要在编译前就加上-g选项。对大型项目,一般选择Debug选项进行编译以 方便gdb调试。

1
$ g++ -g hello.cpp -o hello

调试可执行文件:

1
$gdb <program>

program也就是你的执行文件,一般在当前目录下。

gdb交互命令

启动gdb后,进入到交互模式,通过以下命令完成对程序的调试;注意高频使用的命令一般都会有缩写,熟练使用这些缩写命令能提高调试的效率;

运行

  • run:简记为 r ,其作用是运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令。
  • continue (简写c ):继续执行,到下一个断点处(或运行结束)
  • next:(简写 n),单步跟踪程序,当遇到函数调用时,也不进入此函数体;此命令同 step 的主要区别是,step 遇到用户自定义的函数,将步进到函数中去运行,而 next 则直接调用函数,不会进入到函数体内。
  • step (简写s):单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数的
  • until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。
  • until+行号: 运行至某行,不仅仅用来跳出循环
  • finish: 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
  • call 函数(参数):调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)
  • quit:简记为 q ,退出gdb

设置断点

  • break n (简写b n):在第n行处设置断点
    (可以带上代码路径和代码名称: b OAGUPDATE.cpp:578)
  • b fn1 if a>b:条件断点设置
  • break func(break缩写为b):在函数func()的入口处设置断点,如:break cb_button
  • delete 断点号n:删除第n个断点
  • disable 断点号n:暂停第n个断点
  • enable 断点号n:开启第n个断点
  • clear 行号n:清除第n行的断点
  • info b (info breakpoints) :显示当前程序的断点设置情况
  • delete breakpoints:清除所有断点

查看源代码

  • list :简记为 l ,其作用就是列出程序的源代码,默认每次显示10行。
  • list 行号:将显示当前文件以“行号”为中心的前后10行代码,如:list 12
  • list 函数名:将显示“函数名”所在函数的源代码,如:list main
  • list :不带参数,将接着上一次 list 命令的,输出下边的内容。

打印表达式

  • print 表达式:简记为 p ,其中“表达式”可以是任何当前正在被测试程序的有效表达式,比如当前正在调试C语言的程序,那么“表达式”可以是任何C语言的有效表达式,包括数字,变量甚至是函数调用。
  • print a:将显示整数 a 的值
  • print ++a:将把 a 中的值加1,并显示出来
  • print name:将显示字符串 name 的值
  • print gdb_test(22):将以整数22作为参数调用 gdb_test() 函数
  • print gdb_test(a):将以变量 a 作为参数调用 gdb_test() 函数
  • display 表达式:在单步运行时将非常有用,使用display命令设置一个表达式后,它将在每次单步进行指令后,紧接着输出被设置的表达式及值。如: display a
  • watch 表达式:设置一个监视点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。如: watch a
  • whatis :查询变量或函数
  • info function: 查询函数
  • 扩展info locals: 显示当前堆栈页的所有变量

查询运行信息

  • where/bt :当前运行的堆栈列表;
  • bt backtrace 显示当前调用堆栈
  • up/down 改变堆栈显示的深度
  • set args 参数:指定运行时的参数
1
2
set args -l a -C abc
set args -l=a -C=abc
  • show args:查看设置好的参数
  • info program: 来查看程序的是否在运行,进程号,被暂停的原因。

分割窗口

  • layout:用于分割窗口,可以一边查看代码,一边测试:
  • layout src:显示源代码窗口
  • layout asm:显示反汇编窗口
  • layout regs:显示源代码/反汇编和CPU寄存器窗口
  • layout split:显示源代码和反汇编窗口
  • Ctrl + L:刷新窗口

参考链接

  1. gdb 调试利器,by Linux Tools Quick Tutorial.
  2. GDB调试带参数的程序(转载+整理+实践),by blacet.

随机抽样一致

发表于 2019-06-23

随机抽样一致算法(RANdom SAmple Consensus,RANSAC)。它采用迭代的方式从一组包含离群(outlier)的被观测数据中估算出数学模型的参数。 RANSAC是一个非确定性算法,在某种意义上说,它会产生一个在一定概率下合理的结果,而更多次的迭代会使这一概率增加。此RANSAC算法在1981年由Fischler和Bolles首次提出。

RANSAC的基本假设是

  • “内群”(inlier)数据可以通过几组模型的参数来叙述其分布,而“离群”(outlier)数据则是不适合模型化的数据。
  • 数据会受噪声影响,噪声指的是离群,例如从极端的噪声或错误解释有关数据的测量或不正确的假设。
  • RANSAC假定,给定一组(通常很小)的内群,存在一个程序,这个程序可以估算最佳解释或最适用于这一数据模型的参数。

范例

这里用一个简单的例子来说明,在一组数据点中找到一条最适合的线。假设,此有一组集合包含了内群以及离群,其中内群为可以被拟合到线段上的点,而离群则是无法被拟合的点。如果我们用简单的最小二乘法来找此线,我们将无法得到一条适合于内群的线,因为最小二乘法会受离群影响而影响其结果。而RANSAC,可以只由内群来计算出模型,而且概率还够高。然而,RANSAC无法保证结果一定最好,所以必须小心选择参数,使其能有足够的概率。

包含许多离群的一组数据
包含许多离群的一组数据,要找一条最适合的线。
RANSAC找到的线
RANSAC找到的线,离群值对结果没影响(蓝色点为内群,红色点为离群)

设计

RANSAC算法是一个学习的技巧,通过使用观测数据的随机样本来估计模型参数。RANSAC使用投票机制来寻找优化的拟合结果。每个数据元被用来投票一或多个模型。投票机制基于两点假设:

  • 噪音大的特征并不能一直单独为某个模型投票

  • 有足够多的特征来拟合一个好的模型

一般RANSAC算法由两步骤迭代计算:

  • 一个样本子集,包含数据选取(随机选取)。通过使用这些数据得到一个拟合模型和相关的模型参数。样本子集的数量是最小充分的得到模型参数。

  • 算法检查数据集中的哪些元素是一直在第一步估计到的模型当中的。如果在阈值(相对噪声的最大偏离度)外的话,该模型元素不能拟合估计到的模型便会被当做outlier。

inliers的设置称作“一致性设置”RANSAC算法会一直迭代直到获得足够的inliers。

RANSAC的输入是一些观测数据和一些“可信度”参数,实现步骤:

  1. 随机选择一些原始数据,叫作假设inliers子集
  2. 建立模型拟合
  3. 用其他数据来验证,根据模型特定的loss-function来计算是否符合该模型
  4. 如果足够的点都算是“一致性”设置里则该模型算是好模型
  5. 比较所有的“一致性”设置(就是建立的所有模型)看看哪个inliers多就是我们要的。

参数决定

假设每个点是真正内群的几率是 $w$ :
$$ w = 真正內群的數目 / 數據總共的數量$$

通常我们不知道 $w$ 是多少, $w^n$ 是所选择的n个点都是内群的几率, $1-w^n$ 是所选择的n个点至少有一个不是内群的几率, $(1 − w^n)^k$ 是表示重复k次都没有全部的n个点都是内群的几率, 这边定算法跑k次以后成功的几率是p,那么,

$$ 1 − p =(1 − w^n)^k $$
$$ p = 1 −(1 − w^n)^k $$

所以如果希望成功几率高,p = 0.99, 当n不变时,k越大,p越大, 当w不变时,n越大,所需的k就越大, 通常w未知,所以n选小一点比较好。

应用

RANSAC常被用在计算机视觉,例如,对应点问题和 估算立体摄影机双眼相对点的基本矩阵。

参考链接

  1. 随机抽样一致RANSAC: Random Sample Consensus,by 江知季.
  2. 随机抽样一致,by wikipedia.

尺度空间理论简介

发表于 2019-06-22

尺度空间理论最早可以追溯到1962年的T.Iijima最先提出,学术界开始关注尺度空间技术主要在1986年IEEE PAMI上同时刊出的4篇关于尺度空间理论的文章奠定了发展基础。

现实世界中物体只有具备一定的尺度才能够倍人眼所察觉,计算机视觉学术研究就是在不断的尝试与突破来模拟人眼的观察方法。因此,尺度空间就是试图在图像领域中模拟人眼观察物体的概念与方法。

尺度空间理论

概述

图像的尺度空间是指图像的模糊程度,而非图像的大小。近距离看一个物体和远距离看一个物体,模糊程度是不一样的;从近到远,图像越来越模糊的过程,也是图像的尺度越来越大的过程。

尺度空间的作用

  • 用计算机视觉系统分析未知场景时,计算机并不预先知道图像中物体的尺度。需要同时考虑图像在多尺度下的描述,获知感兴趣物体的最佳尺度;

  • 不同的尺度下都有同样的关键点,那么在不同的尺度的输入图像下就都可以检测出来关键点匹配,也就是尺度不变性;

尺度空间的应用

  • David G. Lowe教授基于描述数字图像局部特征的尺度空间概念,于1999年提出尺度不变特征变换算法(Scale-invariant feature transform,SIFT);
  • 在信息获取与处理、建筑、城市学等不同学科中,都有类似的概念,或有借用尺度空间的概念与方法的可能;

尺度不变特征转换

尺度不变特征转换(Scale-invariant feature transform 或 SIFT)是一种机器视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变数,此算法由 David Lowe 在1999年所发表,2004年完善总结。 [1] 后续的论文中也有许多基于 SIFT 改进的论文,例如 SURF 将 SIFT 的许多过程近似,达到加速的效果;PCA-SIFT利用主成分分析降低描述子的维度,减少内存的使用并加快配对速度。

其应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对。

此算法有其专利,专利拥有者为 英属哥伦比亚大学。

参考链接

  1. SIFT算法系列之尺度空间,by small_munich.
  2. 尺度不变特征转换,by wikipedia.
  3. 尺度空间理论,by ☆Ronny丶.
  4. 特征点检测一,by 张帅宾.
  5. ORB特征提取、匹配及实现,by zhaoxuhui.
  6. 图像特征描述子之BRIEF,by Senit_Co.
上一页1…383940…53下一页

Jack Huang

521 日志
67 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse