使用R语言模拟抢红包
有一次参加了一个特别无聊的讲座,实在是无事可做,就琢磨了一下像微信抢红包那样的机制是如何实现的。自己当时想了一个模拟的方式,出来的结果似乎也可以以假乱真。后来把相关的代码完善了下,用来在自己组织的R语言课上讲for
循环和自编函数。现在把这些内容整理出来,权当作一篇小小的教程。
首先假设,有人发了一个200块钱的红包,分给10个人抢:
money <- 200
people <- 10
给每个人安排一个随机数:
set.seed(181209)
rand_number <- sample(1:10000, people, replace = TRUE)
rand_number
## [1] 4188 591 2386 4520 3692 979 8170 3728 7121 4408
随后用每个随机数除以所有随机数的总和得到一个比值,乘以总钱数,进而得到每个人的钱数:
rand_money <- rand_number/sum(rand_number)*money
rand_money
## [1] 21.054219 2.971118 11.995073 22.723274 18.560692 4.921700 41.072820
## [8] 18.741674 35.799211 22.160219
然后就可以知道具体每个人得到多少钱了:
paste0(paste0(sample(letters, 5, replace = TRUE), collapse = ''),
'得到了', round(rand_money[1], 2), '元,红包剩余',
round(money - sum(rand_money[1:1]), 2), '元。')
## [1] "hdprm得到了21.05元,红包剩余178.95元。"
paste0(paste0(sample(letters, 5, replace = TRUE), collapse = ''),
'得到了', round(rand_money[2], 2), '元,红包剩余',
round(money - sum(rand_money[1:2]), 2), '元。')
## [1] "ptdpj得到了2.97元,红包剩余175.97元。"
paste0(paste0(sample(letters, 5, replace = TRUE), collapse = ''),
'得到了', round(rand_money[3], 2), '元,红包剩余',
round(money - sum(rand_money[1:3]), 2), '元。')
## [1] "pvkfv得到了12元,红包剩余163.98元。"
但是一个人一行代码实在是太麻烦了,这时候就需要for
循环,把相似的代码重复固定的次数,如:
for (number in seq(people)) {
message <- paste0('我是', number, '号!')
print(message)
}
## [1] "我是1号!"
## [1] "我是2号!"
## [1] "我是3号!"
## [1] "我是4号!"
## [1] "我是5号!"
## [1] "我是6号!"
## [1] "我是7号!"
## [1] "我是8号!"
## [1] "我是9号!"
## [1] "我是10号!"
具体到这个例子,就是这样:
for (number in seq(people)) {
message <- paste0(paste0(sample(letters, 5, replace = TRUE), collapse = ''),
'得到了', round(rand_money[number], 2), '元,红包剩余',
round(money - sum(rand_money[1:number]), 2), '元。')
print(message)
}
## [1] "wqrwz得到了21.05元,红包剩余178.95元。"
## [1] "qmqup得到了2.97元,红包剩余175.97元。"
## [1] "haeoi得到了12元,红包剩余163.98元。"
## [1] "xshvs得到了22.72元,红包剩余141.26元。"
## [1] "rjlow得到了18.56元,红包剩余122.7元。"
## [1] "wwhev得到了4.92元,红包剩余117.77元。"
## [1] "jxsbo得到了41.07元,红包剩余76.7元。"
## [1] "qmjel得到了18.74元,红包剩余57.96元。"
## [1] "bqpcw得到了35.8元,红包剩余22.16元。"
## [1] "uefup得到了22.16元,红包剩余0元。"
以上的代码可以写进一个函数中。因为每次的红包不会都是200的,也不是每次都会有10个人抢,这两个值可以作为函数的参数,需要的时候可以设定新的数值:
hongbao <- function(money = 200, people = 10) {
set.seed(181209)
rand_number <- sample(1:10000, people)
rand_money <- rand_number/sum(rand_number)*money
message <- paste0(paste0(sample(letters, 5, replace = TRUE), collapse = ''),
'得到了', round(rand_money[sample(1:people,1)], 2), '元,红包剩余',
round(money - sum(rand_money[1:sample(1:people,1)]), 2), '元。')
return(message)
}
因此设置了参数的默认值,所以直接输入函数名就可以看到默认的结果:
hongbao()
## [1] "hdprm得到了4.92元,红包剩余57.94元。"
也可以重新设定新的钱数和人数:
hongbao(money = 100, people = 20)
## [1] "pvkfv得到了1.77元,红包剩余42.08元。"
这时候只会显示一条抢红包的信息,如果想要知道红包已经被多少人抢过了,以及手气最佳的信息,还需要加一些东西:
hongbao <- function(money = 200, people = 10) {
rand_number <- sample(1:10000, people)
you <- sample(1:people, 1)
rand_money <- rand_number/sum(rand_number)*money
message <- paste0('你得到了', round(rand_money[you], 2), '元,红包剩余',
round(money - sum(rand_money[1:you]), 2), '元。')
print(message)
message_number <- paste0('红包已抢', you, '/', people, '个。')
print(message_number)
for (number in seq(you)) {
if (max(rand_money[1:you]) - rand_money[number] < .00001) {
if (number < you) {
name <- paste0(sample(letters, 5, replace = TRUE), collapse = '')
message_overall <- paste0(name, '得到了', round(rand_money[number], 2), '元,手气最佳。')
}
else {
message_overall <- paste0('你', '得到了', round(rand_money[number], 2), '元,手气最佳。')
}
}
else {
if (number < you) {
name <- paste0(sample(letters, 5, replace = TRUE), collapse = '')
message_overall <- paste0(name, '得到了', round(rand_money[number], 2), '元。')
}
else {
message_overall <- paste0('你', '得到了', round(rand_money[number], 2), '元。')
}
}
print(message_overall)
}
}
然后再看一下:
hongbao()
## [1] "你得到了29.37元,红包剩余147.91元。"
## [1] "红包已抢2/10个。"
## [1] "oixsh得到了22.72元。"
## [1] "你得到了29.37元,手气最佳。"
hongbao(20, 20)
## [1] "你得到了0.6元,红包剩余15.54元。"
## [1] "红包已抢4/20个。"
## [1] "lbqpc得到了1.44元,手气最佳。"
## [1] "wuefu得到了1.24元。"
## [1] "ptyop得到了1.18元。"
## [1] "你得到了0.6元。"
最后的代码有点乱,但没时间整理了,就这样吧。