关于如何评价洗牌质量的猜想

洗牌算法是卡牌类游戏中必须使用的算法,本质上说洗牌算法的目的是使某个给定的顺序更加的无序,因此出现了很多种洗牌算法。我们不重点讨论如何洗牌,我们将眼光关注于洗出的牌是否达到我们预期的要求,以及如何衡量洗出的牌无序的程度。首先先看一个简单有效的洗牌算法。

一、一个简单的洗牌算法

一个比较容易实现的洗牌算法是这样的,通过随机选出两张牌进行交换,通过多次这样的重复操作,就能达到洗牌的目的。事实证明这种洗牌方式还是比较可行,最重要的是比较简单,代码如下。

//洗牌算法,随机交换数组的两个元素,交换数组长度次为一次洗牌
template<class T>
void mess(T data[],unsigned int len)
{
    int i=len,j,k;
    T t;
    srand((unsigned int)time(0));//随机因子
    while(i--)//交换牌数次数
    {
        while(true)//找出两张不一样的牌
        {
            //随机产生两张牌
            j=rand()%len;
            k=rand()%len;
            if(j!=k)//不是同一个元素
            {
                //交换
                t=data[j];
                data[j]=data[k];
                data[k]=t;
                //退出进行下一次交换
                break;
            }
        }
    }
}

这种算法通过对有序的(假设开始洗牌时是有序的,并以此为参考)N张牌进行N次随机交换,事实达到了洗牌的目的。以下是一个20位有序牌几次洗牌后的结果:

虽然得到了我们想要的洗牌效果,但是我们却无法定量的衡量洗出牌的质量。换句话说就是如何确定洗出的牌究竟乱成什么样子?为了验证洗牌的质量,必须给出评价洗出的牌的一个定量的分析。

二、如何评价洗出牌的质量

牌洗出什么样子才算比较好,当然是越乱越好。但是这么说其实并不全面,在实际的卡牌游戏中,我们要达到的目的只需要保证下局游戏时洗出的牌要和洗牌之前的情形变化很大即可,这个就是洗牌算法的本身需要考虑的问题。假如给我们一副新的牌,内部是按照某个顺序排列的,洗牌算法要达到的目的是尽量让它混乱,但是混乱的结果如何呢?所以不论在任何情况下,计算出牌的混乱程度都是必须的,它可以为我们的进行其他流程提供参考。因此,归根结底还是需要讨论洗出牌的混乱程度。为了方便讨论这个问题,我们有个基本假设:如果按照某个顺序,无论是升序还是降序,这种顺序的牌的混乱程度应该定义为0很明显,有序的牌是不混乱的,那么下边就需要讨论混乱的牌的混乱程度怎么计算。

继续讨论之前,首先我们定义一个新的名词——混乱度[Degree of Chaos](无序度),记为Ch。这个概念类似于物理学中的熵。然后,我们把牌抽象为一个一维数组,数组初始值是按照自然数有序的,即1、2、3、4、5……这样,我们讨论的问题就变成了对一个无序数组的处理并得到一个混乱度的问题了。混乱度如何定义才比较合适呢?结合上述的洗牌算法,我有个大胆的猜想,给出混乱度的定义:

定义:无序序列通过交换两个内部元素还原为有序序列需要的最小次数。

这里定义有两个关键点,一个是通过交换两个元素还原为有序,另一个是最小次数。前者说明了评价无序序列的方式,即通过还原序列为有序进行交换元素,后者说明了若一个序列越混乱则越难还原为有序的序列,需要的次数越多,同时包含了还原为升序和降序序列需要的最小次数。

例如:对于序列A=(1,4,2,3,5),需要交换<3,4>、<2,3>两次才能还原为有序序列A0=(1,2,3,4,5)。当然还有其他的还原步骤,也可以还原为B0=(5,4,3,2,1)。但是不管怎么还原,需要的步数最好是2,即Ch(A)=2。

虽然能按照这种方式得到混乱度,但是如何通过定量的计算得到混乱度呢?因为这直接关系着程序设计的能否实现的问题。

三、混乱度的计算Ch(A)

计算混乱度之前,首先我们需要思考混乱度的定义。混乱度强调最少交换次数变为有序序列。在算法设计中,排序是很经典的问题了,排序算法数不胜数。我们期待的是能否通过排序算法的计算得到混乱度。既然是最少交换次数,我们首先想到的或许是快速排序算法,毕竟它是目前性能最好的排序算法。但是经过验证它并不合适,单纯的将元素按照中轴元素进行分割只会加大交换次数。然后我们尝试经典的排序算法——冒泡排序,事实证明也不行,因为冒泡排序每次都要进行交换,做了很多无用功。最后我们会想到选择排序,它的算法性能并不好,但是经过我们的验证,它是最可能接近我们期待答案的排序算法。为什么呢?改进后的选择排序算法思想大致可以这么描述:通过按序将数组的某一个元素与后边的元素比较,最后拿到最小(大)元素与本身交换,最终达到有序。假设数组大小是N,则根据选择排序算法,最大的交换次数不会大于N-1(最后一个元素不需要交换)。还拿刚才那个例子,A=(1,4,2,3,5),按照选择排序算法,通过交换<4,2>、<4,3>也能达到有序,而且也是2步!看起来选择算法能计算出我们想要的混乱度,通过下边的实验可以求出任意序列的混乱度,并能进一步验证我们的猜想。

