《C++ Primer》第17章 标准库特殊设施
17.4节 随机数 习题答案
练习17.28:编写函数,每次调用生成并返回一个均匀分布的随机unsigned int。
【出题思路】
本题练习生成随机数。
【解答】
在函数中定义随机数引擎e作为随机数源,定义均匀分布u生成指定范围内的均匀分布的随机数。然后调用u(e)即可获得一个随机数。注意,将u和e声明为静态变量以保持状态,使得每次调用函数得到序列中的下一个数。
#include <iostream>
#include <random>
using namespace std;
unsigned int rand_int()
{
//生成0到9999之间(包含)均匀分存的随机数
static uniform_int_distribution<unsigned> u(0, 9999);
static default_random_engine e;//生成无符号随机整数
return u(e);
}
int main()
{
for(int i = 0; i < 10; ++i)
cout << rand_int() << " ";
cout << endl;
return 0;
}
运行结果:
练习17.29:修改上一题中编写的函数,允许用户提供一个种子作为可选参数。
【出题思路】
本题练习设置种子。
【解答】
为函数设置一个可选参数,当实参是非负整数时,用seed操作对引擎e重新设置种子。主程序三次生成并打印10个随机数,后两次分别重置种子为0和19743。编译运行程序,观察结果。
#include <iostream>
#include <random>
using namespace std;
unsigned int rand_int(long seed = -1)
{
//生成0到9999之间(包含)均匀分布的随机数
static uniform_int_distribution<unsigned> u(0, 9999);
static default_random_engine e;//生成无符号随机整数
if(seed >= 0)
e.seed(seed);
return u(e);
}
int main()
{
for(int i = 0; i < 10; i++)
cout << rand_int() << " ";
cout << endl;
cout << rand_int(0) << " ";
for(int i = 0; i < 9; i++)
cout << rand_int() << " ";
cout << endl;
cout << rand_int(19743) << " ";
for(int i = 0; i < 9; i++)
cout << rand_int() << " ";
cout << endl;
return 0;
}
运行结果:
练习17.30:再次修改你的程序,此次再增加两个参数,表示函数允许返回的最小值和最大值。【出题思路】
本题练习设置随机数的最大值和最小值。
【解答】
再增加两个参数表示允许的最大值和最小值,当给定参数合法时(缺省参数不合法),构造一个uniform_int_distribution对象,生成的随机数最大最小值符合给定参数要求。编译运行程序,观察后两行输出结果。
#include <iostream>
#include <random>
using namespace std;
unsigned int rand_int(long seed = -1, long min = 1, long max = 0)
{
//生成0到9999之间(包含)均匀分布的随机数
static uniform_int_distribution<unsigned> u(0, 9999);
static default_random_engine e;//生成无符号随机整数
if(seed >= 0)
e.seed(seed);
if(min <= max)
u = uniform_int_distribution<unsigned>(min, max);
return u(e);
}
int main()
{
for(int i = 0; i < 10; i++)
cout << rand_int() << " ";
cout << endl;
cout << rand_int(0) << " ";
for(int i = 0; i < 9; i++)
cout << rand_int() << " ";
cout << endl;
cout << rand_int(19743) << " ";
for(int i = 0; i < 9; i++)
cout << rand_int() << " ";
cout << endl;
cout << rand_int(19743, 0, 9) << " ";
for(int i = 0; i < 9; i++)
cout << rand_int() << " ";
cout << endl;
return 0;
}
运行结果:
练习17.31:对于本节中的游戏程序,如果我们在do循环内定义b和e,会发生什么?
【出题思路】
理解随机数的生成。
【解答】在循环内定义b和e,每个循环步都会用默认的种子(0)重新初始化随机数引擎e。因此,调用b(e)永远得到的是特定随机数序列的第一个数,游戏的先行者永远是固定的。而在循环外定义,则可保持引擎的状态,每次得到随机数序列中的下一个值,游戏的先行者会改变。
练习17.32:如果我们在循环内定义resp,会发生什么?
【出题思路】
理解变量生命周期。
【解答】
如果在循环内定义resp,则其生命周期仅在循环体内,而while循环条件判定不属于循环体。因此,在进行循环条件判定时,resp已经被销毁,程序会产生编译错误。
练习17.33:修改11.3.6节(第392页)中的单词转换程序,允许对一个给定单词有多种转换方式,每次随机选择一种进行实际转换。
【出题思路】
本题练习随机数的实际使用。
【解答】
首先,将trans_map的类型(包括buildMap的返回类型)改为map<string,vector<string>>,以便保存一个单词的多种转换方式。然后,在函数transform中声明随机数引擎e为静态局部变量,以便保存状态,每次得到随机数序列中的下一个数。在检查到存在转换规则后,声明一个均匀分布u,其最小值为0,最大值为转换方法数目减1,因此得到的随机数对应转换方式的下标。最后调用u(e)获得随机数,作为下标获取转换的目标字符串。
#include <map>
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>
#include <sstream>
#include <vector>
#include <random>
#include <time.h>
using namespace std;
map<string, vector<string>> buildMap(ifstream &map_file)
{
//允许多种转换方法
map<string, vector<string>> trans_map;//保存转换映射表
string key;//要转换的单词
string value;//替换的内容
//读取第一个单词存入key,一行中剩余内存存入value
while(map_file >> key && getline(map_file, value))
{
if(value.size() > 1)//检查是否有转换规则
trans_map[key].push_back(value.substr(1));//跳过前导空格
else
throw runtime_error("no rule for " + key);
}
return trans_map;
}
const string &transform(const string &s, const map<string, vector<string>> &m)
{
static default_random_engine e(time(0));//随机数引擎,静态变量保持状态
//完成真正的转换,此程序的核心代码
auto map_it = m.find(s);
//单词在转换映射表中
if(map_it != m.cend()){
//随机数分布
uniform_int_distribution<unsigned> u(0, map_it->second.size() - 1);
return map_it->second[u(e)];//随机选择一种转换方式
}
else {
return s;//否则返回原词
}
}
//第一个参数是转换规则文件,第二个也是要转换的文件
void word_transform(ifstream &map_file, ifstream &input)
{
auto trans_map = buildMap(map_file);//保存转换规则
//调试用:创建转换规则映射表后打印它
cout << "Here is our transformation map: \n\n";
for(auto entry: trans_map)
{
cout << "key: " << entry.first << "\tvalue:";
for(auto s: entry.second)
cout << s << ", ";
cout << endl;
}
cout << endl << endl;
//对给定文本进行转换
string text;//保存输入的每一行
while(getline(input, text))//读取输入一行
{
istringstream stream(text);//读取每个单词
string word;
bool firstword = true;//控制是否打印空格
while(stream >> word){
if(firstword)
firstword = false;
else
cout << " ";//在单词间格打印空格
//transform返回第一个参数或转换结果
cout << transform(word, trans_map);//打印输出
}
cout << endl;//此行处理完毕
}
}
int main(int argc, char **argv)
{
cout << "argc=========" << argc << endl;
//打开并检查两个文件
if(argc != 3)
throw runtime_error("wrong number of arguments");
ifstream map_file(argv[1]);//打开转换规则文件
if(!map_file)//检查是否打开成功
throw runtime_error("no transformation file");
ifstream input(argv[2]);//打开要转换的文件
if(!input)//检查是否打开成功
throw runtime_error("no input file");
word_transform(map_file, input);
return 0;
}
data17_33_input.txt文件内容:
where r u
y don’t u send me a pic
k the l8r
data17_33_map.txt文件内容:
brb be right back
k okay?
y why
r are
u you
pic picture
the thanks!
l8r later
设置命令行参数:
运行结果: