【c++】rand()随机函数的应用(二)——舒尔特方格数字的生成

news2024/11/15 5:07:22

目录

一、舒尔特方格简介

二、如何生成舒尔特方格

(一)线性同余法

1、利用线性同余法生成随机数序列的规律

(1) 当a和c选取合适的数时,可以生成周期为m的随机数序列

(2) 种子seed取值也是有周期的

2、利用线性同余法生成5阶舒尔特方格的程序

(1)先验证当x0不变时,生成的随机数序列是有周期的

 (2)验证seed值x0也具有周期性

 (3)生成5维舒尔特方格数字

 (二)利用rand()函数

1、rand()函数搭配srand(time(NULL))函数

2、动态取模法

(1)初始化

(2)随机将a中数据存入b中

3、图解动态取模法

(1)a、b初始状态

(2)生成第一个随机数

(3)生成第二个随机数

(4)执行最后一步

4、动态取模法代码


rand()随机函数可以用来生成舒尔特方格数字。下边先介绍一下舒尔特方格。

一、舒尔特方格简介

舒尔特方格是用来测试和训练专注力的,百度百科的介绍如下:

舒尔特方格,画一张有25个小方格(规格1cm*1cm)的表格,将1~25的数字顺序打乱,填在表格里面,然后以最快速度从1数到25,要边读边指出,一人指读一人帮忙计时。

采用盯点法就可以随时训练的,在教室和家里,每天盯着某个点和物体看上几分钟就可以的,还可以采用舒尔特训练法。

运用这种方法的时候,可以自制几套卡片,绘制表格,任意填上数字。从 1开始,边念边指出相应的数字,直到25为止。同时诵读出声,施测者一旁记录所用时间。数完 25 个数字所用时间越短,注意力水平越高。以 12 —— 14 岁年龄组为例,能达到 16 "以上为优秀, 26 "属于中等水平, 36 "则需要进行强化提高。

注:百度百科上介绍的舒尔特方格是5维(5行5列)规格的,而实际维数是可变的,维数越高难度越大,1维2维太简单,一般不用。3维、4维属于初级难度,5维6维属于中级难度,7级及以上就属于难度比较高级的了。

二、如何生成舒尔特方格

5维舒尔特方格内数字的排列不是固定的,位置是随机的,运用排列组合的知识可以知道,一共有25!个不同的排列方式。用人工排列生成的方法显然是不可行的,那么用什么算法可以实现这个目的呢?下边介绍两种方法。

(一)线性同余法

在上一篇文章中介绍了线性同余生成随机数的方法。线性同余的公式如下所示:

x_{n+1}=(a*x_{n}+c) mod(m)​     (1)

其中a是乘法器,c是增值,m是模,这三个都是常数,x_{n}​是生成的随机数序列,当n=0时,x_{0}的值称为种子seed。

1、利用线性同余法生成随机数序列的规律

(1) 当a和c选取合适的数时,可以生成周期为m的随机数序列

周期为m的意思就是,在一个周期内,利用公式(1)迭代计算m次,正好可以把数字0~m-1每个数字都可以生成且只生成一次,而且最后一次迭代生成的数正好就是种子数x_{0}。迭代到第m+1次时,生成的数字和第一次迭代生成的数字x_{1}一样,再接着迭代就会重复上一个周期。生成一个周期内的数字排列顺序表面看是无规律,实际上是由公式(1)计算出来的,所以叫伪随机数。

还需要注意,并不是任意取一组a和c,就可以生成周期为m的伪随机数序列。a和c的取值是有限定条件的,条件如下图所示。

(2) 种子seed取值也是有周期的

利用公式(1),当a、c、m都确定后,当seed值选取不同时,生成的随机数序列也不同。但是需要注意,并不是任意取一个seed值,生成的随机数序列都不一样,seed取值也是有周期的,当随机数的周期是m时,seed的周期也是m。也就是seed=0时,和seed=m时,生成的随机数序列是一样的。也就是利用线性同余方法,生成不同的随机数序列最多有m种。

