抢红包的红包生成算法

过年微信红包很火,最近有个项目也要做抢红包,于是写了个红包的生成算法。

红包生成算法的需求

预先生成所有的红包还是一个请求随机生成一个红包

简单来说,就是把一个大整数m分解(直接以“分为单位,如1元即100)分解成n个小整数的过程,小整数的范围是[min, max]。

最简单的思路,先保底,每个小红包保证有min,然后每个请求都随机生成一个0到(max-min)范围的整数,再加上min就是红包的钱数。

这个算法虽然简单,但是有一个弊端:最后生成的红包可能都是min钱数的。也就是说可能最后的红包都是0.01元的。

另一种方式是预先生成所有红包,这样就比较容易控制了。我选择的是预先生成所有的红包。

理想的红包生成算法

理想的红包生成结果是平均值附近的红包比较多,大红包和小红包的数量比较少。

可以想像下,生成红包的数量的分布有点像正态分布

那么如何实现这种平均线附近值比较多的要求呢?

就是要找到一种算法,可以提高平均值附近的概率。那么利用一种”膨胀“再”收缩“的方式来达到这种效果。

先平方,再生成平方范围内的随机数,再开方,那么概率就不再是平均的了。

具体算法:

public class HongBaoAlgorithm {
	static Random random = new Random();
	static {
		random.setSeed(System.currentTimeMillis());
	}

	public static void main(String[] args) {
		long max = 200;
		long min = 1;

		long[] result = HongBaoAlgorithm.generate(100_0000, 10_000, max, min);
		long total = 0;
		for (int i = 0; i < result.length; i++) {
			// System.out.println("result[" + i + "]:" + result[i]);
			// System.out.println(result[i]);
			total += result[i];
		}
		//检查生成的红包的总额是否正确
		System.out.println("total:" + total);

		//统计每个钱数的红包数量,检查是否接近正态分布
		int count[] = new int[(int) max + 1];
		for (int i = 0; i < result.length; i++) {
			count[(int) result[i]] += 1;
		}

		for (int i = 0; i < count.length; i++) {
			System.out.println("" + i + "  " + count[i]);
		}
	}

	/**
	 * 生产min和max之间的随机数,但是概率不是平均的,从min到max方向概率逐渐加大。
	 * 先平方,然后产生一个平方值范围内的随机数,再开方,这样就产生了一种“膨胀”再“收缩”的效果。
	 *
	 * @param min
	 * @param max
	 * @return
	 */
	static long xRandom(long min, long max) {
		return sqrt(nextLong(sqr(max - min)));
	}

	/**
	 *
	 * @param total
	 *            红包总额
	 * @param count
	 *            红包个数
	 * @param max
	 *            每个小红包的最大额
	 * @param min
	 *            每个小红包的最小额
	 * @return 存放生成的每个小红包的值的数组
	 */
	public static long[] generate(long total, int count, long max, long min) {
		long[] result = new long[count];

		long average = total / count;

		long a = average - min;
		long b = max - min;

		//
		//这样的随机数的概率实际改变了,产生大数的可能性要比产生小数的概率要小。
		//这样就实现了大部分红包的值在平均数附近。大红包和小红包比较少。
		long range1 = sqr(average - min);
		long range2 = sqr(max - average);

		for (int i = 0; i < result.length; i++) {
			//因为小红包的数量通常是要比大红包的数量要多的,因为这里的概率要调换过来。
			//当随机数>平均值,则产生小红包
			//当随机数<平均值,则产生大红包
			if (nextLong(min, max) > average) {
				// 在平均线上减钱
//				long temp = min + sqrt(nextLong(range1));
				long temp = min + xRandom(min, average);
				result[i] = temp;
				total -= temp;
			} else {
				// 在平均线上加钱
//				long temp = max - sqrt(nextLong(range2));
				long temp = max - xRandom(average, max);
				result[i] = temp;
				total -= temp;
			}
		}
		// 如果还有余钱,则尝试加到小红包里,如果加不进去,则尝试下一个。
		while (total > 0) {
			for (int i = 0; i < result.length; i++) {
				if (total > 0 && result[i] < max) {
					result[i]++;
					total--;
				}
			}
		}
		// 如果钱是负数了,还得从已生成的小红包中抽取回来
		while (total < 0) {
			for (int i = 0; i < result.length; i++) {
				if (total < 0 && result[i] > min) {
					result[i]--;
					total++;
				}
			}
		}
		return result;
	}

	static long sqrt(long n) {
		// 改进为查表?
		return (long) Math.sqrt(n);
	}

	static long sqr(long n) {
		// 查表快,还是直接算快?
		return n * n;
	}

	static long nextLong(long n) {
		return random.nextInt((int) n);
	}

	static long nextLong(long min, long max) {
		return random.nextInt((int) (max - min + 1)) + min;
	}
}

统计了下生成的结果,还是比较符合要求的。

时间: 2024-09-30 06:36:37

抢红包的红包生成算法的相关文章

Java抢红包的红包生成算法_java

