伪随机序列生成函数&冒泡排序

一、伪随机序列生成函数

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;
}

第一次调试结果:

第二次调试结果:

        以上便是关于伪随机序列生成函数&冒泡排序相关的一些问题,如有错误或未提及之处,欢迎大家在评论区指正或补充以便讨论学习。

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值