一、伪随机序列生成函数
1.伪随机序列生成函数:
rand 函数和 srand 函数(需要定义#include<stdlib.h>头文件)都可以用来生成伪随机数,这两个函数用于模拟程序和游戏程序(例如,在纸牌游戏中用来模拟骰子滚动或者发牌)。
#include<stdlib.h>
int rand(void);
void srand(unsigned int seed);
2.rand()函数:
(1)rand函数并不随机
每次调用 rand 函数时,系统都会返回一个0~RAND_MAX(定义在<stdlib.h>的宏,并非用户自己定义)的数。但 rand 函数返回的数事实上并不是随机的,这些数是由“种子”值产生的,在我们看来,似乎是能够产生随机数值的,但这个完整的程序如果经过多次调试,每一次调试出来的随机值都是相同的。例如:
定义一个 int 类型,大小为100的数组,使用rand()函数给每个元素赋值。
#include<stdio.h>
#include<stdlib.h>
#define ARSIZE 100
int main()
{
int ar[ARSIZE]={0};
for(int i=0;i<ARSIZE;i++)
{
ar[i]=rand()%100+1;
}
for(int i=0;i<ARSIZE;i++)
{
printf("%4d",ar[i]);
if ((i+1) % 10 == 0)
{
printf("\n");
}
}
}
第一次调试结果:
第二次调试结果:
我们发现,每次调试的结果都如上图所示,所以rand函数返回的数事实上并不完全随机。
(2)rand函数语法:
通用公式: a + rand() % n;其中 a 是起始值,n 是随机的范围。一般来说,要取得 [a,b] 的随机整数,使用 (rand() % (b-a+1))+ a;
如(1)中例子所示,如果我们想随机生成1~100里的数,则给出下列代码 ar[i]=rand()%100+1; 如果将代码改为:ar[i]=rand()%100; 这样会随机生成0~99里的数。
3.srand()函数:
我们已经知道rand() 产生的随机数在每次运行的时候都是与上一次相同的,若要不同, 则需在rand()之前使用 srand() 初始化它。那么具体怎么做呢?
我们先来看看srand()的源码声明:void srand(unsigned int seed); 这里的参数 seed 必须是个整数,如果给seed一个确定的值,例如srand(233),则种子值确定,但如果每次 seed 都设相同值,rand() 所产生的随机数值就会每次都是一样的,这和直接使用rand函数的效果相同。如果下一次换为srand(234),rand()所产生的随机数值才会不同于srand(233)下rand()所产生的随机数值。
始终使用同一个种子值的程序将总会从rand函数得到相同的数值序列,这个性质有时是非常有用的:程序在每次运行时按照相同的方式运行,这样会使测试更加容易。但是用户通常希望每次程序运行时rand函数都能产生不同的序列。如果玩纸牌的游戏程序总是发同样的牌,估计就没人会玩了。
srand函数允许我们选择自己想要的序列,所以为使返回的值随机化,需要每次给seed不同的值或直接给一个动态的值,使种子值随机化最常用的方法就是调用time函数,它会返回一个对当前日期和时间进行编码的数,当下时间不同,返回的数值也就不同。直白来说:随着时间的流逝,每过一秒就会返回一个不同的值,即实现了把time函数的动态返回值传递给srand函数,这样就可以使rand函数在每次运行时的返回值都不相同。一般可使用以下两句来定义:
srand((unsigned int)time(0)); 或 srand((unsigned int)time(NULL));
此句定义在rand()函数之前且只定义一遍即可。
#include<stdio.h>
#include<stdlib.h>
#define ARSIZE 100
int main()
{
int ar[ARSIZE] = { 0 };
int val = 0;
srand((unsigned int)time(0));
for (int i = 0; i < ARSIZE; ++i)
{
ar[i]= rand() % 100 + 1;
}
for (int i = 0; i < ARSIZE; i++)
{
printf("%4d", ar[i]);
if ((i+1) % 10 == 0)
{
printf("\n");
}
}
return 0;
}
第一次调试结果:
第二次调试结果:
这时每次调试出来的结果都变得不同。
二、冒泡排序
1.冒泡排序
冒泡排序是一种比较简单的排序算法。通过依次访问要排序的元素,相邻两个元素进行大小对比,实现将较大的数往序列后方排,最终达到将整个序列按照从小到大的顺序进行排列。
2.举个例子
一个序列:2 1 5 6 4 8 3 7 9,按照冒泡排序进行排序的过程如下:
(1)第一趟交换如图所示:
具体实现代码如下:
A.首先定义交换函数:
//伪代码
void Swap(int* ap, int* bp)
{
int tmp = * ap;
*ap = *bp;
*bp = tmp;
}
B.外层for循环实现每一趟交换;内层for循环实现每一趟内部逐个比对:
//伪代码
#define ARSIZE 9
for (int i = 1; i < ARSIZE; i++)
{
for (int j = 0; j < ARSIZE - i; j++)
{
if (ar[j] >= ar[j + 1])
{
Swap(&ar[j], &ar[j + 1]);
}
}
}
Tips:
a.注意外层for循环的初始化语句int i=1而非=0,因为我们之前提到外层循环是代表每一趟每一趟交换,交换是从第一趟开始的。
b.内层循环循环条件给出j<ARSIZE-i,因为每进行一趟交换,就会确定出一个次级大的数值,那么下一趟交换就可以减少一次数值对比,即内层循环次数减少一次。
C.内层for循环第一次执行完毕即完成了第一趟冒泡排序,最终将序列2 1 5 6 4 8 3 7 9变为 1 2 5 4 6 3 7 8 9。
(2)依照上述方法确定每一趟交换后的序列如下,每一趟最终会确定一个次级大的数,如下图所示:
如上图所示,我们不难发现在第五趟交换完成(内层for循环执行第五遍后)后就已经完成了整个序列的排序,但没有经过优化的代码会继续执行,直至内层for循环执行完毕。
我们可以通过在内层循环的if语句中添加bool类型标签来判断是否执行if语句从而提前跳出以结束内层循环,即可实现代码的优化以及时间复杂度的缩短:
//伪代码
#define ARSIZE 9
for (int i = 1; i < ARSIZE; i++)
{
bool tag = 1;
for (int j = 0; j < ARSIZE - i; j++)
{
if (ar[j] >= ar[j + 1])
{
Swap(&ar[j], &ar[j + 1]);
tag = 0;
}
}
if (tag)
{
break;
}
}
三、数组冒泡排序
综合以上,我们来看一道例题:
定义大小为100的整型数组,使用随机函数给元素赋值,数值范围1~100,并且使用冒泡排序给元素进行排序。
1.首先定义交换函数并且定义数组以及随机函数赋值:
#include<stido.h>
#include<stdlib.h>
#define ARSIZE 100
void Swap(int* ap, int* bp) //定义交换函数
{
int tmp = * ap;
*ap = *bp;
*bp = tmp;
}
int main()
{
int ar[ARSIZE] = { 0 };
srand((unsigned int)time(0)); //初始化种子为随机值,使得每次调试所得的随机值结果随机化,此条不能写在循环语句内
for (int i = 0; i < ARSIZE; ++i)
{
ar[i] = rand() % 100 + 1; //rand函数赋值
}
for (int i = 0; i < ARSIZE; i++)
{
printf("%4d", ar[i]);
if ((i + 1) % 10 == 0)
{
printf("\n");
}
}
printf("\n");
return 0;
}
Tips:注意srand((unsigned int)time(0)); 写在for循环外,即作用域为全局,定义一次即可为rand函数提供种子值。
2.内外层for循环实现冒泡排序并再次打印进行对照:
#include<stido.h>
#include<stdlib.h>
#define ARSIZE 100
void Swap(int* ap, int* bp) //定义交换函数
{
int tmp = * ap;
*ap = *bp;
*bp = tmp;
}
int main()
{
int ar[ARSIZE] = { 0 };
srand((unsigned int)time(0)); //初始化种子为随机值,使得每次调试所得的随机值结果随机化,此条不能写在循环语句内
for (int i = 0; i < ARSIZE; ++i)
{
ar[i] = rand() % 100 + 1; //rand函数赋值
}
for (int i = 0; i < ARSIZE; i++)
{
printf("%4d", ar[i]);
if ((i + 1) % 10 == 0)
{
printf("\n");
}
}
printf("\n");
for (int i = 1; i < ARSIZE; i++) //内外for循环实现冒泡排序
{
bool tag = 1; //定义bool tag以优化代码
for (int j = 0; j < ARSIZE - i; j++)
{
if (ar[j] >= ar[j + 1])
{
Swap(&ar[j], &ar[j + 1]);
tag = 0;
}
}
if (tag)
{
break;
}
}
for (int i = 0; i < ARSIZE; i++) //再次打印进行对照
{
printf("%4d", ar[i]);
if ((i + 1) % 10 == 0)
{
printf("\n");
}
}
return 0;
}
第一次调试结果:
第二次调试结果:
以上便是关于伪随机序列生成函数&冒泡排序相关的一些问题,如有错误或未提及之处,欢迎大家在评论区指正或补充以便讨论学习。