原理:割绳子算法:每次都取最大值为总绳长的随机值,最后将其排序,计算每两个的差值,总差值即就是总绳子长度,但是割绳子算法算出来的金额可能会出现超出最大金额的值,那么就会需要使用平均算法。
平均算法(自己取的名字):将超过最大限定金额的值全部重新赋值,并将他们舍去的部分,随机分给最小的金额,并保证最小的金额加上一个值不超过最大值即可。
洗牌算法:最终要将排序的金额进行打乱。
开源是程序员的基本美德
int pp = 100;//元换算成分的比例
int total =(int)((decimal)10 * pp);//红包总额
int num = 8;//红包个数
int min = (int)((decimal)0.02 * pp);//红包不少于
int max = (int)((decimal)2 * pp);//最大红包不超过
if (min <= 0 && min >= (total - max) / (num - 1))
{
Console.WriteLine($"红包最小值异常:{total}分{num}个红包,最小值必须大于0,且不能小余{(total - max) / (num - 1)/100f}");
return;
}
if (num * min > total)
{
Console.WriteLine($"红包最小值异常:{total}分{num}个红包,红包最小值不能超过{total / num / 100f}");
return;
}
if (num * max < total)
{
Console.WriteLine($"红包最大值异常:{total}分{num}个红包,红包最大值不能小余{total / num / 100f}");
return;
}
if (max > total - min * (num - 1))
{
Console.WriteLine($"红包最大值异常:{total}分{num}个红包,红包最大值不能超过{(total - min * (num - 1)) / 100f}");
return;
}
float[] ret = new float[num];
ret[0] = 0;
int i = 1;
//下面不考虑随机数的唯一性,给每个人都预留最少的钱
int totalByHundred = total - (num * min);
Random random = new Random();
while (i < num)
{
ret[i] = random.Next(totalByHundred);
i++;
}
//先从大到小排序 将差值计算出来+最先预留的min值
Array.Sort(ret);
i = 0;
while (i < num - 1)
{
ret[i] = (ret[i + 1] - ret[i] + min);//每个人都预留最少的钱
i++;
}
ret[i] = (totalByHundred - ret[i] + min);//最后一个 仍然是最大可分值-自身+min值即可。
//再次排序金额 将超出最大值的金额 全部重新赋值,并将差值累加给tempmax ,然后将tempmax再依次随机分给最小的金额
Array.Sort(ret);
float tempmax = 0;
for (int k = ret.Length - 1; k >= 0; k--)
{
if (ret[k] > max)
{
float t = random.Next(min,max);
tempmax += ret[k] - t;
ret[k] = t;
}
else
{
break;
}
}
//分完为止 可能运气好 没有可分的
while (tempmax > 0)
{
Console.WriteLine("触发平均算法执行");
Array.Sort(ret);
for (int k = 0; k < ret.Length; k++)
{
//一轮分不完 将再次排序再分 tempmax为0即可
if (tempmax > 0)
{
int tm = max - (int)ret[k];
if (tempmax< tm)
{
//此处判断非常重要
ret[k] = ret[k] + tempmax;
tempmax = 0;
}
else
{
float t = random.Next(tm);
ret[k] = ret[k] + t;
tempmax -= t;
}
}
else
{
break;
}
}
}
//最后利用洗牌算法进行打乱数组
int currentIndex;
float tempValue;
for (int j = 0; j < ret.Length; j++)
{
currentIndex = random.Next(0, ret.Length - j);
tempValue = ret[currentIndex];
ret[currentIndex] = ret[ret.Length -1- j];
ret[ret.Length-1 - j] = tempValue;
}
//完美
for (int j = 0; j < ret.Length; j++)
{
Console.WriteLine($"第{j + 1}个红包:金额{(ret[j] / 100f)}元");
}
//最后检查总红包金额以及最大最小红包是否符合要求 肯定符合要求的 只是输出看一下
Console.WriteLine($"红包总金额:{(ret.Sum() / 100f).ToString("F2")}元,红包最大值:{ret.Max() / 100f}元,红包最小值:{ret.Min() / 100f}元");
该算法由C#写成,更多交流请关注原文博客地址: