问题描述:
设计抢红包算法,使得分配钱的概率随机,互不影响。
红包金额为100,红包数为10个,最小金额1元,最大50元,不存在小数。
设未抢红包的人数为n,剩余金额为m,当有人抢红包时,随机从 [1,m - (n - 1)*1]中随机一个金额,作为用户抢到的红包金额。依次设置m与n的值,当n=0,红包会被抢空。
设计缺陷:这种随机钱的方式,不能做到金额互不影响,越先抢的用户越有概率抢到大红包,因为前面的用户的最大值大,随机范围也大。
事先将红包等概率划分成n份,当有人抢红包时,只需从队列中一份金额的红包即可。那么如何做到等概率划分成n份呢。
先定义一个权重最大值 WEIGHT_BASE=10000,从[1,WEIGHT_BASE]中随机一个值wieght1,依次循环,可以得到weight1,weight2…weight10,将所有的权重相加,会得到一个总权重allweight,那么 weight1/allweight * 100就是第一份红包的金额,以此类推,可以得到n份红包的金额。
如下代码仅供参考
```local table_insert = table.insert
local math_random = math.random
local math_ceil = math.ceil
local math_floor = math.floor
local WEIGHT_BASE = 10000
--随机权重
function RandomWeight(num)
local weightList = {}
local allWeight = 0
local weight
for i = 1, num do
weight = math_random(1, WEIGHT_BASE)
table_insert(weightList, weight)
allWeight = allWeight + weight
end
return allWeight,weightList
end
--将红包按照权重划分
function Divide(total, allWeight, weightList, minPacket, maxPacket)
local packetList = {}
local tmpTotal = 0
for i = 1, #weightList do
local packetValue = math_floor(total * (weightList[i] / allWeight))
packetList[i] = packetValue
if packetValue > maxPacket then
packetList[i] = maxPacket
elseif packetValue < minPacket then
packetList[i] = minPacket
end
tmpTotal = tmpTotal + packetList[i]
end
local diff = tmpTotal - total
-- print("before diff:", diff)
if tmpTotal ~= total then
for i = 1, #packetList do
if diff == 0 then
break
end
local value = packetList[i]
if diff > 0 then
if value - diff > minPacket then
packetList[i] = value - diff
else
packetList[i] = minPacket
end
diff = diff - (value - packetList[i])
else
if value - diff > maxPacket then
packetList[i] = maxPacket
else
packetList[i] = value - diff
end
diff = diff + (packetList[i] - value)
end
end
end
return packetList
end
--total 红包总额
--num 红包个数
-- minPacket 最小红包
-- maxPacket 最大红包
function RobRedPacket(total, num, minPacket, maxPacket)
if total <= 0 or num <= 0 or minPacket <= 0 or maxPacket <= 0 then
print("参数错误")
assert(false)
-- return
end
if num * minPacket > total then
print("红包总额不符")
assert(false)
end
if maxPacket < minPacket then
print("最小包金额超过最大包金额")
assert(false)
end
if maxPacket > total then
print("最大包超出金额上限")
assert(false)
end
local allWeight,weightList = RandomWeight(num)
-- print("权重如下:")
-- print(table.concat(weightList, " "))
local packetList = Divide(total, allWeight, weightList, minPacket, maxPacket)
print(string.format("红包总量:%d,红包数量:%d,最小红包:%d,最大红包:%d", total, num, minPacket, maxPacket))
print(table.concat(packetList," "))
Check(packetList, total, num, minPacket, maxPacket)
end
function Print(list, err)
print(table.concat(list," "))
if err then
print(err)
assert(false)
end
end
function Check(packetList, total, num, minPacket, maxPacket)
if num ~= #packetList then
Print(packetList, "红包数量不对")
end
local tmpTotal = 0
for i = 1, num do
local value = packetList[i]
tmpTotal = tmpTotal + value
if value > maxPacket or value < minPacket then
Print(packetList, "红包大小不符")
end
end
if tmpTotal ~= total then
Print(packetList, "红包总额不符:"..tmpTotal.." ".. total)
end
end
function Main()
RobRedPacket(200, 10, 5, 100)
end
function Unittest()
for i = 1, 10000 do
local num = math_random(1,20)
local total = math.random(num, num * 100)
local minPacket = 1
local maxPacket = total - (num - 1) * minPacket
RobRedPacket(total, num, minPacket, maxPacket)
end
print("success")
end
-- Main()
Unittest()