2、利用线性同余法生成5阶舒尔特方格的程序

 5阶舒尔特方格的数字范围为1~25,每个格子数字各不相同,排列顺序随机。所以可以考虑使用线性同余法。经过测试,发现当c=11,a=11时,利用线性同余计算出来的伪随机数序列的周期是25。

(1)先验证当x0不变时,生成的随机数序列是有周期的

编写一个给定x0=0,生成两个周期随机数的测试程序如下:

#include <iostream>
using namespace std;

int main()
{
	int i,x0=0,a=11,c=11,m=25;	//确定a=11,c=11,m=25	
	for(i=0;i<m*2;i++)		//此循环用于生成2个周期内的随机数 
	{
		x0=(a*x0+c)%m;
		cout<<x0<<" ";
	}
		cout<<endl; 
	return 0;
} 

下图是测试结果,图中黄色框内是第一个周期的25个随机数,绿色框内是第二个周期内的25个随机数。可以看出两个周期内的随机数排列顺序完全一致,因此说明当x0不变时,生成的随机数呈周期性变化。

 (2)验证seed值x0也具有周期性

编写一个x0从0增加到49(两个周期)时,每个seed值生成一个周期的随机函数序列,代码如下:

#include <iostream>
using namespace std;

int main()
{
	int i,j,x0,a=11,c=11,m=25;	//确定a=11,c=11,m=25 
	for(j=0;j<m*2;j++) 			//此循环用于使种子值从0增加到49 
	{
		x0=j;
		cout<<"seed="<<x0<<":";		
		for(i=0;i<m;i++)		//此循环用于生成一个周期内 
		{
			x0=(a*x0+c)%m;
			cout<<x0<<" ";
		}
		cout<<endl; 
	}	
	return 0;
} 

下图是测试结果如下图,从图中可以发现,seed值在0~24范围内(黄线以上),生成的25个随机数序列各不相同,而seed在25~49范围内(黄线以下)时的随机序列,与0~24范围内依次对应,完全一样,说明,seed值对随机序列的影响也是周期性的,周期也是25。 

 综上所述,利用基本的线性同余算法,对于5维舒尔特方格,最多只能生成25个不同的排列。

 (3)生成5维舒尔特方格数字

本例程序,将生成的25个5维舒尔特方格数字输出到一个csv文件内。因为利用线性同余算法生成的随机数范围为0~24,而5维舒尔特方格内的数字为1~25,所以只需将生成的随机数再加1即可。

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
	int i,j,x0,a=11,c=11,m=25;	//确定a=11,c=11,m=25 
	ofstream oFile;				//新建一个ofstream文件oFile
	oFile.open("ShultGrid.csv",ios::out|ios::trunc);//打开oFile文件,文件名称为"ShultGrid.csv"
	
	for(j=0;j<m;j++) 			//此循环用于使种子值从0增加到49 
	{
		x0=j;
		oFile<<"舒尔特方格"<<x0;		
		for(i=0;i<m;i++)		//此循环用于生成一个周期内 
		{
			x0=(a*x0+c)%m;			
			if(i%5==0)			//每5个数字分成一行 
			{
				oFile<<endl;	
			}
			oFile<<x0+1<<",";	//生成的随机数为0~24,加1后变为1~25。输入一个",",代表光标移动到同一行的下一个单元格 
			
		}
		oFile<<endl<<endl; 		//最后一行回车,然后再空一行 
	}	
	return 0;
} 

以下是程序运行后,生成的csv文件部分舒尔特方格截图。

 (二)利用rand()函数

以上介绍了利用线性同余法生成舒尔特方格的方法,方法比较简单,但是最大的缺点是只能生成25个不同的排列。要想生成更多不同的排列,还需要用其他的方法。而利用rand()函数就是一种可行的方法。

1、rand()函数搭配srand(time(NULL))函数

在上一讲种介绍了,可以利用rand()搭配srand(time(NULL))函数,生成1~25的随机数,代码如下所示。

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <windows.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
 
