带权重的随机算法

Posted _Unique_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带权重的随机算法相关的知识,希望对你有一定的参考价值。

在软件开发过程中,尤其是APP或者或者游戏中,经常会涉及到这样一种问题:为了刺激用户消费,需要设置一种抽奖功能,用户点击抽奖按钮,会随机获取其中一种奖励,但是问题来了,不能让一等奖太容易抽到,那样的话,岂不是亏大发了,所以,一般来说,会有这样的需求,越大的奖项,抽中的概率越小,越小的奖项,抽中的概率越大,因此,要对不同的奖项设置权重,例如,3等奖抽中的概率是70%,2等奖是20%,1等奖是10%,这样,大部分人都只能中3等奖,小部分人是二等奖,而只有特别少的人才可能拿到一等奖。

但是,对于这样的问题,怎样才能实现这样的随机效果呢?直接使用random函数,是不可能做到的。其实相信好多人都已经有实现的思路了,就是产生0-100之间的随机数,当随机数在0-70时,就获得3等奖,70-90是2等奖,90-100是一等奖。一般情况下,这种随机概率,是通过读取表格或文件来获取的,然后保存在数组之类的容器中,这里只使用数组来模拟这些数据。下面,我们就来用C++实现这个功能:

由于在C/C++中没有random函数,这里先自己定义一个。这里使用C语言自带的rand函数,但直接使用rand函数会产生一个问题,就是每次产生的随机数都是同一个随机数,所以,这里使用时间来产生随机种子,这样,产生的数就是随机的了。根据时间产生随机种子要用到time.h,使用rand函数要用到stdlib.h,这里引入两个头文件和基本的头文件:

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

创建自己的random函数:

int random(int x)
	srand((int)time(0));
	return rand()%x;


然后在写主函数:


int main()
	//设置各种奖励的权重,这里不一定总和是100,多少都可以
	int a[5] = 5,10,15,25,45;
	int length = sizeof(a)/sizeof(int);
	int sum = 0;
	for (int i = 0; i < length; i++)
	
		sum += a[i];
	
	int randVal = random(sum);
	printf("%d\\n",randVal);
	int grade = 0;
	for (int i = 0;i < length; i++)
	
		if (randVal <= a[i])
		
			grade = i+1;
			break;
		
		randVal -= a[i];
	
	printf("%d等奖\\n",grade);
	system("pause");
	return 0;

首先设置各个奖励的权重,这里的权重之和不必非要是100,我的这段代码设置的权重之和位100,是为了方便读者了解到各个奖励抽中的概率,所以这样设置的,理论上说,数值大一点,产生的随机数更大,也更加随机。这里只将权重放大数组中,一般情况下,需要策划提供相关的配表,然后从配表中读取数值,这样,在应用中,可以通过跟新配表,来事实更新权重信息。由于通过配表设置了权重,所以,奖励种类不确定,因此,通过sizeof函数,获取数所占空间,然后,除以int所占内存空间,获得数组长度。然后,求权重总和,再调用自己创建的随机函数,产生随机数。下面就是判断这个随机数在那种奖励的权重范围了。将获取的随机送randVal和权重数组中的数进行比较,首先和a[0]作比较,加入获取的随机数是41,a[0]=5,41>a[0],那么,将41 - 5 等于36,继续和第二个数a[1]也就是10进行比较,36比10大,就减去10,变成26,然后继续和a[2]也就是15比较,还是大,就减去15变为16,然后和a[3] 也就是25比较,这时,比25小,也就是说,此时随机到了这个奖励,由于等级是从1开始,数组从0开始,所以,将i+1的值赋值给grade作为奖励等级,然后break,退出循环,输出奖励类型。这样就实现了一个简单的带权重的随机函数。也就是上面代码的整体逻辑,后面的system("pause");函数时为了让输出的控制台不要迅速关闭,才加上的。

下面,要加深程序难度了。

