精華區beta C_Chat 關於我們 聯絡資訊
唔... 很多板友說的對,機率的東西抽1萬次真是太兒戲了, 我先入為主覺得這是理所當然的結果,一心求快而忽略了些基本的東西, 非常抱歉... <(_ _)> 花了點時間把實驗做得完整一點... 對照組: 直接抽 實驗組: 先抽30次友抽,全槓龜再用心抽 各抽1億次,結果如下: regular, UR: 1000027 cards, (1.000%) friend pick first, UR: 1000331 cards, (1.000%) 1億抽的結果,一般正常抽取得到了1,000,027張UR, 而先用30張友情抽篩選再心抽則是得到了1,000,331張UR。 可以看得出來,這 python 內建的 random 函數庫很不錯, 1億抽之下,正常抽選的機率非常貼近 1% 世界線, 而用友抽篩選擇是約略增加了萬分之三的獲獎率。 當然這也只是個相當粗糙的實驗,LLSIF 號稱全球 4000萬 download, 1億抽還不夠每個帳號抽一單,而且我也只做了一次, 主要是這程式跑起來也挺花時間的,從昨晚到現在花了10幾個小時吧, 我把程式附在後面,更多抽的實驗就留給有興趣的朋友繼續囉... 稍微知道隨機函數在程式裡的運作原理的朋友應該可以理解這結果, 對一個隨機均勻分佈的序列,一直做把特定的數字拿掉的動作, 當然會造成其他數字的濃度增加,效果如何則是要看各個隨機函數的設計。 後續還有很多板友提出各式各樣的抽選設計方式會造成友抽試水溫無效, 嗯...,我的感覺是...,沒錯,的確有其他的設計方法, 但是,明明一行 r = random() 就能解決的程式,幹嘛要弄那麼複雜? 以商業遊戲 app 來說,原本的隨機函式已經足夠好了, 工程師光是抓 bug,讓每週活動準時上架都是個挑戰, 就連一堆人創了幾十個幾佰個帳號刷 UR 起手,甚至販賣,這種都不防了, 真的有人會那麼閒,花時間去求追完美的隨機結果嗎? 又不是要做什麼超高精度的科學實驗... 更別提人家原本 random() 就已經是經過千錘百鍊,有很良好的特性, 亂改架構搞不好還讓 random 函式變得更不隨機... 回頭來說,先用友抽篩選到底算不算有用,唔...,就看各人吧~~ 現在有兩種抽法,一種是 1%,另一種是 1.0003%, 覺得沒差的人就隨心抽,其實真的也沒差, 萬分之三的機率,抽中100張UR還不夠你多一張, 不過,對我而言,友情抽擺在那裡,本來就會抽掉的, 只不過是調配了一下抽選的順序就加了 0.0003%,why not? -- import time import random def friendPickTest(): for i in range(30): # 3?捡铝???0?? r = random.randint(1, 100) # 1~100 ????嚗??耿? if r <= 5: # R card ?机?? 5% return True return False def routineLovecaPick(nTargetRound, bDoFriendPickFilter, tSeed): random.seed(tSeed) cntLovecaPick = 0 cntUR = 0 while cntLovecaPick < nTargetRound: if bDoFriendPickFilter: if friendPickTest(): continue r = random.randint(1, 100) if r == 1: # hit cntUR = cntUR + 1 cntLovecaPick = cntLovecaPick + 1 if cntLovecaPick % 1000000 == 0: progress = float(cntLovecaPick) / nTargetRound * 100 strEcho = '{0:.2f}, {1}/{2}'.format(progress, cntUR, cntLovecaPick) print(strEcho) return cntUR def main(): tSeed = time.time() nTargetRound = 100000000 # ????對圀??? 1??猟暑 cntNaivePick = routineLovecaPick(nTargetRound, False, tSeed) cntTrickPick = routineLovecaPick(nTargetRound, True, tSeed) rateNaive = float(cntNaivePick) / nTargetRound * 100 rateTrick = float(cntTrickPick) / nTargetRound * 100 strEcho = 'regular, UR: {0} cards, ({1:.3f}%)'.format(cntNaivePick, rateNaive) print(strEcho) strEcho = 'friend pick first, UR: {0} cards, ({1:.3f}%)'.format(cntTrickPick, rateTrick) print(strEcho) if __name__ == '__main__': main() -- 喜歡拍照的正妹們,趕快來找我報到吧!!! https://flic.kr/s/aHsk8JbB2C -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 125.227.30.252 ※ 文章網址: https://www.ptt.cc/bbs/C_Chat/M.1511831771.A.353.html
tonyxfg: 你應該多跑幾次一億抽來做比較才對,這數值看起來就是正 11/28 09:22
tonyxfg: 常的誤差範圍而已,很難說服人一定是這樣啊... 11/28 09:22
這實驗蠻花時間的勒,跑1億抽要十幾個小時... 其實這也可以看成我做了100次100萬抽的實驗呀? 我有把程式附在後頭,有興趣的人可以自己試看看... 不過... 我覺得... 與其多跑幾次實驗, 還不如去看一下計算機結構跟隨機函數的設計原理... ^^;
j9145: 你一開始就搞錯前題。 11/28 09:28
j9145: 不是為什麼要搞這麼複雜,而是必須這麼複雜。 11/28 09:29
j9145: 你要先搞清楚轉蛋遊戲的轉蛋機就是最大營利項目,多花資源 11/28 09:30
j9145: 去維護很合理吧? 11/28 09:31
去把原本就已經很好的 1.000027% 調整成完美的 1% 嗎? 我還蠻難想像會有遊戲公司這麼做就是了... 能不能做到也是個問題... 轉蛋遊戲最大的營利手段,靠的不是完美的1%,而是活動內容跟卡牌設計吧... 與其花資源去追求高精密度亂數,多找兩個繪師把妹子畫美一點會不會比較有用?
Xavy: 又來了,又活在自己世界了 11/28 09:39
j9145: 所以你找畫師來設計轉蛋機機率? 11/28 09:39
我的意思是... 你需要隨機的時候,寫一行 r = random() 就夠了,不用想太多
lanjack: 遊戲廠商:反正有沒有差那一點點機率玩家又不可能知道, 11/28 09:40
lanjack: 有卡面強度就有人抽。不爽拿證據吉我啊~ 11/28 09:40
dreamnook: 我已經搞不懂這串到底是在討論什麼了 官方有無作弊嗎XD 11/28 09:43
官方根本沒必要作弊吧... 說到底,玩家抽不到卡,對營運未必是好事啊 又不是所有玩家都信抽到有教,也是有抽不到就崩潰刪遊戲的啊... ( ̄▽ ̄#)﹏﹏
Cyjustin: j大是當工程師是神嗎,機率越複雜程式越難搞 11/28 09:49
Cyjustin: 搞出來都東西還有一定的誤差範圍,真的沒必要 11/28 09:51
lkzax: 如果1E抽要跑十幾個小時 有10萬人同時按10連是要跑60秒? 11/28 09:51
之所以要跑十幾個小時,因為我的抽法是抽一張心抽時,要先抽30張友抽, 確定30抽都摃龜之後,才會去抽一張心抽。 友情抽30連可能要好幾次才會有有一次全槓,這時才抽一張心抽,所以才要這麼久。
lkzax: 這樣要多少台電腦才夠? 11/28 09:52
e2167471: 有實驗有推 不無可能吧 反正對玩家來說都是黑盒子 11/28 09:57
e2167471: 說這個可能 和說這個不可能的荒謬程度是類似的XD 11/28 09:58
e2167471: 更何況先確定30抽共龜才去抽並不是很容易的做法 11/28 09:58
e2167471: 我是不覺得遊戲公司會為了維護萬分之三的機率去防這個 11/28 09:59
真的... 與其防止玩家抽到,不如先解決一下那些萬分之三, 幾百抽抽不到山翁的問題... 抽到的人以後還會抽別的,但抽不到而崩潰的人就不一定了...
lkzax: 30抽沒中的機率約為0.2 反推回去 總抽數約為5.65E多 11/28 10:05
lkzax: 這樣10萬人同時按10連也是要跑10秒 這樣算合理嗎? 11/28 10:06
剛才算了一下,我的電腦產生100萬筆隨機數字要花 2.38 秒 實際運作還要加上其他迴圈、條件控制、函式進出堆疊、資料庫讀寫什麼的, 要10秒也是有可能吧,不過上雲端的服務,不會是我這樣單台電腦在運作就是了 def calcRandomTime(nRound): tStart = time.time() for i in range(nRound): r = random.randint(1, 100) tEnd = time.time() print('time spend:', tEnd - tStart) if __name__ == '__main__': calcRandomTime(1000000)
lkzax: 就算先抽友抽再抽石抽也才提升萬分之三 實在很雞肋 11/28 10:08
Cyjustin: 合理,所以通常人數過多就會有伺服器分流 11/28 10:10
intela46585: 如果我在廁所跑這程式 出來機率多個百萬分之一 11/28 10:15
intela46585: 我是不是也可以主張在廁所抽會提升機率... 11/28 10:15
要符合這實驗規模的話,你得要上很多次廁所, 然後你也要同時在廁所以外的地方跑多次同樣的實驗, 如果得到在廁所真的會多百萬分之一的機率,我也會認同唷!!
intela46585: 這樣跑的結果 跟玄學有什麼差別? 11/28 10:16
我同意這實驗很粗糙,1萬次不夠,100萬次不夠,1億次不夠,永遠都不夠 其實最簡單就是去理解程式語言產生隨機數字的原理, 當你懂了 random() 的原理之後,就不會覺得這麼玄了
Cyjustin: 跟玄學差不多啊,畢竟沒脫離誤差範圍 11/28 10:26
shinchen: 是你對random的原理只懂一半吧...照你的實驗 第一個seed 11/28 10:30
shinchen: 從頭到尾不改變 第二個只用一台電腦一個thread抽 11/28 10:31
shinchen: 這兩個實際狀況都是不成立的 11/28 10:31
shinchen: 你的理論基礎是在同seed下亂數器會讓各數字出現率平均 11/28 10:32
shinchen: 但是當seed不定的時候這理論就不成立了 11/28 10:33
你在寫程式產生亂數的候會讓 seed 一直換來換去嗎? 正常來講應該不會吧,原本 random 序列已經有良好的特性了, 目標1%,跑了1億次得到1.000027%的結果,請問有什麼道理要去改 seed? 抽卡又不是一次花半小時,也不過就幾分鐘之內的事, 你跑一個隨機程式,卻每分鐘都在改 seed, 搞不好跑出來的序列隨機性比什麼都不做還要差吧... ._.
j9145: http://www.sfoxstudio.com/10493 11/28 10:38
j9145: 沒時間打太多,直接貼文,重點是你能不能提升玩家付費比 11/28 10:39
dreamnook: 到後來反正覺得可以提升萬分之3來滿足心情也可以啦 11/28 10:40
j9145: 而不是機率到底有沒有1%,這篇文作者是業界待很久的企劃, 11/28 10:40
j9145: 只要你無法得知營運有沒有做手腳,那你一切玄學都是沒有意 11/28 10:41
j9145: 義的。 我要表達的就是這樣 11/28 10:41
既然1%機率對營運不是重點,他們就沒必要花時間在完美的隨機上, 處理隨機大半只是簡單的一行 r = random() 解決,不是嗎? 如此一來,利用隨機函式在程式上的特性,先用友抽去濾特定結果, 這是很科學的作法,你花點時間去理解計算機結構跟程式語言,就不會覺得這麼玄了。 我絕對是相信營運沒做手腳的,如果懷疑的話,根本就不會玩這遊戲 ^^; 有動手腳也是為了讓你更好抽... :p
j9145: 沒人規定友抽跟石抽一定是同樣公式XDDD 11/28 10:51
沒人這麼規定,但這樣最簡單 如同你剛才所說的,對遊戲公司而言,重點是能不能提升玩家付費比 完必沒必要多花資源去設計不同的公式來應付友抽跟石抽, 以一個小遊戲公司,每個月領那麼一點點薪水的工程師, 要做出比 1.000027% 更好的隨機函式根本是天方夜談,這部份不用想得太複雜了
j9145: 而且你肯定有沒有把文章看完,繼續用你的玄學試吧 11/28 10:51
Cyjustin: j大那篇講轉蛋機制設計,特定up跟保底本來就會有 11/28 11:03
Cyjustin: 但現在不是在討論那個吧? 11/28 11:06
gn00399745: seed只要用時間就代表示一直變動SEED了阿 11/28 11:07
Cyjustin: 更何況他描述的部分行為在日本還不是合法的 11/28 11:07
noob9527: 重點就是通常會用時間當seed 所以不會符合你的說法阿 11/28 11:16
我有點懷疑我們在講的是不是同件事... 所謂的 seed 是改變隨機生成器的種子, 你給定一個初始條件,然後用既定的演算法不斷迭代產生隨機數, 一般 seed 是用時間,沒錯,但這是初始時候的事, 初始階段過後,整個隨機序列就已經決定好了 如果他初始出來的隨機序列是 1, 3, 7, 11, 你第一次抽一定是1,第二次不管你是早上抽、晚上抽、廁所抽,都是3 附上簡單的程式供您做驗證 def genRandom(nRound): listRandom = [] for i in range(nRound): r = random.randint(1, 100) listRandom.append(r) print(listRandom) if __name__ == '__main__': random.seed(1) genRandom(10)
gn00399745: 在c++上只要call的srand(seed) rand數列就改變了 11/28 11:18
noob9527: 那照你的說法 同seed 然後你先抽友抽30次 11/28 11:20
noob9527: 把你的UR抽抽掉 你回到石抽 抽UR機率也就更低了阿 11/28 11:20
策略是如果友抽中獎了,就不會去石抽,要繼續讓友抽連摃N次之後才會石抽
noob9527: 還是你友抽抽到R都會重開app重抽30次 那也算很閒了 11/28 11:22
不需要重開 app,抽獎的程式是在 server 端跑,跟你手機上的 app 無關
noob9527: 然後亂數的部分 時間當seed是非常正常的 11/28 11:23
當然都是用時間來當 seed,但我就問一句,你寫程式需要用到隨機時, 你會 seed 幾次?除了初始化那一次之外,你之後還會故意再 seed 第二次?
noob9527: server端處理完你的抽 下次當然就不同seed了 11/28 11:24
noob9527: 你如果說抽10連 那個10連是同個seed或許還有可能 11/28 11:24
noob9527: client斷開重新連你 你的server端當然重新初始化參數阿 11/28 11:27
呃... 我想請問... 這些想法... 你是真的都這樣寫程式... 還是... 用想像的... ( ̄ー ̄;)
noob9527: 在下孤陋寡聞 server端用php就這樣寫的 11/28 11:36
你會在 php server 端每次處理一個 request, 如果有需要用到隨機數字時,就執行一次 srand(time())? 我無法想像你這麼做的目的是什麼... 為了要讓同時連線進來的人,保證能隨機出相同的結果嗎?
noob9527: 如果你有更好的寫法 那就指導一下 11/28 11:36
noob9527: 而且php下rand就是直接給一個新的seed 我沒記錯的話 11/28 11:39
直接下 rand() 就好,其他的事不用多想 rand 是從已生成好的隨機序列拿一個出來, 但 seed 的意思則是要重新產生一組新的隨機序列, 我覺得程式在執行中,不管有多少新的連線進來都不應該去重新 srand() 人家的隨機序列是一堆天才設計出來的, 透過程序不斷去 srand(),只怕非但不能得到更好的隨機性質,反而有可能出包
gn00399745: 我懂你的做法 但...思考了以後這個機率提升很無感XD 11/28 11:39
noob9527: 目的是什麼... php就這樣阿 request結束 程式也結束 11/28 11:41
noob9527: 可是..新版php就是更新你講的那個 他會自動srand() 11/28 11:45
自動 srand(),是指你不需要操心初始化的動作,而不是時時在 srand 時時在 srand 這設計很危險,我現在就能想到一個可能性, 我可以先 srand() 未來的時間,比方說今天中午12點整好了 srand(time(今天中午12點整)) r = rand() 這時我就在本地端得到了 r 的結果,如果剛好是 0, 就準時發 request try 你的 server,看是拿到 R 卡還是 UR, 如果拿到 R,那我就再 try 另一個能獲得 100 結果的時間, 網站出隨機數字還卻一直在 srand,會有被預判攻擊的風險, 一般是不會這麼設計的 另外,一般來說,網路服務並不是一個連線進來就開一支程式, 他是一支一直在運行的程式,負責處理不斷湧進來的連線
noob9527: 但是php就是一個連線進來就開一個給他呀 11/28 12:10
noob9527: 什麼東西都要重新初始化 資料庫 參數 seed 11/28 12:11
noob9527: 你想把參數保留到下一個連線還有困難勒 11/28 12:12
呣... 我想你可能是對 seed 的認知有點誤會,或者跟一般的定義不太一樣 http://php.net/manual/en/function.srand.php 這裡剛好有個網頁在討論 php 的亂數怎麼做的 方法一 <?php srand(5); echo(rand(1, 10)); srand(5); echo(rand(1, 10)); srand(5); echo(rand(1, 10)); ?> 方法二 <?php srand(5); echo(rand(1, 10)); echo(rand(1, 10)); echo(rand(1, 10)); ?> 你兩個都拿去跑一下,就會理解為什麼 srand() 該在初始的時候做一次就好 新版 php 甚至把它做成自動的,省去你手動 srand 的需求, 因為,如果你是用時間做 seed,好死不死, 有使用者同時 request,他們會得到相同的結果, 初始化都要小心了,更別提每次 request 都重新下 seed 一直換 seed 並不會讓你的隨機數字更好,反而會有被預判攻擊的風險
noob9527: 你知道php有10個連線進來 方法二的srand()會執行10次嗎 11/28 12:28
對,所以完全不該這麼做,srand 不能寫在處理連線 request 的地方
noob9527: 當然同你講的 10個連線如果分秒不差 他會是同樣結果 11/28 12:29
noob9527: 而且你說服務只會使用同個seed 然後所有人都在抽 11/28 12:29
noob9527: 你的30抽友抽期間 也有一堆人在抽 UR搞不好早被抽走了 11/28 12:30
noob9527: 你根本沒辦法知道30抽的時間內有沒有其他人抽到 11/28 12:30
這完全不重要,其他人所有的行為都是隨機的,當成亂數的一部份即可。 一堆人搞不好會把UR抽走,也可能把R卡抽走呀 這個模型並不是看最近的10抽20抽的結果, 而是未來的一佰萬抽,一億抽之內,少掉最開頭30抽的效果
noob9527: 你不寫在連線request的地方要寫在哪...? 11/28 12:31
別寫,php 已經幫你考慮好了,不要自己做 srand
noob9527: 你這講法就問題點阿 你30抽沒中 未來中的機率更高沒錯 11/28 12:38
noob9527: 但是那機率高的部分不一定是你抽到的阿 隨時都有人在抽 11/28 12:38
noob9527: 機率偏高時 馬上就會被抽掉獎項 機率又會回到原本1% 11/28 12:39
noob9527: 在你把30抽沒中當做一個前提 就是賭徒論了 11/28 12:41
什麼叫做機率高的部份?未來的池是個整體呀... 不管怎麼抽,都沒辦法阻止別人去抽掉後面的UR, 先友抽唯一的功能,就只是讓序列刪掉最開頭30張R卡而已 我會在數列中,發生連續30張R卡之後的時間點切入,這策略就這樣而已 隨機時間切入的1億抽,跟等發生30張R卡之後的1億抽, UR卡的濃度有些許的不同,這是用程式隨機的特性,瞭解這樣就夠了
noob9527: 會這樣說 就是因為你無法保證30抽中間是否有其他因素 11/28 13:12
noob9527: 在無法排除這些因素 你的策略就不成立 11/28 13:13
noob9527: 然後seed那塊 無論是php幫我處理好 還是自己srand 11/28 13:13
noob9527: 都還是每次執行就分配一個新seed 11/28 13:14
呃... 為什麼你這麼有把握啊... php 的 interpreter 是你寫的嗎... 如果是的話我建議下版快點改掉, 每次執行都重新分配一個 seed 是非常不良的設計... http://php.net/manual/en/function.srand.php 看一下這篇文章 hagen 回覆 It is REALLY essential to make sure that srand is called only once. 他甚至用了一些方法來保護 request handle 裡不會重覆呼叫 srand <?php if (!$GLOBALS["IHaveCalledSrandBefore"]++) { srand((double) microtime() * 1000000); } ?> php 我是真的不熟,但是在 request 裡一直 srand 真的超出我的想像...
noob9527: 你架個簡單的server 跑一次網頁 你就知道為什麼了 11/28 13:28
noob9527: 程式碼也夠簡單 就一行沒其他<?php echo mt_rand();?> 11/28 13:29
noob9527: 然後你每次F5重整網頁 就是一個新的執行 11/28 13:30
my_rand() 只是在產生亂數而已,他並沒有一直改 seed
noob9527: 就程式第一次執行php會自動幫你配seed呀.. 11/28 13:33
Yes,這個動作只做了一次,之後就不會再做了, 當你第一次執行時,未來所有的亂數數列也被決定好了, 並不會有什麼每次 request 就分配一個新seed這種事
noob9527: <?php if($a==null){$a=1;} $a++; echo $a;?> 11/28 13:41
noob9527: 以這例子 你覺得這行執行10次分別會輸出多少? 11/28 13:42
noob9527: 再看看上面的mt_rand() 執行10次 你覺得會是怎樣? 11/28 13:46
noob9527: 也沒什麼特別的 就單純網頁跟服務是不一樣的執行方式 11/28 13:47
這跟 seed 有關係嗎?不管 mt_rand() 執行了幾次,他的 seed 是同一個, 得要先把這基本的觀念建立好才行
noob9527: 我想我很難跟你解釋每次執行時 seed都會不存在的問題 11/28 13:52
當然很難解釋,因為根本就不是這樣 php rand() 怎麼運作的?看這裡 https://www.w3schools.com/php/func_math_rand.asp PHP 4.2.0: Random number generator is seeded automatically php 2.4 後會自動幫你 seed,但你把這段話腦補成每次 rand 都會下 seed, 不是的,他只有在第一次(或者是服務初始時)才會播一次 seed,以後就不會變了 如果對 srand 的原理還有疑惑,可以看這裡,中文的 http://libersky.tumblr.com/post/64149021859/c-randsrand ※ 編輯: archon (118.161.36.233), 11/28/2017 19:29:32