int main()
{
	int m,n;
	cin>>n>>m; 
//	srand(getpid());
	srand(time(NULL));
	while(1)
	{
		cout<<rand()%(m-n+1)+n<<endl;
		Sleep(1000);
	}
	
}

运行代码,输入n=1,m=25,生成的结果如下图所示。从图中可以看出,在前25个数中,有重复的数字,还缺了一些数字。这是因为rand()函数是以32768为模,在一个周期内可以生成0~32767范围内互不相同的随机数序列。但是rand()%25的结果就可能在0~25范围内会出现重复或者缺失的情况。

 所以,用这种方法是不可行的。

2、动态取模法

本例提出了一种新的方法实现不同维数舒尔特方格的生成方法,也需要用到rand()、srand()函数,在算法上采用动态取模方法。

动态取模算法的思路是:

(1)初始化

声明两个长度为25的一维数组a[25],b[25],数组a用来按顺序存放1~25数字,b用来存放从a中随机取出来的数字。用j表示数组b的角标,i表示数组a[]的角标,k表示需要更新数据的a[]的角标。

(2)随机将a中数据存入b中

从j=0开始,依次随机将a中的一个数字,存入到b[j]中,然后将a中角标i以后的有效数字依次向前移动一位。此时a[i]就被覆盖了,而a[24-j]那个位置的数据就无效了,下次取数字就不会再考虑这个位置。a中有效数字就是a中还没有被存入到b中的数字。a的角标i是用srand()搭配rand()%(25-j)随机生成的。此步关键代码为

srand(time(NULL));
i=rand()%(25-j);

b[j]=a[i];

a[i]=a[i+1];

模m=25-j,是可变的,因为每次从a中取走一个数字,a中的有效数字就减少了一个,所以生成的随机数范围就要减少1,也就是模m是依次减少1,是动态可变的,所以叫动态取模法。

3、图解动态取模法

(1)a、b初始状态

a、b初始化后的状态如下图所示。

(2)生成第一个随机数

假设第一次i=3,则把a[3]的值4赋给b[0],然后从i=3开始,到i=23结束,执行a[i]=a[i+1]进行数据更新。a、b数组执行生成随机数前后的对比如下图所示。图中a数组中有效的数字所占据的空间用绿色填充,无效的空间用白色填充。数组b中已经填入的随机数字用蓝色填充,没有填入数字的用白色填充。

(3)生成第二个随机数

假设第二次i=22,那么取出其中存储的数据24,放入b[1]中,然后从i=22开始,到i=22结束,执行a[i]=a[i+1]进行数据更新。a、b数组执行生成随机数前后的对比如下图所示。

 

(4)执行最后一步

执行到第25步时,a、b数组内的存储情况如下图所示。

 

4、动态取模法代码

#include <iostream>
#include<stdio.h>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <string>
using namespace std;

int* diff_random(int m,int num)
{
	int a[num];//a数组用于按1-num顺序存放初始数字,b用于存放随机生成的舒尔特数字 
	int *b=new int[num];
	int i,j,k;	
	
	for(i=0;i<num;i++)//初始化初始数字数组 
	{
		a[i]=i+m;
	}
	
	srand(time(NULL));

	for(j=0;j<num;j++)			//利用无重复随机数算法生成舒尔特方格数组,j为生成数组的序号 
	{
		i=rand()%(num-j);		//生成数组a的随机序号,范围为0~num-j,用i表示 
		b[j]=a[i];				//将a[i]中存放的数字(也就是i+1)赋值给b[j]
		 
		for(k=i;k<num-j-1;k++)	//将a[i]中存放的数字删除,将a[i+1]~a[num-j]的数字依次向前移动一个位置 
		{
			a[k]=a[k+1];
		}	
	}
	for(i=0;i<num;i++)			//初始化初始数字数组 
	{
		cout<<a[i]<<endl;
	}	
	return b;
}


