开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 1382|回复: 1
收起左侧

[其它] 比较洗牌算法的两种实现方法

[复制链接]

发表于 2013-2-17 13:09:38 | 显示全部楼层 |阅读模式   广东省广州市
我们首先会介绍随机生成法,该方法要点就是从头开始逐个随机生成规定区域的数字,如果新生成随机数之前已经生成过就不保存该数;如果新生成的随机数之前没有生成过就保存该数;直到生成的数字的数量达到所需的数量。接下来我们还会介绍交换位置法。




方法一:随机生成法
首先,我介绍一种很常见的方法:随机生成法(我自己命名的),这方法我在扫雷游戏中随机分布雷的位置时用过(思想是一样的),该方法要点就是从头开始逐个随机生成规定区域的数字,如果新生成随机数之前已经生成过就不保存该数;如果新生成的随机数之前没有生成过就保存该数;直到生成的数字的数量达到所需的数量。
实现代码如下:

    size_t shuffle(char s[], int n)
  • {
        size_t t=0;//计算循环次数
  •     int c=0;
        while(c<n)
  •     {
            t++;
  •         int num = rand()%n;
            if (memchr(s,num,c) == NULL)
  •         {
                s[c++] = static_cast<char>(num);
  •         }
        }
  •     return t;
    }

  • void printCards(char s[], int n)
    {
  •     for (int i=0; i<n; i++)
        {
  •         cout << static_cast<int>(s) << " ";
        }
  •     cout << endl;
  • }
代码中使用了memchr函数(时间复杂度可能是O(n),没找到依据),即使是O(1),它的循环生成随机数的次数是不固定的(大于等于需要生成的个数)。
方法二:交换位置法
这种方法是我昨天在参加腾讯笔试考试时候想到的,今天回到学校后在寝室测试了一番,基本思路是:先初始化一串分布的数字,然后为每个位置依次生成一个与之交换的随机位置,如果生成的随机位置不是它本身就执行交换操作。
实现代码:

    void swap(int& a, int& b)
  • {
        a = a^b;
  •     b = a^b;
        a = a^b;
  • }
  • size_t shuffle2(int s[], int n)
    {
  •     size_t t=0;//计算循环次数
        for (int i=0; i<n; i++)
  •     {
            t++;
  •         s = i;
        }
  •     for (int i=0; i<n; i++)
        {
  •         t++;
            int num = rand()%n;
  •         if (num != i)
            {
  •             swap(s[num],s);
            }
  •     }
        return t;
  • }
  • void printCards2(int s[], int n)
    {
  •     for (int i=0; i<n; i++)
        {
  •         cout << s << " ";
        }
  •     cout << endl;
  • }

比较:在时间上方法二比方法一快好多,因为交换位置的次数的最大值是限定了的(生成随机数的次数是固定的),而且省去了查找新生成数是否在已生成数中的时间。方法一中,当新生成的数在已生成的数中就需要从新生成一个随机数,从而随机生成数的次数是不固定的(有最小值)。
测试代码:

结果:

我还是不能确定第二种方法是不是更好的,因为是自己想到的,我的验证也不是很完善,也许有什么其他的缺点(比如说随机性太弱),也没在其他书上看到过,如果网友们在哪看到过就告诉下我吧,方法一是在《c和c++代码精粹》中文版P97中找到的。
后续补充:
谢谢chncwang的回复,方法二不是完全随机的,完全随机的修改如下:

    size_t shuffle22(int s[], int n)
  • {
        size_t t=0;//计算循环次数
  •     for (int i=0; i<n; i++)
        {
  •         t++;
            s = i;
  •     }
        for (int i=n-1; i>0; --i)
  •     {
            t++;
  •         int num = rand()%(i+1);
            if (num != i)
  •         {
                swap(s[num],s);
  •         }
        }
  •     return t;
  • }
因为"第1次移动后,第1个数还在第1个位置的概率是1/n,后续移动只会减少这个概率。所以这个算法不是完全随机的"。修改后的算法其实就是使用C++的STL<algorithm>库中的random_shuffle()函数的实现方法。取随机数的时候的范围每次都随着i的改变而变动,这样就不会减少之前的位置的数还是原来的数的概率了(即后续交换操作不会影响到已经交换过的位置)。

使用标准库<algorithm>中的random_shuffle()函数实现很简单,代码如下:

    int main()
  • {
        vector<int> s_stl;
  •     for (int i=0; i<CARDS_COUNT; ++i) s_stl.push_back(i);
        random_shuffle(s_stl.begin(),s_stl.end());
  •     cout << "使用C++算法库:";
        for (vector<int>::iterator it=s_stl.begin(); it!=s_stl.end(); ++it)
  •         cout << " " << *it;
        return 0;
  • }



本帖被以下淘专辑推荐:

  • · VC|主题: 2, 订阅: 0
结帖率:71% (5/7)
发表于 2013-2-17 13:30:10 | 显示全部楼层   广东省潮州市
沙发
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表