有一次参加了一个特别无聊的讲座,实在是无事可做,就琢磨了一下像微信抢红包那样的机制是如何实现的。自己当时想了一个模拟的方式,出来的结果似乎也可以以假乱真。后来把相关的代码完善了下,用来在自己组织的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元。"

最后的代码有点乱,但没时间整理了,就这样吧。