洗牌算法

本文探讨了洗牌算法的实现,通过分析C语言中的rand函数,解释了其随机性背后的原理。并提出了一种改进的洗牌算法,通过不断缩小随机范围,避免重复,实现了数组元素的有效随机排列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

所谓的洗牌算法,大概意思就是将一组数随机打乱,说到随机,很容易就想到C里面的rand函数,en...说到这个函数,看看该函数如何使用

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	srand(time(NULL));
	for(int i=1;i <= 10;++i)
	{
		printf("%d\r\n",rand());
	}
	return 0;
}

这里我打印了10个数,可以快速的运行几组程序,其实可以发现每组程序出来的结果会很接近或者相似,为啥呢,可以跟进去看看源码

// Seeds the random number generator with the provided integer.
extern "C" void __cdecl srand(unsigned int const seed)
{
    __acrt_getptd()->_rand_state = seed;
}



// Returns a pseudorandom number in the range [0,32767].
extern "C" int __cdecl rand()
{
    __acrt_ptd* const ptd = __acrt_getptd();

    ptd->_rand_state = ptd->_rand_state * 214013 + 2531011;
    return (ptd->_rand_state >> 16) & RAND_MAX;
}

可以看出,其算法之简陋,其代码中的_rand_state也就是种子,所以当srand传人一个固定值的话,每次随机出来的值也会一样。我们使用时间当种子,当快速运行几组程序后,其结果当然也会近似。所以在一些重要的工作场合中,要慎用rand函数随机,毕竟可能可以预测哦。

这里的话扯远了,我们学习就先用这个rand函数吧,那么洗牌中的打乱问题我们可以解决了,下面解决下一个问题,当随机出来一个数后,我们必然要保证这个随机数不能重复出现,毕竟牌值是唯一的。

很容易想到一种方案,那就是遇到重复就重新在随机一个,en,那么当范围值很大的话,可以考虑下可行不?

假设打乱十万个数,这个时候,越到后面,或者说还剩余一两个数的时候,那么太困难了,虽然可能最后可能可以实现,不过时间成本太大,无限猴子定理,哈哈。

所以换种思路,上面的思路根本原因是没有把随机的范围给缩小,如何缩小,直接把筛选出来的数扔出去呗,重新在这个范围内选择,即可以确保其不会重复,也会缩小范围,大概的算法思想是这样,具体看下面分析

假设数组有10个元素,其元素值依次为1 ~ 10,这里的话因为涉及到缩小范围,所以我们需要的是随机下标值,如下:

那么我们随机的范围就是[0,9],假设我们随机出来的是5,那么我们只需将5下标对应的数值6和10进行交换,再将其end指针往前挪一位即可

后面我们的随机范围就变成了[0,8],假设此时随机出来为3,那么变换后结果如下:

后面的步骤就不多说了,重复即可。

参考代码:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	srand(time(NULL));
	
	int nArr[10] = {0};
	int nLen = sizeof(nArr)/sizeof(int);
	int nEnd = nLen;
	
	for(int i=0;i < nLen;++i)
	{
		nArr[i] = i + 1;
	}
	//洗牌算法核心 
	while(nEnd)
	{
		int index = rand() % nEnd; //随机出来[0,nEnd-1]
		--nEnd;//缩小范围 
		if(index != nEnd)
		{//交换 
			nArr[index] ^= nArr[nEnd];
			nArr[nEnd] ^= nArr[index];
			nArr[index] ^= nArr[nEnd];
		}
	}
	
	printf("洗牌后结果: \r\n");
	for(int i=0;i < nLen;++i)
	{
		printf("%d ",nArr[i]);
	}
	printf("\r\n");
	return 0;
}

测试结果如下:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值