Last active
March 16, 2018 09:20
-
-
Save acalism/af731b5863754aab1216b9cfd6107024 to your computer and use it in GitHub Desktop.
带最小金额的随机红包分配方法
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 随机红包(仅带最小金额) | |
/// | |
/// - Parameters: | |
/// - amount: 总额(分) | |
/// - slice: 分成几份,至少一份 | |
/// - minimun: 每个红包的最小金额(分),默认1分(这和微信目前业务场景是一致的) | |
func redPacket(ammount: Int, slice: Int = 1, minimum: Int = 1) throws -> [Int] { | |
guard minimum > 0, slice > 0, ammount >= minimum * slice else { | |
throw NSError(domain: "com.tencent.wechat.redpacket", code: -1, userInfo: [NSLocalizedDescriptionKey: "无法按要求分配红包"]) | |
return [ammount] | |
} | |
if slice == 1 { | |
return [ammount] | |
} | |
// 插板法:总共 ammount - minimum * slice + 1个空,需插入 slice - 1 块板 | |
// 每个空均可重插,空与空之间可以是0间距 | |
let numberOfSlot = ammount - minimum * slice + 1 | |
// arc4random_uniform 的结果是 less than 给定的参数 | |
let arr = Array(0..<(slice-1)).map({ _ in Int(arc4random_uniform(UInt32(numberOfSlot))) }).sorted() | |
var result: [Int] = [arr[0] + minimum] | |
for i in 1..<arr.count { | |
result.append(arr[i] - arr[i - 1] + minimum) | |
} | |
result.append(ammount - result.reduce(0, +)) | |
return result | |
} | |
// 测试代码: | |
print("开始发红包:") | |
for _ in 0..<100 { // 执行100次 | |
do { | |
// 这儿的值可随意修改。比如,现在可解读为10块钱,9个红包,每个红包不少于1块钱。 | |
let t = 1000, s = 9, m = 100 | |
let result = try redPacket(ammount: t, slice: s, minimum: m) | |
let total = result.reduce(0, +) | |
print(result, total == t) | |
} catch { | |
print(error) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 带上下限的红包分配方案 | |
// 一次分发 | |
func deliverRedPacket(total: Int, min: Int, max: Int, num: Int) throws -> Int { | |
guard total > 0, min > 0, max >= min, total >= min * num, total <= max * num else { | |
throw NSError.init(domain: "redpacket", code: -1, userInfo: nil) | |
} | |
guard num > 1 else { | |
return total | |
} | |
let leastLastRedPacket = total - (num - 1) * max | |
let maxLastRedPacket = total - (num - 1) * min | |
let low = leastLastRedPacket < min ? min: leastLastRedPacket | |
var high = maxLastRedPacket > max ? max : maxLastRedPacket | |
let ave = total / num > 1 ? total / num : 1 | |
if high > 2 * ave { | |
print(high, ave) | |
// high = 2 * ave // 不允许单个红包大于剩余红包平均值的2倍 | |
} | |
return low + Int(arc4random_uniform(UInt32(high - low + 1))) | |
} | |
// 整个分发过程 | |
func redPacket(total: Int, min: Int, max: Int, num: Int) throws -> [Int] { | |
var t = total | |
var result = [Int]() | |
for i in 0..<num { | |
let packet = try deliverRedPacket(total: t, min: min, max: max, num: num - i) | |
result.append(packet) | |
t -= packet | |
} | |
return result | |
} | |
// 测试代码 | |
let t = 200000 | |
let min = 1000 | |
let max = 20000 | |
let num = 50 | |
for _ in 0..<30 { | |
do { | |
print(try redPacket(total: t, min: min, max: max, num: num)) | |
} catch { | |
print(error) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
目前,微信随机红包不支持设定最小金额(实为1分),我给出一种带最小金额的随机红包分配方法,且分配结果公平公正。 本质是排列组合里的插板法。 仅需19行代码,算法复杂度O(nn),n为红包个数(sorted和reduce是nn的复杂度)。