由于笔者是游戏开发者,这里就以游戏来说事儿吧(貌似只有游戏会有这么多的要求)。在游戏中通常会有VIP机制,也就是说,玩家需要充钱,来提升自己的VIP等级,然后,在抽奖过程中,部分奖励的概率也会相应的大一些,VIP等级越高,抽取高级奖励的概率会越大。那么,我们只需要将上面的数组a变成二位数组,来模拟这种情况即可。相比你已经想到了该怎么做,现在上代码:


int main()
	int a[4][5] = 1,5,10,35,49,
		<span style="white-space:pre">	</span>2,6,12,32,48,
			3,8,15,29,45,
			5,10,15,30,40;
	int length = sizeof(a[0])/sizeof(int);
	//设置VIP等级
	int vip = 3;
	int sum = 0;
	for (int i = 0; i < length; i++)
	
		sum += a[vip][i];
	
	int randVal = random(sum);
	printf("%d\\n",randVal);
	int grade = 0;
	for (int i = 0;i < length; i++)
	
		if (randVal <= a[vip][i])
		
			grade = i+1;
			break;
		
		randVal -= a[vip][i];
	
	printf("%d等奖\\n",grade);
	system("pause");
	return 0;

首先将各个VIP等级的数值获取出来,放在数组中,一般也可以在想要抽奖的时候,直接去表中读取也可以,只读取对应VIP的那一行数据,然后,放在一个一维数组中。这里的二维数组,其实就是一维数组的数组,相信大家都看得懂,通过获取a[0]的长度,来判断奖励有几种。然后,套用之前的方法,根据VIP等级和产生的随机数,来产生奖励,相信大家也能看出来,VIP等级越高,高级奖励的概率越大( 从我的表中可能看的不明显,因为我不懂数值策划这东西,就当这样吧),所以,有的游戏中会说什么VIP一等奖奖励翻倍,是不是觉得自己被坑了,其实就是这样,要是概率变得那么多,那游戏运营商或者商场的商家岂不是要赔死了,说了这些东西,会不会被打啊,哈哈不扯淡了,还有更多内容。

这还只是简单的问题,还有更难的。大家都知道,随机函数会产生一个范围的数,例如,0-100,也就是说会产生边界值0和100,不仅如此,而且,概率也不小。0-100总共101个数,这两个数的概率都是1/101,举个例子,当VIP0的时候,如果权重是这样的:0,5,10,35,50,也就是说,在设计的时候,不是VIP的话,抽不到1等奖,但是从上面的方法来说,如果随机的数randVal的值为0,randVal<=a[0][0],也就是0,等于条件满足了,也就是抽到了1等奖,但是,按照策划的要求,不应该有0,程序设计的结果和策划要求不匹配,而且,概率还很大,有人会想,如果将权重同时乘以100,产生0-10000的随机数,这样概率就小很多了,但实际上,还是不可行,还是有概率,因此,要想方法将为0的值过滤掉,这样,就不会产生权重为0的奖励了。所以,这里要对0进行判断,当a[vip][i] == 0时,就直接退出当前循环,进入下次的循环,这样就避免了这样的问题。代码上,只需要在上面的for循环中,先判断是否为0即可。代码如下:


	for (int i = 0;i < length; i++)
	
		if (a[vip][i] == 0)
		
			continue;
		
		if (randVal <= a[vip][i])
		
			grade = i+1;
			break;
		
		randVal -= a[vip][i];
	

这样基本上就完成了一个带权重的随机数算法。其实这个功能也不是很难,只不过是可能存在一些特殊情况,这里将其进行了部分考虑,如果有表达不严谨的地方,还请技术大牛们指正。

以上是关于带权重的随机算法的主要内容,如果未能解决你的问题,请参考以下文章

源码走读:Dubbo带权重的随机负载均衡算法与warmup

源码走读:Dubbo带权重的随机负载均衡算法与warmup

源码走读:Dubbo带权重的随机负载均衡算法与warmup

源码走读:Dubbo带权重的随机负载均衡算法与warmup

源码走读:Dubbo带权重的随机负载均衡算法与warmup

算法珠玑单源最短路径问题