using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace testDemo1
{
/// <summary>
/// 红包算法,波动比例0-1之间
/// </summary>
class Program
{
static void Main(string[] args) {
Program p = new Program();
//金额分配的时候一定注意总金额与分发数量以及最大金额的合理规划
List<decimal> lists = p.createBonusList(500,500,decimal.Parse("0.01"),10,decimal.Parse("0.5"));
decimal Count = 0;
foreach (var item in lists)
{
Count += item;
Console.WriteLine(item);
}
Console.WriteLine("总:"+Count);
}
Random ra = new Random();
/// <summary>
/// 生成红包一次分配结果·主要方法入口
/// </summary>
/// <param name="totalBonus">totalBonus</param>
/// <param name="totalNum">totalNum</param>
/// <param name="rdMin"></param>
/// <param name="rdMax"></param>
/// <param name="bigRate">指定大范围区间的概率</param>
/// <returns></returns>
public List<decimal> createBonusList(decimal totalBonus, decimal totalNum, decimal rdMin, decimal rdMax, decimal bigRate)
{
decimal sendedBonus = 0;
decimal sendedNum = 0;
List<decimal> bonusList = new List<decimal>();
while (sendedNum < totalNum)
{
decimal bonus = randomBonusWithSpecifyBound(totalBonus, totalNum, sendedBonus, sendedNum, rdMin, rdMax, bigRate);
bonus = Convert.ToDecimal(bonus.ToString("#0.00"));
bonusList.Add(bonus);
sendedNum++;
sendedBonus += bonus;
}
return bonusList;
}
/// <summary>
/// 随机分配第n个红包
/// </summary>
/// <param name="totalBonus"></param>
/// <param name="totalNum"></param>
/// <param name="sendedBonus"></param>
/// <param name="sendedNum"></param>
/// <param name="rdMin"></param>
/// <param name="rdMax"></param>
/// <param name="bigRate">平衡度0-1</param>
/// <returns></returns>
private decimal randomBonusWithSpecifyBound(decimal totalBonus, decimal totalNum, decimal sendedBonus,
decimal sendedNum, decimal rdMin, decimal rdMax, decimal bigRate)
{
decimal avg = totalBonus / totalNum; // 平均值
decimal leftLen = avg - rdMin;
decimal rightLen = rdMax - avg;
decimal boundMin = 0, boundMax = 0;
// 大范围设置小概率
if (leftLen.Equals(rightLen))
{
boundMin = Math.Max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), rdMin);
boundMax = Math.Min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), rdMax);
}
else if (rightLen.CompareTo(leftLen) > 0)
{
// 上限偏离
decimal standardRdMax = avg + leftLen; // 右侧对称上限点
decimal _rdMax = canReward(bigRate) ? rdMax : standardRdMax;
boundMin = Math.Max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMax), rdMin);
boundMax = Math.Min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), _rdMax);
}
else
{
// 下限偏离
decimal standardRdMin = avg - rightLen; // 左侧对称下限点
decimal _rdMin = canReward(bigRate) ? rdMin : standardRdMin;
boundMin = Math.Max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), _rdMin);
boundMax = Math.Min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMin), rdMax);
}
// 已发平均值偏移修正-动态比例
if (boundMin.Equals(boundMax))
{
return getRandomVal(boundMin, boundMax);
}
decimal currAvg = sendedNum == 0 ? avg : (sendedBonus / sendedNum); // 当前已发平均值
decimal middle = (boundMin + boundMax) / Convert.ToDecimal(2.0);
decimal subMin = boundMin, subMax = boundMax;
// 期望值
decimal exp = avg - (currAvg - avg) * sendedNum / (totalNum - sendedNum);
if (middle > exp)
{
subMax = Math.Round((boundMin + exp) / Convert.ToDecimal(2.0));
}
else
{
subMin = Math.Round((exp + boundMax) / Convert.ToDecimal(2.0));
}
decimal expBound = (boundMin + boundMax) / 2;
decimal expSub = (subMin + subMax) / 2;
decimal subRate = (exp - expBound) / (expSub - expBound);
return getRandomValWithSpecifySubRate(boundMin, boundMax, subMin, subMax, subRate);
}
/// <summary>
/// 下限随机
/// </summary>
/// <param name="rate"></param>
/// <returns></returns>
private bool canReward(decimal rate)
{
return Convert.ToDecimal(ra.NextDouble().ToString()) <= rate;
}
/**
* 返回min~max区间内随机数,含min和max
* @param min
* @param max
* @return
*/
private decimal getRandomVal(decimal min, decimal max)
{
decimal v = Convert.ToDecimal(ra.NextDouble()) * max;
v = Convert.ToDecimal(v.ToString("#0.00"));
return v >= min ? v : min;
}
/// <summary>
/// 带概率偏向的随机算法,概率偏向subMin~subMax区间返回boundMin ~boundMax区间内随机数(含boundMin和boundMax),同时可以指定子区间subMin ~subMax的优先概率例:传入参数(10, 50, 20, 30, 0.8),则随机结果有80%概率从20 ~30中随机返回,有20%概率从10 ~50中随机返回
/// </summary>
/// <param name="boundMin"></param>
/// <param name="boundMax"></param>
/// <param name="subMin"></param>
/// <param name="subMax"></param>
/// <param name="subRate"></param>
/// <returns></returns>
private decimal getRandomValWithSpecifySubRate(decimal boundMin, decimal boundMax, decimal subMin, decimal subMax, decimal subRate)
{
if (canReward(subRate))
{
return getRandomVal(subMin, subMax);
}
return getRandomVal(boundMin, boundMax);
}
}
}