洗牌复原次数c语言公式,洗牌算法Fisher-Yates以及C语言随机数的产生

前些天在蘑菇街的面试中碰到一道洗牌的算法题,拿出来和大家分享一下!

原题是:54张有序的牌,如何无序的发给3个人?

这个题是运用经典的洗牌算法完成。首先介绍一种经典的洗牌算法--Fisher-Yates.现在大家在网上看到,大多是Fisher-Yates算法的变形。将本来O(n2),简化到了O(n).代码如下:

#include

#include

void func(char *, int);

void main()

{

char a[7] = {'a','b','c','d','e','f'};

func(a,6);

//a[7] = '\0';

puts(a);

}

void

func(char *date, int length){

char t; //t为交换字符空间

int i, j;

while(--length){

srand(time(0));

i = rand()%(length+1);

t = date[i];

date[i] = date[length];

date[length] = t;

}

}

算法的思路就是:

a,选中未操作的最后1个位置,在1至该位置之间产生一个随机数(包含这两个值),

b,然后交换这两个值,造成的结果就是在未操作的所有牌中随机的取一张放到未操作的牌底(数组尾),然后length-1使未操作的范围减一,

c,重复上面步骤,直到未操作的值为1停止

代码写的很清晰,也很简单。网上还有很多种方法。但通过大量的测试,可以给出肯定,改进后的Fisher-Yates算法,即上述代码,已经是洗牌算法中很好的一个了。即简单,随机性又好。所以如果在面试中给出上述答案,就基本可以进行下一个问题了。如果面试官还觉得不够。可以从代码的完整性和鲁棒性(剑指offer中有提到具体做法),如参数正确性,遇到无效输入等方面改进。或者优化随机值。

关于优化随机值,这里我们还可以再提一点。

首先必须肯定的一点是:计算机中的随机数并不是真正的随机数,它是一种伪随机数,是通过某些公式计算出来的随机数,通过改变式子中参数的值从而达到随机的效果。虽然是伪随机数,但其实已经可以达到我们编程中的要求了。

C语言中生成随机数的函数有4个,两两搭配使用:

int rand(void);                  返回 0 ------- RAND_MAX 之间的一个 int 类型整数,该函数为非线程安全函数。并且生成随机数的性能不是很好,已经不推荐使用。

void srand(unsigned int seed);          设置种子值,一般与“当前时间 + 进程ID”作为种子,如果没用调用该函数,则通过rand返回的默认种子值为1。

long int random(void);      返回 0 ------- RAND_MAX 之间的一个 long 类型整数,该函数会产生一个非常大的随机值,最大为 16*((2**31)-1)。

random 函数使用非线性反馈随机数发生器生成默认大小为31个长整数表所返回的连续伪随机数。

void srandom(unsigned int seed);      设置种子值,一般与“当前时间 + 进程ID”作为种子,如果没用调用该函数,则通过random返回的默认种子值为1。

上面的这四个函数都是C语言产生随机数时用到的函数,

如果你使用 srandom 种植种子, 则你应该使用 random 返回随机数, 如果你使用 srand 种植种子, 则你应该使用rand返回随机数。

不过srand和rand官方已经不推荐使用。原因是产生随机数的性能不是很好, 另外是随机数的随机性没有random好, 再者就是不是线程安全。

至于rand的线程不安全,如果你在linux下看过它的源代码的话,这句话就很容易理解了。

/*

这两个函数是C库中产生随机数的程序。你需要先

使用srand()函数赋随机数种子值。然后再使用

rand()函数来产生随机数。但是产生随机数的算法

较简单,srandom()和random()函数是对这两个函数

的改良,用法也很类似。

*/

#define RANDOM_MAX 0x7FFFFFFF

static long my_do_rand(unsigned long *value)

{

/*

这个算法保证所产生的值不会超过(2^31 - 1)

这里(2^31 - 1)就是 0x7FFFFFFF。而 0x7FFFFFFF

等于127773 * (7^5) + 2836,7^5 = 16807。

整个算法是通过:t = (7^5 * t) mod (2^31 - 1)

这个公式来计算随机值,并且把这次得到的值,作为

下次计算的随机种子值。

*/

long quotient, remainder, t;

quotient = *value / 127773L;

remainder = *value % 127773L;

t = 16807L * remainder - 2836L * quotient;

if (t <= )

t += 0x7FFFFFFFL;

return ((*value = t) % ((unsigned long)RANDOM_MAX + ));

}

static unsigned long next = ;

int my_rand(void)

{

return my_do_rand(&next);

}

void my_srand(unsigned int seed)

{

next = seed;

}

从代码中可以清晰的看到,srand()函数是将参数赋值给了一个全局变量,这也是为什么srand()使用int型参数且没有返回值却可以影响rand()的值。srand()修改了next的值后,rand()函数可能隐式的调用了该参数(间接调用)。看到这里,你就能明白为什么它是线程不安全函数,如果再多线程操作中,一个线程线调用srand,再未完成时另一个线程修改了next则该次操作无意义,这就是基本的多线程编程中的进程的同步与互斥。random()的改进就是基于这一点进行改进。除此之外,我们还可以看到rand()的返回值是基于一个类似于y=ax+b的函数计算出来的。而该表达式中a,b都是常量值,而这里的x就是随机种子,即通过srand()修改种子。

