今天试着写了一个微信红包的算法。先是自己写了一个算法,但是效果不好。然后在网上参考了一位大神写的方法。
红包的要求就:设置m个,发n分(这里就以分为单位了),保证每个红包的金额要大于1分。
首先我自己的思路是:
1、判断n是否会大于等于m,如果n<m,那么必定会用红包不足1分钱,所以应该是不合法的(1分钱都没还玩啥)。
2、如果n==m,那么每个就是1分钱。如果n>m那么每个红包的金额范围应该是1~n-m+1之间,例如1块钱,发10个的话,合法的金额应该是1~91。
3、根据红包的个数每次在合法的范围内产生一个随机数,就像上面的例子,一块钱发10个,那么第一个就是在0~91的范围内产生一个随机数,第二次的时候在剩余的金额(总金额-已产生的合法金额)中生成一个合法的随机数。直到最后一个。
代码如下:
/**
* 生成随机红包
* @param count 红包个数
* @param fen 红包金额(分)
*/
private void hongbao1(int count, int fen) {
int max;
Random random = new Random();
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < count - 1; i++) {
max = fen - (count - i - 1);
int result = random.nextInt(max) + 1;
buffer.append(" " + result);
fen -= result;
}
buffer.append(" " + fen);
tvShow.setText(buffer.toString());
}
刚开始觉得这样就行了。但是运行程序的结果发现每次都是前面产生的随机数要大很多,到后面的随机数基本就是1了。
仔细想了一下,这样是不妥的。第一次生成的随机数的合理范围会是最大的,越到后面就越小,所以导致红包越到后面就越小了。
之后我在网上看到了一位大神的做法,http://blog.youkuaiyun.com/hengyunabc/article/details/19177877/
下面是大神的代码,我在xRandom里面稍微做了一点修改。大神的思路就不细说了。
public class HongBaoAlgorithm {
static Random random = new Random();
static {
random.setSeed(System.currentTimeMillis());
}
/**
* 生产min和max之间的随机数,但是概率不是平均的,从min到max方向概率逐渐加大。
* 先平方,然后产生一个平方值范围内的随机数,再开方,这样就产生了一种“膨胀”再“收缩”的效果。
*
* @param min
* @param max
* @return
*/
static long xRandom(long min, long max) {
LogUtil.e("xRandom---->min:" + min + " max:" + max);
return sqrt(nextLong(sqr(max - min) + 1));
}
/**
* @param total 红包总额
* @param count 红包个数
* @param max 每个小红包的最大额
* @param min 每个小红包的最小额
* @return 存放生成的每个小红包的值的数组
*/
public static long[] generate(long total, int count, long max, long min) {
LogUtil.e("generate-->total:" + total + " count:" + count + " max" + max + " min:" + min);
long[] result = new long[count];
long average = total / count;
LogUtil.e("average:" + average);
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;
}
/**
* 最小值默认是1的分法
*
* @param total 红包总额
* @param count 红包个数
* @return
*/
public static long[] generate(long total, int count) {
return generate(total, count, total - count + 1, 1);
}
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;
}
}