一种较优的洗牌算法

本人已经多次接触到关于洗牌算法的讨论,在之前面试时也有人曾问我过,据我所看,大多的实现都不太理想,今晚来了兴致也就捣鼓了一下,放上来以供大家交流交流,从代码中可以看出来,本屌丝的洗牌算法还是挺优的,并且做到了52张牌,张张都是随机抽取到的,如果还有什么不足之处还请大神指点指点

main.cpp

#include "Cards.h"

#include <iostream>
#include <time.h>

int main(int argc, const char * argv[])
{
    // 以系统时间为随机种子
    srand((unsigned)time(0));
    Cards cards;
    cards.init();
    cards.shuffle();
    for (int i=0; i<5; ++i)
    {
        Cards::Card* card = cards.getCardByIndex(i);
        printf("%3d", card->_index);
    }
    return 0;
}

Cards.h

#ifndef __Shuffle__Cards__
#define __Shuffle__Cards__

#include <iostream>

class Cards
{
public:
    // 单张纸牌,这里是为了面向对象,里面的成员你也根据自己的需求来修改
    struct Card
    {
    public:
        Card(){cleanUp();}
        virtual ~Card(){}
        
        // 索引号
        char _index;
        
        void cleanUp()
        {
            _index = 0;
        }
    };
    
    Cards();
    virtual ~Cards();
    
    // 初始化
    void init();
    // 洗牌
    void shuffle();
    // 取牌
    Card* getCardByIndex(int index);
    // 展示纸牌的索引
    void showIndexes();
private:
    // 牌的总数量
    int _count;
    // 纸牌的索引,因为纸牌最多为52张,所以这里用char就足够了
    char* _card_indexes;
    // 所有纸牌
    Card* _cards;
};
#endif /* defined(__Shuffle__Cards__) */


Cards.cpp

#include "Cards.h"

Cards::Cards()
{
    // 纸牌总数为52张
    _count = 52;
    // 申请内存加初始化
    _card_indexes = new char[_count];
    memset(_card_indexes, 0, sizeof(char) * _count);
    _cards = new Card[_count];
    /* _cards不在这初始化是因为每个元素已经在Card的构造函数里初始化了,
     * 这里不能用malloc来申请内存,如果用malloc那就得进行强转,就不会走Card的构造函数和析构函数
     */
}

Cards::~Cards()
{
    // 释放内存
    delete []_card_indexes;
    delete []_cards;
}

void Cards::init()
{
    for (int i=0; i<_count; ++i)
    {
        // 将纸牌的索引号放入_card_indexes数组
        _card_indexes[i] = i;
        // 填上每张纸牌的索引号,
        _cards[i]._index = i;
    }
}

void Cards::shuffle()
{
    // rand_max为随机上限,初始值为纸牌总数量
    int rand_max = _count;
    // 随机抽取,直到次数为纸牌的总数量为止
    for (int i=0; i<_count; ++i)
    {
        // 取到随机数,该随机数为_card_indexes的索引
        int index_of_indexes = rand() % rand_max;
        // 如果抽到的索引号不为随机上限,则进行移动
        if (index_of_indexes != rand_max - 1)
        {
            // 取出即将被覆盖的内存的数据
            char card_index = _card_indexes[index_of_indexes];
            // 拷贝目标
            char* dest = _card_indexes + index_of_indexes;
            // 拷贝源
            char* src = dest + 1;
            // 拷贝的元素个数
            int n = _count - index_of_indexes - i - 1;
            // 拷贝
            memcpy(dest, src, sizeof(char) * n);
            // 将之前取出的数据放入移动了的数据之后
            _card_indexes[rand_max - 1] = card_index;
        }
        // 打印出每抽取一次的结果
        printf("%d--->%d\n", i, index_of_indexes);
        showIndexes();
        // 每抽完一次,随机上限就少一次,
        --rand_max;
    }
}

Cards::Card* Cards::getCardByIndex(int index)
{
    // 先取到纸牌的索引
    int card_index = _card_indexes[index];
    // 再去取纸牌
    return _cards + card_index;
}

void Cards::showIndexes()
{
    for (int i=0; i<_count; ++i)
    {
        int card_index = _card_indexes[i];
        printf("%2d,", card_index);
        // 每打印13个元素换行
        if (!((i + 1) % 13))
        {
            printf("\n");
        }
    }
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值