首先,我们需要通过选择排序计算混乱度。算法大致如下:

//计算数组的混乱程度:无需数组通过交换元素恢复到有序(升序和降序)数组需要的最少交换次数
//暂时使用选择排序交换的次数进行计算,捎带验证
template<class T>
int messDegree(const T data[],const unsigned int len)
{
    T*upData=new T[len];//升序数据
    T*downData=new T[len];//降序数据
    int degree;//混乱度
    unsigned int i,j,upK,downK,upSum=0,downSum=0;
    T t;
    //拷贝数据
    for(i=0;i<len;i++)
    {
        upData[i]=downData[i]=data[i];
    }
    //排序计算
    for(i=0;i<len-1;i++)
    {
        upK=downK=i;
        for(j=i+1;j<len;j++)
        {
            if(upData[j]<upData[upK])//有更小的
                upK=j;
            if(downData[j]>downData[downK])//有更大的
                downK=j;
        }
        if(upK!=i)
        {
            t=upData[i];
            upData[i]=upData[upK];
            upData[upK]=t;
            upSum++;//计算升序交换次数
        }
        if(downK!=i)
        {
            t=downData[i];
            downData[i]=downData[downK];
            downData[downK]=t;
            downSum++;//计算降序交换次数
        }
    }
    degree=upSum<downSum?upSum:downSum;//取最小值
    delete[] upData;
    delete[] downData;
    return degree;
}

对通过选择排序得到的结果是否就是混乱度进行证明并不是很容易,本人知识有限,希望有高人出现帮我证明这个命题。下边讨论内容的前提是默认选择排序的方法是正确的。

四、最大混乱度Ch(N)

我们得到了混乱度的计算方法,针对有序序列A0=(1,2,3…),混乱度Ch(A0)=0。但是对于N维序列A,它的最大的混乱度Ch(N)是多少,这个能否计算呢?要知道,按照混乱度的定义,最大的混乱度代表着序列的最无序的状态,相应的也就代表牌被洗得不能再洗的情况,所以,最大混乱度有它实际的含义。但是计算最大混乱度目前还没有比较直接的公式可以使用,既然如此,我们就使用计算机枚举所有可能的序列来计算一部分最大混乱度。

我们将计算混乱度的算法嵌入到n阶排列算法中,于是得到所有的n!个排列对应的混乱度。由于我们只关心最大的混乱度Ch(N),所以算法只记录了Ch(N)。

//构造所有排列,求最大需要的交换个数——最大混乱度
template<class T>
void perm(T data[],int k,int m,int &max,int len)
{
    if(k==m)//一个排列出现
    {
        int t=messDegree(data,len);//计算混乱度
        if(max<t)
            max=t;//记录最大混乱度
        return ;
    }
    else
    {
        for(int i=k;i<=m;i++)
        {
            int t=data[i];
            data[i]=data[k];
            data[k]=t;
            perm(data,k+1,m,max,len);
            t=data[i];
            data[i]=data[k];
            data[k]=t;
        }
    }
}

对于N维序列,枚举出所有的可能序列是个排列问题,时间复杂度是O(n!)。因此算法的复杂度相当高,受限于机器能力,我只测试到N=11时对应的Ch(N)。由于3个元素一下的序列不存在混乱的问题,很明显Ch(1)=Ch(2)=0。所以讨论N≥3情况下的Ch(N)才有实际意义。以下是枚举所有情况得到的Ch(n):

Ch(3)=1、Ch(4)=3、Ch(5)=4、Ch(6)=4、Ch(7)=5、Ch(8)=7、Ch(9)=8、Ch(10)=8、Ch(11)=9。

得到这个结果其实并不令人满意,本想从中找到一些规律来更好地计算最大混乱度的希望破灭了。

五、总结

本文从洗牌算法中引申出如何评价洗出的牌质量的方法,首先引入概念——混乱度,然后提出通过按照选择排序算法进行计算混乱度的猜想,最后使用穷举的方式求解了简单的序列的最大混乱度Ch(N)。遗憾的是笔者并没有给出选择排序算法计算混乱度是否正确的形式化证明和最大混乱度的简便计算方法。希望基于笔者的想法,有能人异士帮助笔者帮助解决这两个问题,必不胜感激!(——Florian fanzhidongyzby@163.com)

