《C++ Primer》第17章 17.4节习题答案

《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

设置命令行参数:

 运行结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值