2017-07-02 61 views
0

我正在創建一個簡單的方法來模擬洗牌甲板。我的想法是存儲原始卡組的大小,並且只要大小不是負數就重複循環。爲什麼Math.random忽略最後一個元素?

在循環過程中,我從列表中複製一個對象(卡),並將它放在另一個列表中。從原始列表中刪除,並恢復循環。

while(size >= 0){ 
     int random = (int) ((Math.random() * size) + 0);  
     shuffledDeck.add(this.orderedDeck.get(random)); 
     orderedDeck.remove(random); 
     size--; 
    } 

這個特殊的甲板的大小是

int size = this.getDeckSize()-1; //51 (52 cards, from 0 to 51)

我的問題是,在幾個不同的嘗試,在洗牌甲板上的最後一張牌是一致的同一張卡作爲最後的unshuffled甲板。這表明random永遠不會等於size-1


我該怎麼做才能讓最後一張卡實際上能夠被洗牌?

(換句話說,爲什麼random永遠等於size-1?)

+1

你說你有52張牌,然後繼續說,大小爲51尺寸應的卡,即52('的Math.random(數量)'從來沒有返回1.0,如果這是你擔心的) –

+0

你有沒有考慮過'新Random()。nextInt(size)'? – hotzst

+0

請編輯您的代碼以顯示[mcve]。 –

回答

3

Math.random()永遠不會返回1.0,作爲docs解釋:

返回double值與一個積極的跡象,大於或者等於0.0並且小於1.0。

即使它沒有返回1.0,它將與極小概率這樣做,你的代碼將磨圓任何其他值了,所以你還是不會被均勻地得到你想要的結果。正如其他人已經注意到你的代碼中有一些進一步的算術錯誤,但即使糾正它們也不會產生正確分佈的隨機值。

您不應該使用Math.random()來完成此任務 - 而是使用Random.nextInt(n),該設計旨在正確返回所需範圍內的統一值。

妥善處理隨機數據源非常棘手。作爲一個經驗法則,如果你發現自己在隨機值上進行算術運算,很可能你做了錯誤的事情(例如產生不一致的結果),你應該尋找一個現有的函數來提供你正在尋找的隨機性的類型代替。

+0

從RNG返回'1.0'不是問題,因爲這會給索引太大。 –

+0

明確提到將'double'強制轉換爲'int'可能對此答案有用。 'Math.random'應該可以正常工作,如果你將'size'增加1,但nextInt確實看起來更合適並且不太容易出錯。 – Dukeling

+0

@代碼學徒甚至修正算術錯誤'Math.random()'不是一種從離散範圍生成隨機值的可靠方法。結果仍然是不一致的,並且使用「Random」類的正確方法是避免這些問題的方法。 – dimo414

0
int size = this.getDeckSize()-1; //51 (52 cards, from 0 to 51) 

問題是你從返回值中減去1。 getDeckSize()顯示你的套牌有完全正確的元素數量(52張牌)。您不應該減1.

+0

它必須是'size = 51',因爲如果我有'size = 52'它會提示Math.random()能夠返回'52'(我測試過了)。這導致'java.lang.IndexOutOfBoundsException:'因爲那裏列表只有52個元素,並且指向元素52是不可能的(因爲列表從元素0開始) – Oak

+0

@Oak請編輯問題以顯示給出的代碼IndexOutOfBoundsException。 –

+1

@Oak「那裏列表只有52個元素,並且指向元素52是不可能的(因爲列表從元素0開始)」請注意,0到51之間的索引給出52個可能的選擇。因此,'size'應該是52.除非你錯誤地命名了你的變量,它應該是'maxIndex'而不是'size'。 –

2

它看起來像size被初始化爲orderedDeck.size() - 1。 由於(int) (Math.random() * size)返回[0, size)範圍內的值,即size本身從不包含, 最後一個元素將永遠不會被選中。

您可以通過使用size + 1作爲上,而不是必然的size修復:

while (size >= 0) { 
    int random = (int) (Math.random() * (size + 1)); 
    shuffledDeck.add(orderedDeck.get(random)); 
    orderedDeck.remove(random); 
    size--; 
} 

但是,請不要使用此技術來創建一個洗牌名單。

當前的方法效率很低, ,因爲從列表中間刪除元素效率不高。 使用Fisher-Yates shuffle來實現高效分類會更好也更容易。 例如:

Random random = new Random(); 
for (int i = list.size() - 1; i > 0; i--) { 
    int j = random.nextInt(i); 
    int tmp = list.get(i); 
    list.set(i, list.get(j)); 
    list.set(j, tmp); 
} 
相關問題