LoginSignup
3
0

More than 3 years have passed since last update.

Go言語製 抽選Botの精度が怪しいので検証してみた話

Last updated at Posted at 2020-02-27

いきさつ

ACALL株式会社では、slackで抽選をするbotがあって、会議のファシリテーターとか
誰か担当を決めるときにそのbotを使って決めています。

Go + AWS LambdaでSlackの社員抽選botを作った話
https://blog.acall.jp/2019/11/develop-slack-lottery-bot/

こんな感じで当選するのですが、
8c967525c648c218ea742cd5bfa515e8.png

このbotの精度がどうも怪しい。
私と弊社CTOがやたら当選する気がする!

d34c8bf56910f5a3db67231b5117a886.png

最初は「まあプログラムのランダム性ってそんなもんだよね。ハハハ」なんて言ってたもんですが、
なかなかの頻度で当選するので、これはもしやバグが寝てるのでは・・・・と思い、精度を検証してみました。

前提

  • goのバージョンは1.14rc1を使用

検証内容

抽出処理の確認

まず、抽選botのランダム処理は下記のようになっています。

func lotteryOneUserFromUsers(userIDs []string) string {
    rand.Seed(time.Now().UnixNano())
    userID := userIDs[rand.Intn(len(userIDs))]
    return userID
}

userIDsには抽選対象のユーザーが入っているわけですが、この処理自体はシンプルで怪しいところはない。
というわけで、この処理を複数回実行して確率を算出します。

確認用コード

下記のようなコードを用意しました。

probability.go
package main

import (
  "math/rand"
  "time"
  "fmt"
  "strconv"
)

func main() {
  userIDs := []string{"CTO","EM","山田","鈴木","藤田","田中","中島","北川","西山","川口","遠藤"}
  var lottery_cnt map[string]int = make(map[string]int)
  const LOOP_CNT int = 1000
  //初期化
  for _, s := range userIDs {
    lottery_cnt[s] = 0
  }

  var userID string
  for i := 0; i < LOOP_CNT; i++ {
    userID = lotteryOneUserFromUsers(userIDs)
    lottery_cnt[userID] += 1
  } 

  fmt.Println("抽選実施回数は" + strconv.Itoa(LOOP_CNT) + "回です")
  fmt.Println("----------------------------")
  sum := 0
  for k, v := range lottery_cnt {
    fmt.Println(k + " : " + strconv.FormatFloat((float64(v)/float64(LOOP_CNT)*100.0), 'f', 1, 64) + "% (" + strconv.Itoa(v) + "回)")
    sum += v
  }
}

// 抽選処理実体
func lotteryOneUserFromUsers(userIDs []string) string {
    rand.Seed(time.Now().UnixNano())
  userID := userIDs[rand.Intn(len(userIDs))]
    return userID
}

LOOP_CNTで抽選する回数を設定しています。

いざ検証

さて、上記のコードを使ってまずは1,000回の抽選で検証してみます。

~/d/s/lottery ❯❯❯ go run probability.go
抽選実施回数は1000回です
----------------------------
山田 : 7.5% (75回)
鈴木 : 10.8% (108回)
西山 : 7.3% (73回)
遠藤 : 8.8% (88回)
中島 : 10.0% (100回)
北川 : 9.9% (99回)
川口 : 9.9% (99回)
CTO : 7.8% (78回)
EM : 10.1% (101回)
藤田 : 9.8% (98回)
田中 : 8.1% (81回)

ほうほう・・・それなりのゆらぎがあります。
ちなみにEMが私です。
理論上は回数を重ねれば重なるほど確率は均一になるはずなので、回数を増やします。
次は1万回でやってみます。

~/d/s/lottery ❯❯❯ go run probability.go
抽選実施回数は10000回です
----------------------------
遠藤 : 8.6% (856回)
EM : 9.4% (936回)
山田 : 9.2% (921回)
鈴木 : 9.1% (914回)
藤田 : 9.1% (908回)
中島 : 9.6% (960回)
川口 : 9.3% (927回)
CTO : 8.9% (889回)
田中 : 9.0% (900回)
北川 : 9.2% (921回)
西山 : 8.7% (868回)

前回よりも均一化されてきました。ほぼ9%前後です。
では一気に、50万件まで増やしてみます。

~/d/s/lottery ❯❯❯ go run probability.go
抽選実施回数は500000回です
----------------------------
北川 : 9.1% (45357回)
西山 : 9.1% (45277回)
川口 : 9.1% (45562回)
CTO : 9.0% (45103回)
山田 : 9.2% (45825回)
藤田 : 9.1% (45574回)
田中 : 9.1% (45283回)
EM : 9.2% (45765回)
鈴木 : 9.1% (45417回)
中島 : 9.1% (45630回)
遠藤 : 9.0% (45207回)

ほぼゆらぎはなくなりましたね!
50万回やれば問題ないと言えそうです。

が!

会社の抽選BOTで50万回もやるわけはない。
実際に弊社Slackの抽選bot利用回数を調べると163回の抽選が行われていました。
というわけで163回で検証してみます。

~/d/s/lottery ❯❯❯ go run probability.go
抽選実施回数は163回です
----------------------------
西山 : 9.2% (15回)
川口 : 12.9% (21回)
藤田 : 7.4% (12回)
中島 : 10.4% (17回)
北川 : 10.4% (17回)
鈴木 : 7.4% (12回)
田中 : 9.2% (15回)
遠藤 : 6.1% (10回)
CTO : 7.4% (12回)
EM : 10.4% (17回)
山田 : 9.2% (15回)

ふーむ・・・・多い人で12%。前後2〜3%程度のゆらぎですね。
母数が少ないとけっこうバラけます。

まとめ

slack上でのbot実行回数の総回数もすくないし、当選確率が均一化されないのは仕方ない。
よく当選する気がするのは気のせいかもしれない。気のせいでしょうきっと。
これからもこのbotは運用されるので、確率が均一化されていくのを期待します!

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0