PCDVD數位科技討論區

PCDVD數位科技討論區 (https://www.pcdvd.com.tw/index.php)
-   七嘴八舌異言堂 (https://www.pcdvd.com.tw/forumdisplay.php?f=12)
-   -   能看出這個簡單程式有什麼問題的人絕對是個高手 (https://www.pcdvd.com.tw/showthread.php?t=816594)

michelle_lai529 2008-09-22 12:40 AM

引用:
作者8:5
洗完牌後,會亂,但是不會亂得很均勻。

比如說,rnd_plus(49,49) 有可能產生的洗牌結果有 49! 種。
如果是樂透的話,我們會期望每一種發生的機率都是 1/49!。
但是洗牌的結果顯然不會 1/49!...

比如說,我們要依照這個演算法,洗完牌後,順序剛好完全倒置的機率?
因為程式總共取了 2*49 次的亂數,每個亂數有 1-49 種可能。
所以樣本空間是 49^98。

其中某 X 種樣本,洗完後會完全倒置,則剛好導置的機率是.. X / 49^98。
但不管 X 是多少, X / 49^98 都不會是 1/49!

同樣的,我們想算洗完牌後,1,2 剛好交換,其餘都不動的機率?
那會是 Y / 49^98,也不會是 1/49!。

--
如果對於上面的描述很難體會的話,跑 rnd_plus(3,3) 幾萬次,統計一下結果看看...

機率本來就是一個很鑽牛角尖的問題,認為1號跟最後一號抽到的機會會比較小嗎,其實一樣,某張牌再某個特定的位置機率會不同嗎,那那個特定的位置是哪個,那剛好被選到的機率呢,也是不一定,機率的機率還是機率...如同上面網有所說的,我不是數學家,沒有辦法用式子證明,只是這個方式對我而言夠亂了,也用了這種方式好幾年,不想再為了夠不夠亂這種問題鑽牛角尖,畢竟我寫的程式並不是運用在賭場上,也提供出來給大家做參考,當然有更精簡有效的方式,我也很樂意聽取,拋磚引玉嘛

傳說 2008-09-22 01:00 AM

我不熟VB
但就請教Google大神的結果
宣告了Option Base 1(據說用以指定陣列元素註標起始值為1)
那麼Text1(0)不會遇到錯誤嗎????
至於機率上應該是沒問題的

引用:
作者=風=
會公佈答案
但在公佈答案前
想看看那些高手可以看出問題點
能看出來
真的很不簡單
第一次看到這個程式
發覺寫這程式的人真是利害
樂透程式碼可以寫到這麼短
後來仔細研究才發現這程式碼絕對不能拿來跑樂透 :)
這是個vb6的程式
程式如下

Option Base 1
'樂透,39號取出6個號碼
Private Sub Form_Load()
Dim ball(39) As Integer
Dim i, b1, num As Integer
Randomize
'把球編號 1至39號
For i = 1 To 39
ball(i) = i
Next

num = 39
For i = 0 To 5
b1 = Int(Rnd() * num) + 1 '亂數選號
Text1(i).Text = ball(b1) '選中的號碼,放入Text1文字方塊中
ball(b1) = ball(num)
num = num - 1
Next i

End Sub
:)

okdwnk 2008-09-22 01:09 AM

亂入ing........................

很久以前有用QB寫過類似的程式
QB隨機是抓cpu時脈的某一段時間,來達到接近隨機的隨機

vb沒學過....看不懂

樂透是六台機器,各是39選1
所以不會有愈選愈少的問題

機率只是大概而已

我只知道想要靠寫程式來猜樂透是不可能的~~"就算你有什麼鬼機率學加持

我想樓主想討論應該是邏輯上的盲點吧XD期待解答 :shy:

michelle_lai529 2008-09-22 01:36 AM

引用:
作者8:5
洗完牌後,會亂,但是不會亂得很均勻。

比如說,rnd_plus(49,49) 有可能產生的洗牌結果有 49! 種。
如果是樂透的話,我們會期望每一種發生的機率都是 1/49!。
但是洗牌的結果顯然不會 1/49!...

比如說,我們要依照這個演算法,洗完牌後,順序剛好完全倒置的機率?
因為程式總共取了 2*49 次的亂數,每個亂數有 1-49 種可能。
所以樣本空間是 49^98。

其中某 X 種樣本,洗完後會完全倒置,則剛好導置的機率是.. X / 49^98。
但不管 X 是多少, X / 49^98 都不會是 1/49!

同樣的,我們想算洗完牌後,1,2 剛好交換,其餘都不動的機率?
那會是 Y / 49^98,也不會是 1/49!。

--
如果對於上面的描述很難體會的話,跑 rnd_plus(3,3) 幾萬次,統計一下結果看看...

函數是很久以前寫的,經您的提醒,突然想起來了,原始的想法是:
隨機由1∼n張牌中抽一張,與n交換,然後往前推,1∼(n-1)抽,與(n-1)交換...但是卻會發生洗了牌,結果卻是沒洗的可能情況,後來改為隨機由1∼(n-1)張牌中抽一張,與n交換,強制每張牌一定都會交換到,雖然不合理,卻也避開了完全沒有洗的情況(雖然事實上有可能發生),然而用了這麼久,我都不曉得我是用哪種方式比較多,經由網兄提醒才想到,也順便提出來給其他網友作為參考

山賊 2008-09-22 01:47 AM

num = 39
For i = 0 To 5
b1 = Int(Rnd() * num) + 1 '亂數選號, 範圍為 整數[(0~1)*num]+1, 此時所計算出的亂數整數為數列的座標
Text1(i).Text = ball(b1) '選中的號碼,放入Text1文字方塊中
ball(b1) = ball(num) '拿最末尾的數取代已選出的號碼
num = num - 1 '選擇範圍減 1
Next i '因為已選過的號碼已被最末尾數取代, 故不會再出現, 又因為範圍減一, 所以最末尾數不會重複

====================================

整個運作邏輯乍看之下大致似乎沒問題, 但是 Rnd() 所得到的數必須小於 (38.5/39), 否則乘上 num 再加 1, 會超過數列末尾, 譬如:

1. 若第一次 rnd() 即得 0.9872 以上的數, 則 b1 = 0.9872*39 + 1=40

2. 若是第一次選到 39, 而第二次 rnd() 得 0.9872 以上的數, 得出 b1=39, 也就是重複選號.

3. 同理, 其它任一次只要 rnd() 得 0.9872 以上的數, 都會導致末尾數代替被選中的數, 結果會變成末尾數重複加到數列中, 也就是機率會升高, 另外因為範圍每次減一, 只要 rnd() 得 0.9872 以上的那一次, 該數列末尾的數會被跳過丟棄, 也就是失去被選擇機會, 譬如:

第一次選中位址 38, 位址 38 內的數被位址 39 內含值(39), 位址範圍減一到 38..

第二次選中位址 26, 位址 26 內的數被此時位址最末尾內含值 (39) 代替, 位址範圍減一到 37..

第三次 rnd() 得 0.9872 以上的數, 選中位址 38, 位址 38 內的數是 39, 位址 38 內的數被此時位址 39 內含值 (39) 代替, 位址範圍減一只到 36, 位址 37 內含值 (37) 將永不被選到, 機率為零..

第四次選中 26, 位址 26 內的數為 39, 因為數字 39 已被選過, 變成重複選號, 位址 26 內的數被此時位址最末尾內含值 (36) 代替, 位址範圍減一只到 35..


所以整個關鍵在於 rnd() 不能超過 38.5/39.....

怒火燒盡九重天 2008-09-22 02:02 AM

引用:
作者山賊
整個運作邏輯乍看之下大致似乎沒問題, 但是 Rnd() 所得到的數必須小於 (38.5/39), 否則乘上 num 再加 1, 會超過數列末尾, 譬如:

1. 若第一次 rnd() 即得 0.9872 以上的數, 則 b1 = 0.9872*39 + 1=40


VB 裡的 Int() 指的是取整數部份。對大於零的數字來說,
Int() 的效果應該是捨去小數部份,而不是四捨五入。可能
不會有大哥您所說的疑慮。

參考
http://msdn.microsoft.com/en-us/library/xh29swte.aspx

spooky_mulder 2008-09-22 02:06 AM

引用:
作者okdwnk
亂入ing........................

很久以前有用QB寫過類似的程式
QB隨機是抓cpu時脈的某一段時間,來達到接近隨機的隨機

vb沒學過....看不懂

樂透是六台機器,各是39選1
所以不會有愈選愈少的問題

機率只是大概而已

我只知道想要靠寫程式來猜樂透是不可能的~~"就算你有什麼鬼機率學加持

我想樓主想討論應該是邏輯上的盲點吧XD期待解答 :shy:


大樂透跟今彩 539 這種同一個號碼不會重複出現的樂透,只會有一台機器吧,你有看過大樂透或今彩 539 是開出這種同一期中相同號碼出現兩次的嗎?

chenyy 2008-09-22 02:12 AM

引用:
作者山賊
num = 39
For i = 0 To 5
b1 = Int(Rnd() * num) + 1 '亂數選號, 範圍為 整數[(0~1)*num]+1, 此時所計算出的亂數整數為數列的座標
Text1(i).Text = ball(b1) '選中的號碼,放入Text1文字方塊中
ball(b1) = ball(num) '拿最末尾的數取代已選出的號碼
num = num - 1 '選擇範圍減 1
Next i '因為已選過的號碼已被最末尾數取代, 故不會再出現, 又因為範圍減一, 所以最末尾數不會重複

====================================

整個運作邏輯乍看之下大致似乎沒問題, 但是 Rnd() 所得到的數必須小於 (38.5/39), 否則乘上 num 再加 1, 會超過數列末尾, 譬如:

1. 若第一次 rnd() 即得 0.9872 以上的數, 則 b1 = 0.9872*39 + 1=40

2. 若是第一次選到 39, 而第二次 rnd() 得 0.9872 以上的數, 得出 b1=39, 也就是重複選號.

3. 同理, 其它任一次只要 rnd() 得 0.9872 以上的數, 都會導致末尾數代替被...


Int()不是四捨五入..
不會有這個問題,因為Int是取數字整數部份,Rnd()是取[0~(不到1)]的亂數
EX:
0.9999999999 * 39 = 38.9999999961 整數部份為 38,38+1 = 39

若Rnd是理想的亂數(無限位數,演算法分佈平均)這個程式做法上我看不出有什麼問題.

以現在的寫法它的分佈理論上會比洗牌法平均很多...

53346 2008-09-22 02:19 AM

引用:
作者slamcar
如果想不通,您可以拿一張紙,寫下1~39,

然後,step by step的執行它的程式,應該就會了解。

仔細想想,您就會懂了。

※ball(b1)=ball(num)是把被抽中的數,用被排除掉的數字(num)代替。
※num = num - 1,則是控制剩下數字的數量,39、38、37.........


我應該看出來了...我把兩個For迴圈看成雙重For迴圈了...
For i = 1 To 39
.
.
.
Next

num = 39
For i = 0 To 5
...............
num = num - 1
Next i

這裡的Next i 是從0跑到5和上面的For i = 1 To 39沒關係,
num = num - 1是讓For i = 0 To 5內的選號不要重複。

solonglin 2008-09-22 02:41 AM

這個程式的寫法,看不出有什麼問題,用機率來看也沒問題
最直接的測試方法,用這個程式的演算法寫一個來測試,跑個1萬次,看各個號碼出現的機率是不是有平均就知了 :)


所有的時間均為GMT +8。 現在的時間是08:07 PM.

vBulletin Version 3.0.1
powered_by_vbulletin 2025。