最后要提的一点是:

假如你想产生 1 ------10 之间的一个随机数, 建议您像下面这样编码

j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));

而不是下面这样的代码

j = 1 + (rand() % 10);

第一行代码的优势在于使用实型代替整形,数据是连续的,从而随机的更均匀,从而提高随机性。

如果你想要产生1-8的随机数,j = 1 + (int) (8.0 * (rand() / (RAND_MAX + 1.0)));  //即可

1-x:    j = 1 + (int) (x.0 * (rand() / (RAND_MAX + 1.0)));      //即可

Fisher&ndash&semi;Yates shuffle 洗牌算法&lpar;zz&rpar;

1,缘起 最近工作上遇到一个问题,即将一组数据,比如[A,B,C,D,E]其中的两个B,E按随机排列,其他的仍在原来的位置: 原始数组:[A,B,C,D,E] 随机字母:[B,D] 可能结果:[A,B ...

由乱序播放说开了去-数组的打乱算法Fisher–Yates Shuffle

之前用HTML5的Audio API写了个音乐频谱效果,再之后又加了个播放列表就成了个简单的播放器,其中弄了个功能是'Shuffle'也就是一般播放器都有的列表打乱功能,或者理解为随机播放. 但我觉得 ...

洗牌算法shuffle

对这个问题的研究始于一次在群里看到朋友发的洗牌面试题.当时也不知道具体的解法如何,于是随口回了一句:每次从剩下的数字中随机一个.过后找相关资料了解了下,洗牌算法大致有3种,按发明时间先后顺序如下: 一 ...

random array &amp&semi; shuffle 洗牌算法 &sol; 随机算法

random array & shuffle shuffle 洗牌算法 / 随机算法 https://en.wikipedia.org/wiki/Fisher–Yates_shuffle ES ...

洗牌算法Fisher&lowbar;Yates原理

1.算法 http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle 简单的原理如下图所示: 2.原理 总结下,洗牌算法Fisher_Yates ...

js 随机数 洗牌算法

function shuffle(arr){ var len = arr.length; for(var i = 0;i

knuth洗牌算法

首先来思考一个问题: 设计一个公平的洗牌算法 1. 看问题,洗牌,显然是一个随机算法了.随机算法还不简单?随机呗.把所有牌放到一个数组中,每次取两张牌交换位置,随机 k 次即可. 如果你的答案是这样, ...

C&num; 洗牌算法

最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精   C#洗牌算法如下: class Program { ...

519&period; Random Flip Matrix(Fisher-Yates洗牌算法)

1. 问题 给定一个全零矩阵的行和列,实现flip函数随机把一个0变成1并返回索引,实现rest函数将所有数归零. 2. 思路 拒绝采样 (1)先计算矩阵的元素个数(行乘以列),记作n,那么[0, n ...

随机推荐

&lpar;AS3&rpar;关于arguments

一.官方说明 点击访问 二.使用心得 arguments包含了当前执行方法的参数,注意,不包含默认参数! arguments可以全局访问,可以在任何方法里访问,除此之外,在定义变量的时候或者初始化的时 ...

jquery常用正则表达式

1.邮箱验证正则表达式:/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/ 2.手机验证正则表达式:/^ ...

radio checked不起作用的原因

Servlet工作原理

Servlet生命周期分为三个阶段: 1,初始化阶段  调用init()方法 2,响应客户请求阶段 调用service()方法 3,终止阶段 调用destroy()方法 Servlet初始化阶段: 在 ...

poj 2976 Dropping tests

http://poj.org/problem?id=2976 这道题就是从n个a[i]和b[i]中去掉k个a[i]和[i]使得.最大. #include #include ...

sqlplus登录Oracle时ORA-01017&colon; invalid username&sol;password&semi; logon denied的错误

今天用scott用户登录Oracle数 据库时,竟然出现了ORA-01017: invalid username/password; logon denied错误,原以为是因为我的scott用户没有解 ...

C&num;面向对象(1)

一.面向对象(OOP) 面向过程 面向过程就是分析出解决问题的所需要的步骤,然后每个步骤使用函数实现,使用时将函数依次调用即可 C语言 面向对象 对象:生活中真实存在的事物(电脑.手机.草.树.... ...

山西大同大学教务处学生端--送给学弟,学妹的礼物,可在PC端,手机端操作

解决问题:大同大学教务处官网学生端口一进去就卡住了,点上面一行的菜单无响应 转眼已是四年,想想自己大学即将结束,不由得让人感慨啊.这才刚开学几天,我就听到有同学在因为补考,选课的事情发愁.学校官方的教 ...

k8s的使用

. 查看 kubectl 的状态 kubectl version . 查看集群信息 kubectl cluster-info . 查看节点信息 kubectl get nodes . 创建一个发布 k ...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值