int main()
{
	int dim,num;//dim:舒尔特方格维数,num:方格个数 
	cout<<"请输入舒尔特方格维数:"<<endl;
	cin>>dim; 
	num=dim*dim;
	int i;
	
	int *b=diff_random(1,num);
	
	ofstream oFile;//新建一个ofstream文件oFile
	oFile.open("ShultGrid1.csv",ios::out|ios::trunc);//打开oFile文件,文件名称为"ShultGrid.csv"
	oFile<<dim<<"维舒尔特方格"<<endl;	
	for(i=0;i<num;i++)
	{
		oFile<<b[i]<<",";
		if((i+1)%dim==0)
		{
			oFile<<endl;
		}	
	}
	oFile.close();
	
	cout<<dim<<"维舒尔特方格已生成,请打开ShultGrid1.csv文件查阅"<<endl;
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/833260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IIC子系统-实现si7006温湿度传感器采集温湿度功能

1.将IIC核心层和总线驱动层配置进内核 *********************配置核心层*************************1.找到核心层代码目录&#xff1a;内核顶层目录/drivers/i2c2. 内核顶层目录执行make menuconfig3. > Device Drivers > I2C support ->-*-I2C support4.保存退出***…

Java进阶——数据结构与算法之哈希表与树的入门小结(四)

文章大纲 引言一、哈希表1、哈希表概述2、哈希表的基本设计思想3、JDK中的哈希表的设计思想概述 二、树1、树的概述2、树的特点3、树的相关术语4、树的存储结构4.1、双亲表示法4.2、孩子兄弟表示法&#xff1a;4.3、孩子表示法&#xff1a;4.4、双亲孩子表示法 三、二叉树1、二…

ThreadPoolExecutor详解(上)

为什么会有线程池&#xff1f; 如果客户端发一个请求&#xff0c;服务端就创建一个线程接收请求&#xff0c;线程资源是有限的&#xff0c;而且创建一个线程和执行结束之后都要调用操作系统资源销毁线程&#xff0c;这样频繁操作肯定非常占用cpu和内存资源&#xff0c;线程池的…

性能测试 —— “问题分析”

性能测试大致分以下几个步骤&#xff1a; 需求分析 脚本准备 测试执行 结果整理 问题分析 今天要说的是最后一个步骤——“问题分析”&#xff1b; 需求描述 有一个服务&#xff0c;启动时会加载一个1G的词表文件到内存&#xff0c;请求来了之后&#xff0c;会把请求词去…

构建稳健的PostgreSQL数据库:备份、恢复与灾难恢复策略

在当今数字化时代&#xff0c;数据成为企业最宝贵的资产之一。而数据库是存储、管理和保护这些数据的核心。PostgreSQL&#xff0c;作为一个强大的开源关系型数据库管理系统&#xff0c;被广泛用于各种企业和应用场景。然而&#xff0c;即使使用了最强大的数据库系统&#xff0…

LeetCode 25题:K个一组翻转链表

题目&#xff1a; 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯…

嵌入式:C高级 Day2

一、递归实现&#xff0c;输入一个数&#xff0c;输出这个数的每一位 二、递归实现&#xff0c;输入一个数字&#xff0c;输出这个数的二进制 三、写一个脚本&#xff0c;包含以下内容 1.显示/etc/group文件中第五行的内容 2.创建目录/home/ubuntu/copy 3.切换工作路径到此目录…

《命运》阅读笔记

《命运》阅读笔记 2023年5月17号在杭州的小屋读完&#xff0c;我读完后&#xff0c;脑海里经常把余华的《活着》和这本《命运》的故事情节搞混淆&#xff0c;几乎都是讲着生活的苦难。全文以阿太&#xff08;外婆的妈妈&#xff09;的视角&#xff0c;在她九十九岁的人生里&…

在excel中整理sql语句

数据准备 CREATE TABLE t_test (id varchar(32) NOT NULL,title varchar(255) DEFAULT NULL,date datetime DEFAULT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO t_test VALUES (87896cf20b5a4043b841351c2fd9271f,张三1,2023/6/8 14:06); INSERT INTO t_test …

Electron + Vue3 + Vite + TS 构建桌面应用

之前是使用React、Electron、TS和webpack来构建桌面应用的。虽然功能齐全,但是打包等等开发的体验不太理想,总感觉太慢了。作为一个开发者,我们总是希望,执行构建命令后,可以快速打包或者启动本地应用,且通过更少的配置,来完成开发体验。 现在的vite已经得到广泛的应用…

如今音视频开发还有前途吗?

下面我从两个角度来跟大家进行分析&#xff1a; 市场角度薪资角度 这两角度分析下来&#xff0c;估计大家心里就会有答案了&#xff01;&#xff01;&#xff01; 1. 市场角度 目前市场中使用音视频技术的公司太多了&#xff0c;大到全民观看短视频&#xff0c;小到直播带货…

IT 基础架构自动化

什么是 IT 基础架构自动化 IT 基础架构自动化是通过使用技术来控制和管理构成 IT 基础架构的软件、硬件、存储和其他网络组件来减少人为干预的过程&#xff0c;目标是构建高效、可靠的 IT 环境。 为什么要自动化 IT 基础架构 为客户和员工提供无缝的数字体验已成为企业的当务…

【从零开始学习JAVA | 第三十八篇】应用多线程

目录 前言&#xff1a; 多线程的实现方式&#xff1a; Thread常见的成员方法&#xff1a; 总结&#xff1a; 前言&#xff1a; 多线程的引入不仅仅是提高计算机处理能力的技术手段&#xff0c;更是适应当前时代对效率和性能要求的必然选择。在本文中&#xff0c;我们将深入…

百度网盘加速下载

下载网页插件可搜索各种奇葩工具 Tampermonkey 在线解析连接&#xff1a;https://api.94speed.com/web/ 工具下载&#xff1a;https://motrix.app/zh-CN/download 解析完成后发送到&#xff1a;motrix

LeetCode 626. 换座位

题目链接&#xff1a;LeetCode 626. 换座位 题目描述 表名&#xff1a;Seat 编写SQL查询来交换每两个连续的学生的座位号。如果学生的数量是奇数&#xff0c;则最后一个学生的id不交换。 按 id 升序 返回结果表。 查询结果格式如下所示。 示例1&#xff1a; 题目分析 如…

Ubuntu20.04安装MySQL8

Ubuntu20.04安装MySQL8 MySQL8下载 点击MySQL download下载官网&#xff0c;按照自己所需要的版本下载对应的MySQL版本&#xff0c;如下图 点击下载后在进行解压&#xff0c;大致文件如下所示 然后需要一次安装对应的.deb文件。普通.deb程序安装命令&#xff1a; dpkg -i …

前端:地图篇(一)

1、前言 在很多的出行程序中&#xff0c;都会使用到地图这一个功能&#xff0c;在实际的开发中我们也不会去开发一个自己的地图模型。如果自己开发一个地图模型&#xff0c;那么需要投入的成本、人力都是非常巨大的。所以我们很多网站和APP中使用的都是第三方的接口和JS&#…

本地部署 audiocraft

本地部署 audiocraft 1. 什么是 audiocraft2. Github 地址3. 安装 Miniconda34. 创建虚拟环境5. 部署 audiocraft6. 启动 MusicGen7. 访问 MusicGen 1. 什么是 audiocraft Audiocraft 是一个通过深度学习进行音频处理和生成的库。它具有最先进的 EnCodec 音频压缩器/分词器&am…

Java Selenium WebDriver 网页填报

一、windows环境安装配置 1.安装chrome浏览器 在“关于chrome”界面&#xff0c;查看浏览器版本号 2.下载chromeDriver 在https://registry.npmmirror.com/binary.html?pathchromedriver/下载对应版本的驱动&#xff08;如果浏览器版本过新&#xff0c;建议下载最接近的版…

day20-101. 对称二叉树

101. 对称二叉树 力扣题目链接 给定一个二叉树&#xff0c;检查它是否是镜像对称的。 思路 镜像对称必要的条件就是根节点的左右子树互相对称 左子树的左孩子 右子树的右孩子左子树的右孩子 右子树的左孩子 递归 使用递归前要确定递归的顺序&#xff0c;是前序、后序还…