C++如何避免使用rand() % xxx的方式实现类似抽奖概率类的问题

头文件:

#pragma once
#include <vector>
#include <windows.h>
class Weight
{
	public:
		template< typename T >
		static int GetWeightIndex( const T *pSrcWeight, size_t Size )
		{
			if( 0 == pSrcWeight || 0 == Size )
			{
				return ~0;
			}
			std::vector< T > Temp;
			Temp.assign( pSrcWeight, pSrcWeight + Size );
			return GetWeightIndex( Temp );
		}

		template< typename T >
		static int GetWeightIndex( const std::vector< T > &SrcWeight )
		{
			if( SrcWeight.empty() )
			{
				return ~0;
			}

			struct WeightRegion
			{
				T mLeftWeight;
				T mRightWeight;
			};

			std::vector< WeightRegion > Weights;

			Weights.resize( SrcWeight.size() );

			WeightRegion Temp = {};

			for( size_t i = 0; i < SrcWeight.size(); ++i )
			{
				const T &Source = SrcWeight[ i ];
				WeightRegion &Target = Weights[ i ];

				Temp.mLeftWeight = Temp.mRightWeight + 1;
				Temp.mRightWeight += Source;
				memcpy_s( &Target, sizeof( Target ), &Temp, sizeof( Temp ) );
			}

			const T RandomPos = 1 + rand() % Temp.mRightWeight;

			for( size_t i = 0; i < Weights.size(); ++i )
			{
				const WeightRegion &Source = Weights[ i ];
				if( RandomPos < Source.mLeftWeight || RandomPos > Source.mRightWeight )
				{
					continue;
				}
				return i;
			}
			return ~0;
		}
		template< typename T >
		static int GetWeightIndexTypeX( const T *pSrcData, size_t Size, size_t WeightOffset )
		{
			if( 0 == pSrcData || 0 == Size )
			{
				return ~0;
			}
			std::vector< int > Temp;
			Temp.resize( Size );
			for( size_t i = 0; i < Size; ++i )
			{
				memcpy_s( &Temp[ i ], sizeof( Temp[ i ] ), ( char* )( pSrcData + i ) + WeightOffset, sizeof( int ) );
			}
			return GetWeightIndex( Temp );
		}
};
测试:

#include "Weight.h"
#include <time.h>
int main()
{
    srand( time( NULL ) );

    //比如抽奖系统5个奖品每个奖品的概率权重不同
    //如果用rand() % xxx 的方式太繁琐而且太麻烦和容易出错
    //如果策划经常改对于程序员来说简直是噩梦...
    //我们不如封装一下避免使用rand() % xxx的方式

    struct LotteryAward
    {
        int mLotteryID;    //奖品ID
        int mWeight;    //奖品权重
    };

    const LotteryAward Awards[] = 
    {
        0,10,
        1,22,
        2,33,
        3,65,
        4,41,
    };


    //
    //方式一
    //提取出权重
    std::vector< int > Weights;
    Weights.resize( 5 );
    for( size_t i = 0; i < 5; ++i )
    {
        Weights[ i ] = Awards[ i ].mWeight;
    }

    //抽奖
    printf( "方式一抽奖:\n" );
    for( int i = 0; i < 10; ++i )
    {
        const size_t Index = Weight::GetWeightIndex( Weights );
        printf( "第%d次抽奖 抽中奖品:%d\n", i + 1, Awards[ Index ].mLotteryID );
    }

    //
    //方式二
    //外部可以避免提取出权重,但是需要提供权重在结构中的自身对齐偏移量
    //求权重自身对齐偏移量
    const int WeightOffset = ( int )&( ( LotteryAward* )0 )->mWeight;
    printf( "方式二抽奖:\n" );
    for( int i = 0; i < 10; ++i )
    {
        const size_t Index = Weight::GetWeightIndexTypeX( Awards, 5, WeightOffset );
        printf( "第%d次抽奖 抽中奖品:%d\n", i + 1, Awards[ Index ].mLotteryID );
    }

    //多次抽奖效率较慢,解决办法将循环封装到内部提供
    //类似static bool GetWeightIndex( const T *pSrcWeight, size_t Size, size_t LotteryTimes, std::vector< size_t > &Out );
    //或static bool GetWeightIndexTypeX( const T *pSrcData, size_t Size, size_t WeightOffset, size_t LotteryTimes, std::vector< size_t > &Out );
    //将所有索引以数组的形式输出,在外部维护这个数组用完再调用输出
    return 0;
}

输出:


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值