Analysis of shuffling algorithm

Problem overview

Question: How to implement the shuffling algorithm?
Algorithm 1: According to the general idea, assume N cards, randomly exchange two of them at a time, cycle N times, and finally form a card sequence.
Algorithm 2: Simulate the shuffling strategy in real life, first shuffle the cards in two piles, then cut the cards, repeat this process a certain number of times, and finally form a card sequence.
Algorithm 3: Take a method similar to drawing lots, initialize an ordered deck, randomly draw one card from the deck each time, and do not put the drawn cards back until the deck is empty, and finally form the order in which the cards were drawn. A card sequence.

Algorithm implementation

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define     POKER_NUM       10 // 共有10张牌, 标号为: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

#define     TEST_NUM        1000000  //测试100万次

// 洗牌后结果, 其中g_aShuffleTestResult[i][j]表示标号为i+1的牌出现在数组j位置的次数  
int g_aShuffleTestResult[POKER_NUM][POKER_NUM] = {0};  

void print_shuffle_result()
{
    int i = 0;
    int j = 0;

    for(i = 0; i < POKER_NUM; i++)
    {
        if (0 == i)
            printf("   ");

        printf("%8d", i+1);
    }

    printf("\n");
    printf("    ---------------------------------------------------------------------------------\n");

    for (i = 0; i < POKER_NUM; i++){
        for(j = 0; j< POKER_NUM; j++){
            if (0==j)
                printf("%2d|", i+1);
            printf("%8d", g_aShuffleTestResult[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

// poker初始化  
void InitArray(int array[], int num)
{
    int i = 0;

    for(i = 0; i < num; i++)
        array[i] = i + 1; 
}

void PrintArray(int array[], int num)
{
    int i = 0;
    for(i = 0; i < num; i++)
        printf("%6d", array[i]);
    printf("\n\n");
}

// 一般洗牌算法
void shuffle_array_general(int array[], int num)
{   
    int i = 0;
    int temp = 0;
    int r1 = 0;
    int r2 = 0;

    // 随机交换
    for (i = 0; i < num; i++){
        r1 = rand()%num;
        r2 = rand()%num;
        temp = array[r1];
        array[r1] = array[r2];
        array[r2] = temp;
    }
}

// 模拟现实生活中洗牌
void shuffle_array_manual(int array[], int num)
{
    int i = 0;
    int j = 0;
    int temp = 0;
    int rstartnum = 0;
    int rdiynum = 0;
    int mid = num/2;
    int* pbuf = (int*)malloc(sizeof(int)*num);
    if (NULL == pbuf)
        return;

#define LOOP_NUM        5

    for (int index = 0; index < LOOP_NUM; index++){

        // 两手洗牌
        for (i = 1; i < mid; i+=2){
            temp = array[i];
            array[i] = array[mid+i];
            array[mid+i] = temp;
        }

        //随机切牌
        memset(pbuf, 0, sizeof(pbuf));

        for (j = 0; j < LOOP_NUM; j++){
            rstartnum = rand()%(num-1)+1; // 从开始到切牌位置的牌数
            rdiynum = rand()%(mid)+1; // 切牌数,每次切不超过一半

            if (rstartnum+rdiynum>num)
                rdiynum = num - rstartnum;  // 保证从起始位置的切牌数不越界

            memcpy(pbuf, array, rstartnum*sizeof(int)); // pbuf用于中转从开始到切牌位置的牌
            memcpy(array, array+rstartnum, rdiynum*sizeof(int));
            memcpy(array+rdiynum, pbuf, rstartnum*sizeof(int));
        }
    }

    free(pbuf);
}

// fisher_yates算法
void shuffle_array_fisher_yates(int array[], int num)
{
    int i = num;
    int r = 0;
    int temp = 0;

    if (0==i)
        return;

    while(--i)
    {
        r = rand()%(i+1);
        temp = array[i];
        array[i] = array[r]; // 每次循环随机取出一张牌,放在i位置
        array[r] = temp;
    }
}

int main()
{
    srand(time(NULL));
    clock_t cBegin, cTime = 0;

    int i = 0;
    int j = 0;
    int k = 0;
    int array[POKER_NUM]={0};

    cBegin = clock();
    printf("[shuffle_array_general]\n");
    memset(g_aShuffleTestResult, 0, sizeof(g_aShuffleTestResult));
    for (i=0; i<TEST_NUM; i++){
        InitArray(array, POKER_NUM);
//      PrintArray(array, POKER_NUM);
        shuffle_array_general(array, POKER_NUM);
//      PrintArray(array, POKER_NUM);

        for (j=0; j< POKER_NUM; j++){ // j遍历array数组
            for (k=0; k<POKER_NUM; k++){ // k+1表示牌号
                if (k+1 == array[j]) 
                    g_aShuffleTestResult[k][j]++; // k+1这张牌出现在j位置,计数加1
            }
        }
    }
    cTime = clock() - cBegin;

    print_shuffle_result();
#ifndef  WIN32
    cTime=cTime/1000;
#endif
    printf("Method shuffle_array_general spend %d ms.\n\n", cTime);


    cBegin = clock();
    printf("\n[shuffle_array_manual]\n");
    memset(g_aShuffleTestResult, 0, sizeof(g_aShuffleTestResult));
    for (i=0; i<TEST_NUM; i++){
        InitArray(array, POKER_NUM);
        shuffle_array_manual(array, POKER_NUM);

        for (j=0; j< POKER_NUM; j++){ // j遍历array数组
            for (k=0; k<POKER_NUM; k++){ // k+1表示牌号
                if (k+1 == array[j]) 
                    g_aShuffleTestResult[k][j]++; // k+1这张牌出现在j位置,计数加1
            }
        }
    }
    cTime = clock() - cBegin;

    print_shuffle_result();
#ifndef  WIN32
    cTime=cTime/1000;
#endif
    printf("Method shuffle_array_manual spend %d ms.\n\n", cTime);


    cBegin = clock();
    printf("\n[shuffle_array_fisher_yates]\n");
    memset(g_aShuffleTestResult, 0, sizeof(g_aShuffleTestResult));
    for (i=0; i<TEST_NUM; i++){
        InitArray(array, POKER_NUM);
        shuffle_array_fisher_yates(array, POKER_NUM);

        for (j=0; j< POKER_NUM; j++){ // j遍历array数组
            for (k=0; k<POKER_NUM; k++){ // k+1表示牌号
                if (k+1 == array[j]) 
                    g_aShuffleTestResult[k][j]++; // k+1这张牌出现在j位置,计数加1
            }
        }
    }
    cTime = clock() - cBegin;

    print_shuffle_result();
#ifndef  WIN32
    cTime=cTime/1000;
#endif
    printf("Method shuffle_array_fisher_yates spend %d ms.\n\n", cTime);


    return 0;
}

operation result

CPU:Intel Core i3 2120
RAM:2G

[shuffle_array_general]
          1       2       3       4       5       6       7       8       9      10
    ---------------------------------------------------------------------------------
 1|  196104   89271   89117   89314   89263   89533   89300   89484   89308   89306
 2|   89368  196844   89240   89468   89384   88985   89118   89202   89598   88793
 3|   88773   89346  197025   89132   89304   89831   89176   88605   89425   89383
 4|   89677   89065   89213  196401   89402   89204   89852   89096   88876   89214
 5|   89345   88894   89124   89259  196925   89680   89747   89523   89009   88494
 6|   89801   88930   89200   89194   89346  196636   89079   89286   89277   89251
 7|   89444   89763   88573   89144   88780   89040  196286   89490   89842   89638
 8|   89184   89481   89095   89679   89282   88864   89361  197035   88862   89157
 9|   89030   89152   89606   88720   89490   89133   89326   89297  196337   89909
10|   89274   89254   89807   89689   88824   89094   88755   88982   89466  196855

Method shuffle_array_general spend 454 ms.


[shuffle_array_manual]
          1       2       3       4       5       6       7       8       9      10
    ---------------------------------------------------------------------------------
 1|   99709  100129  100290   99846  100373   99925   99753  100397   99776   99802
 2|   99691  100237  101140   99815   99316  100126   99285   99904  100157  100329
 3|  100154  100540  100028  100117   99751   99816   99811   99809   99767  100207
 4|   99826  100248  100545   99586   99913  100392   99824   99796  100118   99752
 5|  100150   99857   99834   99810  100136  100476  100138  100008   99566  100025
 6|   99789   99942  100072  100074   99780   99416  100043  100178  100353  100353
 7|   99781   99931   98956  100351  100547   99343  100316  100542  100197  100036
 8|   99776   99720   99804   99846  100301  100625  100392   99664   99978   99894
 9|  100830   99764   99649  100320   99793   99821  100387   99524  100000   99912
10|  100294   99632   99682  100235  100090  100060  100051  100178  100088   99690

Method shuffle_array_manual spend 2246 ms.


[shuffle_array_fisher_yates]
          1       2       3       4       5       6       7       8       9      10
    ---------------------------------------------------------------------------------
 1|   99765  100052   99801  100327   99679  100406  100443   99911  100086   99530
 2|  100149  100223  100506   99888  100031   99846  100140   99822   99762   99633
 3|  100471  100452   99336   99855   99912  100071  100125  100494   99280  100004
 4|   99587  100195  100107  100390   99688  100116  100412   99954   99570   99981
 5|  100396   99570   99774   99744  100224   99897   99767   99961  100025  100642
 6|  100329   99813  100053  100299   99850   99814   99360  100094  100179  100209
 7|   99596  100165  100067   99558  100251   99983  100086   99964  100447   99883
 8|   99810   99731   99922  100125  100493   99690  100290   99845  100060  100034
 9|  100357   99470  100172  100062   99815  100148   99867   99875  100279   99955
10|   99540  100329  100262   99752  100057  100029   99510  100080  100312  100129

Method shuffle_array_fisher_yates spend 415 ms.

Press any key to continue

Analysis of Algorithms

From the running results, the random effect of Algorithm 1 is not ideal, and it can even be said to be a wrong algorithm. Algorithm 2 has a good random effect, but the time and space complexity are not good enough. Algorithm 3 has both O(N time and space complexity. ), and the random effect is good.
Remark 1: Algorithm 3 As long as the method of drawing cards is randomly selected with equal probability, the algorithm is theoretically guaranteed in probability, that is, the probability of each card appearing in each position is the same, which will not be described in detail here.
Remark 2: The rand() function is a pseudo-random method and depends on the random number seed.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325990753&siteId=291194637