一、随机函数
C++中使用 random 头文件的 random_device 类型变量产生随机数。该随机 数生成器使用梅森旋转算法(Mersenne twister),用于快速产生高质量伪随机无 符号整数(unsigned int)。
1、伪随机数和真随机数 伪随机数:看似随机的数字,具有可重复性和可预见性 真随机数:以非确定方式出现,具有不可重复性和不可预见性
2、计算机不适合生成真正的随机数,只能由公式生成伪随机数。对于大多数应 用而言,伪随机数已足够用。真正的随机数可以用放射性元素的衰变周期、噪声 频率,实现难度大。
3、评价伪随机数发生器性能的标准:伪随机序列的周期。
4、案例
(1)产生 20 个随机无符号整数值
说明:
如果想要生成有符号的整数, 那么可以把 cout << rd() << endl;
修改为: cout << (int)rd() << endl;
或者 int a = rd(); cout << a << endl;
#include <iostream>
#include <random>
using namespace std;
int main()
{
random_device rd;
for (int i = 0; i < 20; i++)
{
cout << rd() << endl;
}
return 0;
}
(2)产生 0~99 之间的随机整数
把第(1)题中的 rd()修改为 rd() % 100
说明:一个无符号整数除以 100 的余数,范围在 0~99 之间。
(3)产生 a~b 之间的随机整数(范围包含 a 和 b) 把第(1)题中的 rd()修改为 rd() % (b - a + 1) + a
说明:b - a + 1 表示 a~b 范围内的不同整数数量,rd() % (b - a + 1)生成 0~b-a 之间的随机整数。
(4)产生 1.0~9.9 之间的随机浮点数(一位小数部分) 把第(1)题中的 rd()修改为(rd() % 90 + 10) / 10.0
说明:先考虑生成 10~99 之间的整数,再除以 10.0。
(5)产生-20~10 之间的随机整数 把第(1)题中的 rd()修改为(int)(rd() % 31) -20
注意:rd()类型是无符号的,要在取余数后转化为有符号的 int。
二、函数模板
模板是 C++泛型编程的基础。泛型程序独立于特性的数据类型来编写代码, 在程序编译时确定具体的数据类型。
假设需要编写一个函数比较两个值,如果两个值相等则返回 0,如果参数 1 小于参数 2 则返回-1,如果 参数 1 大于参数 2 则返回 1。如果两个值都是整数,
则函数可以写成:
int compare(int a, int b)
{
if (a == b)
{
return 0;
}
else if (a < b)
{
return -1;
}
else
{
return 1;
}
}
当 if、for、while 的大括号里只有一句话,则可以不写大括号(但不建议)。 为了节省空间,上面的代码也可以写成:
int compare(int a, int b)
{
if (a == b) return 0;
else if (a < b) return -1;
else return 1;
}
但如果是比较两个浮点数的大小,则函数又要写成:
int compare(double a, double b)
{
if (a == b) return 0;
else if (a < b) return -1;
else return 1;
}
还有可能是更多类型的数据比较,虽然 C++允许参数类型不同的同名函数 (下节课的重载),但这不是好的解决方案。
1、定义函数模板
可以定义一个通用的函数模板(function template),而不是为每种数据类型 都定义一个新函数。 一个函数模板就是一个公式,用来生成针对特定数据类型的 函数版本。 模板定义以关键字 template 开始,后面跟着一个模板参数列表,表示函数定 义中用到的特定数据类型。
当使用模板时,可以隐式或者显式指定模板实参,将 其绑定到模板参数上。
compare 函数可以写成模板:
#include <iostream>
using namespace std;
template<typename T>
int compare(T a, T b)
{
if (a == b) return 0;
else if (a < b) return -1;
else return 1;
}
说明:
compare 函数的模板参数列表声明了一个名为 T 的类型参数,用 T 表示一 个数据类型。T 具体表示哪一种数据类型,是由编译时根据 compare 函数的调用 来确定。
例如在 main 函数中
int main()
{
cout << compare(1, 0) << endl;
return 0;
}
实参类型是 int,编译器会推断出模板实参 a 和 b 为 int,并把 int 绑定到模 板参数 T 中。
编译器用推断出的模板参数来实例化一个特定版本的函数。
如果 main 函数的代码是:
int main()
{
cout << compare(1, 0) << endl;
cout << compare(1.5, 2.1) << endl;
return 0;
}
那么编译器会实例化出两个不同版本的 compare 函数。
对于第一个调用,编译器生成的 compare 中,T 被替换为 int。
对于第二个调用,编译器生成的 compare 中,T 被替换为 double。
2、多个模板参数的情况
在上述代码中,如果在主函数中 cout << compare(1.5, 2) << endl; ,则会引 发编译报错,原因是根据 1.5 和 2 推断出 T 的类型不一致。 如果要支持不同类型的两个实参比较大小,则需要在模板参数列表中定义两 个模板参数,比如命名为 T1 和 T2。
#include<iostream>
using namespace std;
template<typename T1, typename T2>
int compare(T1 a, T2 b)
{
if (a == b) return 0;
else if (a < b) return -1;
else return 1;
}
int main()
{
cout << compare(1, 0) << endl;
cout << compare(1.5, 2.1) << endl;
return 0;
}
运行结果如下——
3、案例
(1)求两个相同类型数的总和
#include<iostream>
using namespace std;
template <typename T>
T getSum(T a, T b)
{
return a + b;
}
int main()
{
cout << getSum(4, 5);
return 0;
}
运行结果如下——
(2)交换两个相同类型的数
#include<iostream>
using namespace std;
template <typename T>
void f(T& a, T& b)
{
T c;
c = a;
a = b;
b = c;
}
int main()
{
int a = 5, b = 10;
f(a, b);
cout << a << "\n" << b << "\n";
return 0;
}
运行结果如下——
【练习】以下程序的运行结果是 ,其中字母 A 的 ASCII 码为 65
#include<iostream>
using namespace std;
template<class T1, class T2>
T1 f(T1 x, T2 y)
{
return x + y;
}
int main()
{
cout << f('A', 1) << f(1, 'A') << endl;
return 0;
}
T1决定了输出的类型——
运行结果如下——
三、自动类型
1、auto 自动类型变量
编程时经常需要把表达式的值赋给变量,这要求声明变量时清楚地知道表达式的类型。有时要做到这一点并不容易,甚至根本做不到。为了解决这个问题, 从 C++11 标准开始,引入了 auto 类型,能让编译器去自动分析表达式所属的类 型。和原来那些只定义一种特定类型的说明符(例如 double)不同,auto 让编 译器通过初始值来推算变量的类型。显然,auto 定义的变量必须有初始值。
auto c = a + b;
此时编译器将根据 a 和 b 相加的结果来推断 c 的类型。
如果 a 和 b 都是 int,则 c 是 int 类型;
如果 a 是 double,b 是 int,则 c 是 double 类型。
使用 auto 也可以在一条语句中定义多个变量。因为一条变量声明语句只能 有一个数据类型,所以该语句中所有变量的数据类型都必须一样。
auto a = 0.5, b = 1.0;
2、函数中的自动返回类型
当函数的返回值类型为 auto 类型时,会根据 return 的值自动推断返回值的类型。要注意的是,一个返回值类型是 auto 的函数里如果存在多处 return 语句, 每处 return 语句返回值都要能推断成同一种类型。
例:求两个任意类型数的总和(C++14 开始的语法,OJ 不支持)
#include<iostream>
using namespace std;
template<typename T1, typename T2>
auto getMax(T1 x, T2 y)
{
return x + y;
}
int main()
{
cout << getMax(1, 2) << endl;
cout << getMax('A', 1) << endl;
cout << getMax(1.5, 2) << endl;
return 0;
}
运行结果如下——
四、匿名函数
匿名函数又称为𝜆函数,使用 lambda 表达式定义函数,是函数式编程的基础。 匿名函数定义在函数内部,仅在所在函数中可见。
1、lambda 函数语法
[capture](parameter)->return-type{statement}
[capture]:捕捉列表,捕捉上下文中的变量以供 lambda 函数使用。
(parameter):参数列表,与普通函数的参数列表一致。如果无参数,可以连同括 号一起省略。
->return-type:返回类型,如果无返回值则可以连同->一起省略。返回类型明确 时,也可以省略,由编译器负责对返回类型进行推导。
{statement}:函数体。与普通函数一样,不过除了可以使用参数外,还可以使用 所有捕捉的变量。
2、变量捕捉列表
匿名函数语法中[capture]变量捕获模块,可以捕捉的匿名函数外部作用域中 的变量,以供匿名函数的函数体使用。
[ ] 函数不需要访问外部作用域中任何变量
[&] 函数引用调用外部作用域中所有变量
引用调用——可以访问并且可以修改外围变量的值 (&)引用符号
[=] 函数传值调用外部作用域中所有变量
传值调用——可以访问但是不能修改外围变量的值
[=, &y] 函数传值调用外部作用域中所有变量,但是引用调用 y 变量
[&, x] 函数引用调用外部作用域中所有变量,但是传值调用 x 变量
[x, &y] 函数传值调用 x 变量,引用调用 y 变量
3、案例
(1)使用匿名函数实现求和函数
#include<iostream>
using namespace std;
int main()
{
auto sum = [](int a, int b)->int {return a + b; };
int x = 5, y = 10;
cout << sum(x, y) << endl;
cout << sum(x, x) << endl;
return 0;
}
运行结果如下——
理解这句代码!
[capture](parameter)->return-type{statement}
(2)使用匿名函数计算 n 的阶乘
代码如下——
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
auto fac = [=]()->int {
int f = 1;
for (int i = 1; i <= n; i++)
{
f = f * i;
}
return f;
};
cout << fac() << endl;
return 0;
}
运行结果如下——
(3)使用匿名函数生成 x~y 之间的随机整数
代码如下——
#include<iostream>
#include<random>
using namespace std;
int main()
{
int x, y;
cin >> x >> y;
random_device rd;
auto getRand = [&]()->int
{
return rd() % (y - x + 1) + x;
};
int a = getRand();
int b = getRand();
int c = getRand();
cout << "a=" << a << "\tb=" << b << "\tc=" << c << endl;
return 0;
}
运行结果如下——
重点理解!!!!!!!!!!!!!!!
上文中均有提及!