17.4
随机数
注意计算机程序中给出的都是伪随机数。c++中不再像C一样使用rand()而是引擎+分布。
default_random_engine e;
for(size_t i=00;i<10;++i)
cout<<e()<<endl;
标准库中其实有多个引擎,最常用的就是default_random_engine.其常用操作有:
e.seed();//指定种子
e.min();
e.max();//该引擎可生产的最大最小值
e.discard(u);//将引擎推进n步
default_random_engine e;
uniform_int_distribution<unsigned> u(0,9);//该分布产生0-9的unsigned
for(size_t i=0;i<10;++i)
cout<<u(e);//u接受的是一个engine而不是engine产生的数
分布对象使用它的引擎参数生成随机数,并将其映射到指定的分布。
当我们说随机数发生器时,是指分布对象和引擎的组合。
vector<unsigned> bad_randVec()
{
default_random_engine e(time(0));
default_random_engine e(time(0));
uniform_int_distribution<unsigned> u(0, 9);vector<unsigned> ret;for (size_t i = 0; i < 100; ++i){ret.push_back(u(e));}return ret;}//每次生成相同的结果vector<unsigned> good_randVec(){static default_random_engine e;static uniform_int_distribution<unsigned> u;vector<unsigned> ret;for (size_t i = 0; i < 100; ++i){ret.push_back(u(e));}return ret;}
因为每次调用bad是都初始化生成engine,seed相同。导致每次的engine和distribution都相同,而下面的使用了static每次的engine和u已经初始化过,seed每调用一次默认改变。
一个给定的随机数发生器一直会生成相同的随机数序列。一个函数如果定义了局部的随机数发生器,应该将其(包括引擎和分布对象)定义为static的,否则每次调用函数都会生成相同的序列。
随机数发生器通过变换种子来获取不同的随机数,一个很好的方式是使用time()作为种子。
default_random_engine e(time(0));
但注意,如果程序作为某个自动过程的一部分反复运行,将time的作为返回值作为种子的方式就无效了;它可能多次使用的都是相同的种子。
其它随机数分布
生成随机实数,在以往的C中使用rand()/RAND_MAX来获取实数的方法是常用但错误的,原因是该表达式返回值得值域不是全体double。在新标准下使用uniform_real_distribution<>获得随机实数,如果<>内未加数字则默认double.
生成非均匀随机数分布
1.正态分布
normal_distribution<double> n(4, 1.5);//均值4,标准差1.5
2.伯努利分布(两点分布)
该分布返回一个bool,默认概率0.5
string resp;
default_random_engine e;
bernoulli_distribution b(.55);
do{bool first=b(e);cout<<(first?"we go first":"you get to go first")<<endl;}while(cin>>resp&&resp[0]=='y');}
由于引擎返回相同的随机数序列,所以我们必须在循环外声明引擎对象。否则,每步循环都会创造一个新引擎,从而每步循环都会生成相同的值。类似的,分布对象也要保持状态,因此也应该在循环外定义。
在此程序中使用bernoulli_distribution的原因是可以改变true概率,将概率改为0.55.
17.5
IO库再探
标准库定义了一组操纵符来修改流的格式状态。一个操作符是一个函数或是一个对象,影响流的状态,并能作用于输入或输出运算符的运算对象。
当操纵符改变流的格式状态时,通常改变后的状态对所有后续的IO都生效。
cout << "default bool values: " << true << " " << false
<< "\nalpha bool values: " << boolalpha
<< true << " " << false << endl;
这个时候原来用01表示的布尔值自动转化为true、false。
cout<<noboolalpha;//取消了原有的01->tf转化
cout << "default: " << 20 << " " << 1024 << endl;
cout << "octal: " <<oct<< 20 << " " << 1024 << endl;
cout << "hex: " <<hex<< 20 << " " << 1024 << endl;
cout << "decimal: " <<dec<< 20 << " " << 1024 << endl;//分别以默认、八进制、十六进制、十进制打印20和1024
操纵符hex、oct和dec只影响整形运算对象,浮点数的表示形式不受影响。
使用showbase来展示数制
cout<<showbase;
cout << "default: " << 20 << " " << 1024 << endl;
cout << "octal: " <<oct<< 20 << " " << 1024 << endl;
cout << "hex: " <<hex<< 20 << " " << 1024 << endl;
cout << "decimal: " <<dec<< 20 << " " << 1024 << endl;
cout<<noshowbase;//恢复默认状态
使用IO的precision成员或setprecision操作符来改变精度。precision是重载的。一个版本接受一个int值,将精度设定为此值,并返回旧精度值。另一个版本不接受参数,返回当前精度值。setprecision操纵符接受一个参数,用来设置精度。操纵符setprecision和其它接受参数的操纵符都定义在头文件iomanip中。
cout<<"Precision: "<<cout.preciosn()<<",Value: "<<sqrt(2.0)<<endl;
cout.precision(12);
cout<<"Precision: "<<cout.preciosn()<<",Value: "<<sqrt(2.0)<<endl;
cout<<setprecision(3);
cout<<"Precision: "<<cout.preciosn()<<",Value: "<<sqrt(2.0)<<endl;
除非需要特别控制浮点数的表示形式,否则由标准库选择计数法是最好的方式。
cout<<scientific;//科学计数法
cout<<fixed;//修正
cout<<hexfloat;//十六进制浮点数
cout<<defaultfloat;//默认状态
cout<<showpoint;//强制打印小数点
cout<<noshowpoint;恢复
基数大小写
cout<<uppercase;
cout<<nouppercase;
补出空白
setw
left
right
internal
setfill
操作符noskipws保留原输入的空格
char ch;
cin>>noskipws;//没有的话所有空格被忽略
while(cin>>ch)
{
cout<<ch;
}
cin>>skipws;
由于未格式化的IO使用极不安全所以暂且跳过。
流随机访问
随机IO的特性基于系统。由于istream和ostream不支持随机访问,所以一下讨论只针对fstream和sstream.
seek和tell函数,分别表示查找和告知,根据put和get又分为
tellg()、tellp()返回输入或输出流当前位置
seekg(pos).seekp(pos)在一个输入流或输出流中将标记重定位到给定位置的地址。