关于如何评价洗牌质量的猜想相关推荐

  1. 2013手机App发展趋势预测:遇冷和洗牌

    自从2009年3G发牌,iPhone.Android等智能手机大规模上市,手机App市场呈现火爆态势.经历了将近4年的发展,2013年手机App市场将何去何从?接下来,根据笔者长期的观察,从市场.产品 ...

  2. 关于洗牌的研究(五)——从数学到魔术之印度洗牌

    爱学习,勤思考:学数学,玩魔术.欢迎点击头部蓝字关注MatheMagician,这里有你要的奇迹! 写再前面:本系列作品由MathMagician独家首发,一共有七篇,从数学和魔术两个角度对日常生活中 ...

  3. 开放式创新改变世界——OpenStack生态系统将重新洗牌

    摘要:OpenStack基础软件开发周期长.投入大.技术创新转化成产品需要更多时间,超越公司的开放协作才有利于和促进产业繁荣发展.OpenStack能否成为继Linux之后开放式创新的又一成功案例呢? ...

  4. PWA将带来新一轮大前端技术洗牌?

    作者 | 彭星 编辑 | 尾尾 一.回顾历史:移动时代之初,Web遭遇两大枷锁 Web 在移动时代遭遇两大枷锁1.Web 在移动时代遭遇两大枷锁 当 Web 自信满满,步入移动时代之时,它还没有做好充 ...

  5. 关于洗牌的研究(六)——从数学到魔术之完美洗牌

    爱学习,勤思考:学数学,玩魔术.欢迎点击头部蓝字关注MatheMagician,这里有你要的奇迹! 写再前面:本系列作品由MathMagician独家首发,一共有七篇,从数学和魔术两个角度对日常生活中 ...

  6. 深度:进入洗牌期的老年鞋市场对中国老年消费品行业的发展启示—足力健地位能否持续,奥康/红蜻蜓能否逆袭?

    本篇文章是继去年AgeClub解读老年鞋系列数据报告之后,时隔一年再次对老年鞋市场进行分析: 之所以持续不断分析老年鞋市场变化趋势是因为老年鞋是过去五年中老年消费品领域最快爆发出来的新品类机会,其发展 ...

  7. 大数据征信进入洗牌期,区块链或成新一代征信系统底层架构

    由于中国的消费金融需求旺盛但服务相对落后的行业特点,导致行业内数据滥用现象严重. 此前,人民银行征信中心副主任汪路在公开场合表示,在当前征信体系已经严重滞后于经济.金融发展需求的情况下,这些弊端应当受 ...

  8. 微软洗牌低代码开发市场,引发软件产业生态变局

    (基于Power Platform开发的微软员工应用) 2020年的全球软件市场将发生重大变化.Forrester预测,Service Mesh.Kubernetes.AI应用和低代码开发将在2020 ...

  9. 汽车基础软件赛道正在经历一轮洗牌

    http://www.evinchina.com/newsshow-1733.html 导读:基于SOA的软件架构,标准化基础软件(比如,OS及定制系统).AutoSAR及开发工具.专用中间件形成了域 ...

最新文章

  1. Linux 利用lsof命令恢复删除的文件
  2. Django中提供了6种缓存方式,你会几种?
  3. 从零写一个编译器(四):语法分析之构造有限状态自动机
  4. redis存储新闻列表_Redis对象——集合(Set)
  5. RequireJS使用注意地方
  6. 计算机操作系统详细学习笔记(四):设备管理 —— I/O 管理
  7. 漫画解读软件开发模式
  8. 获取支付宝小程序链接
  9. CSDN钱包提现协议
  10. 做了5年研发效能度量,我的6点思考
  11. 语音文件格式转换:.amr 转 .MP3, .wav格式
  12. 在你的 Android 手机上「云养猫」:Android 11 Beta 3 具透
  13. Django项目(五):注册模块—登录注册
  14. 二十八条改善 ASP 性能和外观的技巧
  15. 第一款带无代码应用搭建平台的固定资产管理系统
  16. 十个python热门项目,你知道几个
  17. 算法-回溯backtrack
  18. 云计算介绍 TCP/IP协议以及配置
  19. 微博评论爬虫 | 情感分析 | 词云图展示
  20. 线性回归、logistic回归、一般线性模型回归

热门文章

  1. python鱼眼图像识别_一种融合鱼眼图像与深度图像的动态环境视觉里程计方法与流程...
  2. 内存文件系统-md伪设备
  3. Python中的多线程编程
  4. 隐马尔可夫(HMM)、前/后向算法、Viterbi算法
  5. hadoop SecondNamenode 详解
  6. 20145207 《Java程序设计》第5周学习总结
  7. 【MongoDB】MongoDb的“not master and slaveok=false”错误及解决方法
  8. 盘点游戏行业的那些干货网站
  9. XPath element 格式
  10. php关闭warning