一个数组实现扑克牌均匀随机洗牌------多次洗牌能否避免黑客的计算?

闲来无事,研究下纸牌发牌,按斗地主来发吧,思路如下:

1,新建一个数组,长度52,将四种花色和大小王存储进数组

2,循环0至51,在循环因子i至52之间取随机数(能取到下界,不能取到上界),取到的随机数作为数组元素下标取该元素,与第i个元素交换位置,循环结束即排序完毕

3,输出纸牌即可。

思路明确,"啪啪啪~~" 12秒之后 贴上代码

1初始化数组

            //声明存放纸牌的数组
            string[] Card = new string[54];
            //初始化四种花色和大小王,分别用▲★◆■+数字和♂♀代表
            for (int i = 0; i < 54; i++)
            {
                switch(i/13)
                {
                    case 0: Card[i] = ‘▲‘ + (i % 13 + 1).ToString(); break;
                    case 1: Card[i] = ‘★‘ + (i % 13 + 1).ToString(); break;
                    case 2: Card[i] = ‘◆‘ + (i % 13 + 1).ToString(); break;
                    case 3: Card[i] = ‘■‘ + (i % 13 + 1).ToString(); break;
                    case 4: Card[i] = i % 13 == 0 ? "♂" : "♀"; break;
                    default: break;
                }
            }

2 排序

        //纸牌洗牌函数(排序)
        public static string[] Sort(string[] Card)
        {
            //定义随机数
            Random ran=new Random();
            if (Card.Count() > 0)
            {
                for (int i = 0; i < Card.Count(); i++)
                {
                    int R=ran.Next(i, Card.Count());
                    //交换第i张牌和i之后的随机一张牌
                    string flag = Card[i];
                    Card[i] = Card[R];
                    Card[R] = flag;
                }
            }
            return Card;
        }

3打印出来

            //打印
            //定义变量判断是否发完一家牌
            int pionter = 0;
            foreach (var card in Card)
            {
                Console.Write(card);
                pionter++;
                //发完一家
                if (pionter % 17 == 0)
                {
                    Console.Write("\n");
                    Console.Write("*****************************************************************");
                    Console.Write("\n");
                }
            }

运行结果如下:

理论上说这个排序应该已经OK了, 但是看了《当随机不够随机:一个在线扑克游戏的教训》这篇博文之后,我们会意识到这种排序由于种子选取的原因(系统时间)存在被破解的风险

那么问题来了,怎么来避免这种情况呢 ? 首先我们尝试排序多次,多少次合适呢,完全可以排序随机次数,暂定在(5-15)之前随机一个数字作为排序次数,

那么我们可以修改我们的排序方法为如下代码:

        /// <summary>
        /// 纸牌洗牌函数(排序
        /// </summary>
        /// <param name="Card">待排序数组</param>
        /// <param name="num">排序次数</param>
        /// <returns></returns>
        public static string[] Sort(string[] Card,int num)
        {
            //定义随机数
            Random ran=new Random();
            if (Card.Count() > 0)
            {
                for (int i = 0; i < Card.Count(); i++)
                {
                    int R=ran.Next(i, Card.Count());
                    //交换第i张牌和i之后的随机一张牌
                    string flag = Card[i];
                    Card[i] = Card[R];
                    Card[R] = flag;
                }
            }
            //排序次数大于1则递归继续排序,当次数小于等于1时,返回Card
            if (num > 1)
            {
                num--;
                Sort(Card,num);
            }
            return Card;
        }

理论上我们多次排序之后,通过运算解出排列的解已经非常困难了,先贴上全部的代码,然后再考虑我们的排序方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        Random ran=new Random();
        static void Main(string[] args)
        {
            //声明存放纸牌的数组
            string[] Card = new string[54];
            //初始化四种花色和大小王,分别用▲★◆■+数字和♂♀代表
            for (int i = 0; i < 54; i++)
            {
                switch (i / 13)
                {
                    case 0: Card[i] = ‘▲‘ + (i % 13 + 1).ToString(); break;
                    case 1: Card[i] = ‘★‘ + (i % 13 + 1).ToString(); break;
                    case 2: Card[i] = ‘◆‘ + (i % 13 + 1).ToString(); break;
                    case 3: Card[i] = ‘■‘ + (i % 13 + 1).ToString(); break;
                    case 4: Card[i] = i % 13 == 0 ? "♂" : "♀"; break;
                    default: break;
                }
            }
            Random Ran = new Random();
            int num = Ran.Next(5, 15);
            //调用洗牌函数洗牌
            Card = Sort(Card, num);

            //打印
            //定义变量判断是否发完一家牌
            int pionter = 0;
            foreach (var card in Card)
            {
                Console.Write(card);
                pionter++;
                //发完一家
                if (pionter % 17 == 0)
                {
                    Console.Write("\n");
                    Console.Write("*****************************************************************");
                    Console.Write("\n");
                }
            }
        }
        /// <summary>
        /// 纸牌洗牌函数(排序
        /// </summary>
        /// <param name="Card">待排序数组</param>
        /// <param name="num">排序次数</param>
        /// <returns></returns>
        public static string[] Sort(string[] Card,int num)
        {
            //定义随机数
            Random ran=new Random();
            if (Card.Count() > 0)
            {
                for (int i = 0; i < Card.Count(); i++)
                {
                    int R=ran.Next(i, Card.Count());
                    //交换第i张牌和i之后的随机一张牌
                    string flag = Card[i];
                    Card[i] = Card[R];
                    Card[R] = flag;
                }
            }
            //排序次数大于1则递归继续排序,当次数小于等于1时,返回Card
            if (num > 1)
            {
                num--;
                Sort(Card,num);
            }
            return Card;
        }
    }
}