马上过年了.过年微信红包很火,最近有个项目也要做抢红包,于是写了个红包的生成算法. 红包生成算法的需求 预先生成所有的红包还是一个请求随机生成一个红包 简单来说,就是把一个大整数m分解(直接以"分为单位,如1元即100)分解成n个小整数的过程,小整数的范围是[min, max]. 最简单的思路,先保底,每个小红包保证有min,然后每个请求都随机生成一个0到(max-min)范围的整数,再加上min就是红包的钱数. 这个算法虽然简单,但是有一个弊端:最后生成的红包可能都是min钱数的.也就是说可能

微信随机生成红包金额算法php版_php实例

最近在研究发红包的功能,于是写了个红包的生成算法. 红包生成算法的需求预先生成所有的红包还是一个请求随机生成一个红包 简单来说,就是把一个大整数m分解(直接以"分为单位,如1元即100)分解成n个小整数的过程,小整数的范围是[min, max]. 最简单的思路,先保底,每个小红包保证有min,然后每个请求都随机生成一个0到(max-min)范围的整数,再加上min就是红包的钱数. 这个算法虽然简单,但是有一个弊端:最后生成的红包可能都是min钱数的.也就是说可能最后的红包都是0.01元的. 另一

微信随机生成红包金额算法java版_java

最近几年玩得最疯狂的应该是发红包了,尤其是过年的时候特别受欢迎,下面写了红包的随机算法,其实挺简单的,仅是提供一种思路,希望可以给大家一些启发.  public class WxAlgorithm{ /** * @param moneySum 输入总金额 * @param redNum 输入红包数量 */ private static void wxAlgorithm(double moneySum, int redNum) { // 设置最小的金额 double moneyMin = 0.01

微信红包随机生成算法php版_php实例

想了想,自己写写php版的微信红包随机生成算法,能不能实现类似的功能(其实也不敢说是算法).// $bonus_total 红包总金额// $bonus_count 红包个数// $bonus_type 红包类型 1=拼手气红包 0=普通红包 function randBonus($bonus_total=0, $bonus_count=3, $bonus_type=1){ $bonus_items = array(); // 将要瓜分的结果 $bonus_balance = $bonus_to

子集和问题:一种组合生成算法

今天在网上看见这么一道题目:给你m个数,从里面找出和为sum的n个数,问一共能找到多少组这样的 数. 根据我的理解,这是一道组合生成的题目.令m个数组成的集合为M,就是要找到所有元素个数为n且和 为sum的子集. 最笨的方法是生成所有的子集,然后进行验证,这样复杂度为阶乘.显然有一种改进的算法,在笨方 法中,我们连元素个数不为n的子集都生成了,而这显然是不必要的.这种改进想到很容易,但实现起来 有点困难,经过数个小时的艰苦奋战,我终于设计了一种原创性的组合生成算法,虽然它应该早被计算机 科学家想

算法系列(十三) 椭圆的生成算法

椭圆和直线.圆一样,是图形学领域中的一种常见图元,椭圆的生成算法(光栅转换算法)也是图 形学软件中最常见的生成算法之一.在平面解析几何中,椭圆的方程可以描述为(x – x0)2 / a2+ (y – y0)2 / b2 = 1,其中(x0, y0)是圆心坐标,a和b是椭圆的长短轴,特别的,当(x0, y0)就是坐标 中心点时,椭圆方程可以简化为x2 / a2 + y2 / b2 = 1.在计算机图形学中,椭圆图形也存在在点阵 输出设备上显示或输出的问题,因此也需要一套光栅扫描转换算法.为了简化,

算法系列(十一) 圆生成算法

在平面解析几何中,圆的方程可以描述为(x – x0)2 + (y – y0)2 = R2,其中(x0, y0)是圆心坐 标,R是圆的半径,特别的,当(x0, y0)就是坐标中心点时,圆方程可以简化为x2 + y2 = R2.在计算 机图形学中,圆和直线一样,也存在在点阵输出设备上显示或输出的问题,因此也需要一套光栅扫描 转换算法.为了简化,我们先考虑圆心在原点的圆的生成,对于中心不是原点的圆,可以通过坐标的 平移变换获得相应位置的圆. 在进行扫描转换之前,需要了解一个圆的特性,就是圆的八分对 成

算法系列(十) 直线生成算法

在欧氏几何空间中,平面方程就是一个三元一次方程,直线就是两个非平行平面的交线,所以直线 方程就是两个三元一次方程组联立.但是在平面解析几何中,直线的方程就简单的多了.平面几何中 直线方程有多种形式,一般式直线方程可用于描述所有直线: Ax+By+C = 0  (A.B不同 时为0) 当知道直线上一点坐标(X0,Y0)和直线的斜率K存在时,可以用点斜式方程: Y-Y0 = K(X – X0) (当K不存在时,直线方程简化成X = X0) 当知道直线上的两个点 (X0,Y0)和(X1,Y1)时,还可

全排列生成算法 .

参考链接: 全排列生成算法(一) 原文讲的很详细了,为了完整性,这里粘贴的参考链接中大部分文字,并且在原文的基础上,添加了"给定某个排列,求其字典序中上一个元素"的算法. 字典序 全排列生成算法的一个重要思路,就是将集合A中的元素的排列,与某种顺序建立一一映射的关系,按照这种顺序,将集合的所有排列全部输出.这种顺序需要保证,既可以输出全部的排列,又不能重复输出某种排列,或者循环输出一部分排列.字典序就是用此种思想输出全排列的一种方式.这里以A{1,2,3,4}来说明用字典序输出全排列的