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

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

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

#include<stdio.h>
#include <stdlib.h> 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-Yates以及C语言随机数的产生的更多相关文章

  1. Fisher–Yates shuffle 洗牌算法(zz)

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

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

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

  3. 洗牌算法shuffle

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

  4. random array & shuffle 洗牌算法 / 随机算法

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

  5. 洗牌算法Fisher_Yates原理

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

  6. js 随机数 洗牌算法

    function shuffle(arr){ var len = arr.length; for(var i = 0;i<len -1;i++) { var idx = Math.floor(M ...

  7. knuth洗牌算法

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

  8. C# 洗牌算法

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

  9. 519. Random Flip Matrix(Fisher-Yates洗牌算法)

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

随机推荐

  1. C++语言基础(10)-虚继承

    一.产生背景 先看下列一份代码: //间接基类A class A{ protected: int m_a; }; //直接基类B class B: public A{ protected: int m ...

  2. Swift 烧脑体操一

    Swift 烧脑体操(一) - Optional 的嵌套   前言 Swift 其实比 Objective-C 复杂很多,相对于出生于上世纪 80 年代的 Objective-C 来说,Swift 融 ...

  3. python 转化文件编码 utf8

    使用visual studio最大的一个问题就是文件编码问题,当文件中有中文时,visual studio 会默认为区域编码,也就是gb2312,如果想跨平台或者不用vs编译的话,就会因为编码问题导致 ...

  4. https原理与实践

    HTTPS 原理与证书实践   分类: Web应用   1.1 网络安全知识 1.1.1 网结安全出现背景 网络就是实现不同主机之间的通讯,网络出现之初利用TCP/IP协议簇的相关协议概念,已经满足了 ...

  5. atom安装插件

    1. 由于墙的原因,在界面中安装不了 先安装 npm 可以下载node.js桌面版命令行 2.打开命令行中进入atom的安装目录,进入到packages 中 3. 进入github 官网 输入你要下载 ...

  6. 【转】支付宝WAP支付接口开发

    支付宝WAP支付接口开发 因项目需要,要增加支付宝手机网站支付功能,找了支付宝的样例代码和接口说明,折腾两天搞定,谨以此文作为这两天摸索的总结.由于公司有自己的支付接口,并不直接使用这个接口,所以晚些 ...

  7. AJAX是基于现有的Internet标准

    AJAX是基于现有的Internet标准 AJAX是基于现有的Internet标准,并且联合使用它们: XMLHttpRequest 对象 (异步的与服务器交换数据) JavaScript/DOM ( ...

  8. Spring MVC配置静态资源和资源包教程

    1- 介绍 这篇教程文章是基于: Spring 4 MVC 2- 创建一个项目 File/New/Other.. 输入: Group ID: com.yiibai Artifact ID: Sprin ...

  9. Spring MVC资源绑定视图解析器

    ResourceBundleViewResolver使用属性文件中定义的视图bean来解析视图名称. 以下示例显示如何使用Spring Web MVC框架中的ResourceBundleViewRes ...

  10. c语言之linux下gettimeofday函数windows替换方案

    * Copyright (C) 2008 mymtom (mymtom@hotmail.com) * All rights reserved. * * Redistribution and use i ...