1,我们的排序方法是从第一个数字开始排列,在一轮排序中,当排到第i个元素时,i之前的已经排列完毕,固定不动,

但是i之后的所有元素被抽到的概率是一样的,这能够保证排到每一张牌时,都是剩下所有牌均匀概率取1

2,我们不是排到i时候将除i之外的所有元素随机取1个来和i交换,这种方法显然会导致已排完的元素再次交换,导致洗牌不均匀的问题

3,我们排序每次都是从第一个开始来排,为什么不是随机一个元素开始排(例如从第10个元素开始排,排到53之后,再排0到9,实现很简单),

我个人思考之后认为这与从第一个元素排的随机程度和被破解的难度应该是一致的(貌似是废话)。

说了一堆~现就这样吧 OK that‘s all!

时间: 2024-10-01 10:59:17

一个数组实现扑克牌均匀随机洗牌------多次洗牌能否避免黑客的计算?的相关文章

如何随机洗牌一个数组

在使用javascript的时候有可能会有随机打乱一个数组的需求,我们可以利用数组的sort方法和Math.random来随机排序 const arr = [1,2,3]; arr.sort(() => 0.5 - Math.random()) console.log(arr) 主要利用了Math.random()生成随机数与0.5的大小比较来排序,如果0.5 - Math.random()大于或等于0,数组中的两个数位置不变,小于0就交换位置. 乍一看这样是达到了随机排序的效果,但是实际上这个

js实现随机选取[10,100)中的10个整数,存入一个数组,并排序。 另考虑(10,100]和[10,100]两种情况。

1.js实现随机选取[10,100)中的10个整数,存入一个数组,并排序. 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script type="text/java

写一个函数实现数组中的元素随机乱序排序

//原生JS写一个函数实现一个shuffle方法,将数组中的元素随机乱序排序 var shuffle = function(arr){ var len,t,rand; for(var i =0;len = arr.length,i<len;i++){ rand = parseInt(Math.random()*len);//parseInt(Math.random()*(len-1-0)+1);或者rand = Math.floor(Math.random()*(len-1-0)+1);即Mat

从一个数组中随机取出一定数量元素组成新数组

/** * 从一个数组中随机取出一定数量元素组成新数组 * @param array 一个String类型的数组 * @param number需要取出元素的数量 * @return 一个随机的数组 * @throws NullPointerException原数组不能为空 *@throws ArrayIndexOutOfBoundsException新数组长度应不大于原数组的长度 */ public static String[]  getRandomArray(String[] array,

JS案例之8——从一个数组中随机取数

近期项目中遇到一个需求,从一个列表中随机展示列表的部分内容,需求不大,JS也非常容易实现.主要是运用到了Math对象的random方法,和Array的splice方法. 思路是先新建一个数组,存放所有的列表,然后算出随机数,从数组中取出这个随机索引对应的值,然后组成一个随机数组. 源代码如下: 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" Conten

如何从一个数组中生成随机数组

有一个需要,给定一个数组,从中生成要求个数的随机数组,不重复,即getRandomArray(int[] originalArray,int number) 这样的一个函数.想了一下,可以这样做: 把数组元素放到一个List中 从List中随机取一个数 把取到的数从List中删除 重复上述过程 代码如下: import java.util.ArrayList; import java.util.List; import java.util.Random; public class Test {

数组的三种随机排序方法

第一种.利用数组自带的sort方法(下面是完整代码) 这种方法是利用随机出一个正数或者负数来让数组里面的内容两两对比,是正数就是顺序,是负数则是倒序,这种方法的缺点就是随机性不高,不能完全随机,因为是两两对比,所以最后一个数在最后的可能性较大. 第二种.利用递归函数对比(下面是完整代码) 递归的方法是利用递归函数的自调,定义一个随机数index(因为定了向下取整,所以范围为0~8)作为随机下标,然后将它对应的数从数组中取下压入到result数组中,从而实现随机排序,定义if判断,如果cloneA

代码实现:模拟斗地主洗牌和发牌,牌没有排序

package com.loaderman.test; import java.util.ArrayList; import java.util.Collections; public class Test { /** * * A:案例演示 * 模拟斗地主洗牌和发牌,牌没有排序 * * 分析: * 1,买一副扑克,其实就是自己创建一个集合对象,将扑克牌存储进去 * 2,洗牌 * 3,发牌 * 4,看牌 */ public static void main(String[] args) { //1

ArrayList模拟斗地主的洗牌,发牌和看牌

/* * ArrayList模拟斗地主的洗牌,发牌和看牌 * * 分析: * 1.创建一个牌盒 * 2.装牌 * 3.洗牌 * 4.发牌 * 5.看牌 */ 代码: import java.util.ArrayList; import java.util.Collections; /** * ArrayList模拟斗地主的洗牌,发牌和看牌 * @author TP * */ public class Doudizhu { /* * ArrayList模拟斗地主的洗牌,发牌和看牌 * * 分析: