當前位置:首頁 » 操作系統 » 洗牌演算法

洗牌演算法

發布時間: 2022-01-10 11:47:51

Ⅰ 隨機洗牌:哪一種演算法是正確的

幾乎所有的程序員都寫過類似於「洗牌」的演算法,也就是將一個數組隨機打亂後輸出,雖然很簡單,但是深入研究起來,這個小小的演算法也是大有講究。我在面試程序員的時候,就會經常讓他們當場寫一個洗牌的函數,從中可以觀察到他們對於這個問題的理解和寫程序的基本功。 在深入討論之前,必須先定義出一個基本概念:究竟洗牌演算法的本質是什麼?也就是說,什麼樣的洗牌結果是「正確」的? 雲風曾經有一篇博文,專門討論了這個問題,他也給出了一個比較確切的定義,在經過洗牌函數後,如果能夠保證每一個數據出現在所有位置的概率是相等的,那麼這種演算法是符合要求的。在這個前提下,盡量降低時間復雜度和空間復雜度就能得到好的演算法。 第一個洗牌演算法:隨機抽出一張牌,檢查這張牌是否被抽取過,如果已經被抽取過,則重新抽取,直到找到沒被抽出過的牌,然後把這張牌放入洗好的隊列中,重復該過程,直到所有的牌被抽出。 大概是比較符合大腦對於洗牌的直觀思維,這個演算法經常出現在我遇到的面試結果中,雖然它符合我們對於洗牌演算法的基本要求,但這個演算法並不好,首先它的復雜度為O(N2),而且需要額外的內存空間保存已經被抽出的牌的索引。所以當數據量比較大時,會極大降低效率。

Ⅱ 關於洗牌演算法,請用java編寫,定義一個數組,儲存1-52以內的數,打亂順序輸出!

import java.util.Enumeration;
import java.util.Hashtable;/**
* 7. * 亂序撲克牌 洗牌方法 8. * 9. *
*
* @author virture 10. * 11.
*/
public class Cards { Hashtable htMember = new Hashtable();// 放置撲克牌的Hash表 public Cards() { } public void put(String card) {
htMember.put(card, card);
} public void get() {
System.out.println("你拿到的牌是:");
Enumeration RLKey = htMember.keys();
while (RLKey.hasMoreElements()) {
String accKey = RLKey.nextElement().toString();// 取HashTable中的關鍵字詞
System.out.print((String) htMember.get(accKey) + ",");
}
} public static void main(String[] args) {
String[] cards = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10",
"J", "Q", "K" };
String[] kinds = { "黑桃", "紅桃", "梅花", "方塊" }; Cards cardList = new Cards(); String suit;// 當前選中牌的類型
String face;// 當前選中牌
int randomCardNum = 52;// 當前隨機取牌的個數,記住不能大於全部牌52張 while (true) {
suit = kinds[Math.round((float) Math.random() * (kinds.length - 1))];
face = cards[Math.round((float) Math.random() * (cards.length - 1))]; cardList.put(suit + face);
if (cardList.htMember.size() >= randomCardNum
&& cardList.htMember.size() <= 52) {
break;
}
}
cardList.get();
}
}

Ⅲ 用C++編寫一個洗牌發牌的函數,玩家可能有兩個、三個和四個

幾乎所有的程序員都寫過類似於「洗牌」的演算法,也就是將一個數組隨機打亂後輸出,雖然很簡單,但是深入研究起來,這個小小的演算法也是大有講究。我在面試程序員的時候,就會經常讓他們當場寫一個洗牌的函數,從中可以觀察到他們對於這個問題的理解和寫程序的基本功。

在深入討論之前,必須先定義出一個基本概念:究竟洗牌演算法的本質是什麼?也就是說,什麼樣的洗牌結果是「正確」的?

雲風曾經有一篇博文,專門討論了這個問題,他也給出了一個比較確切的定義,在經過洗牌函數後,如果能夠保證每一個數據出現在所有位置的概率是相等的,那麼這種演算法是符合要求的。在這個前提下,盡量降低時間復雜度和空間復雜度就能得到好的演算法。

第一個洗牌演算法:

隨機抽出一張牌,檢查這張牌是否被抽取過,如果已經被抽取過,則重新抽取,直到找到沒被抽出過的牌,然後把這張牌放入洗好的隊列中,重復該過程,直到所有的牌被抽出。

大概是比較符合大腦對於洗牌的直觀思維,這個演算法經常出現在我遇到的面試結果中,雖然它符合我們對於洗牌演算法的基本要求,但這個演算法並不好,首先它的復雜度為O(N2),而且需要額外的內存空間保存已經被抽出的牌的索引。所以當數據量比較大時,會極大降低效率。

第二個演算法:

設牌的張數為n,首先准備n個不容易碰撞的隨機數,然後進行排序,通過排序可以得到一個打亂次序的序列,按照這個序列將牌打亂。

這也是一個符合要求的演算法,但是同樣需要額外的存儲空間,在復雜度上也會取決於所採用的排序演算法,所以仍然不是一個好的演算法。

第三個演算法:

每次隨機抽出兩張牌交換,重復交換一定次數次後結束

void shuffle(int* data, int length)

{

for(int i=0; i<SWAP_COUNTS; i++)

{

//Rand(min, max)返回[min, max)區間內的隨機數

int index1 = Rand(0, length);

int index2 = Rand(0, length);

std::swap(data[index1], data[index2]);

}

}

這又是一個常見的洗牌方法,比較有意思的問題是其中的「交換次數」,我們該如何確定一個合適的交換次數?簡單的計算,交換m次後,具體某張牌始終沒有被抽到的概率為((n-2)/n)^m,如果我們要求這個概率小於1/1000,那麼 m>-3*ln(10)/ln(1-2/n),對於52張牌,這個數大約是176次,需要注意的是,這是滿足「具體某張牌」始終沒有被抽到的概率,如果需要滿足「任意一張牌」沒被抽到的概率小於1/1000,需要的次數還要大一些,但這個概率計算起來比較復雜,有興趣的朋友可以試一下。

Update: 這個概率是,推算過程可以參考這里,根據這個概率,需要交換280次才能符合要求

第四個演算法:

從第一張牌開始,將每張牌和隨機的一張牌進行交換

void shuffle(int* data, int length)

{

for(int i=0; i<length; i++)

{

int index = Rand(0, length);

std::swap(data[i], data[index]);

}

}

很明顯,這個演算法是符合我們先前的要求的,時間復雜度為O(N),而且也不需要額外的臨時空間,似乎我們找到了最優的演算法,然而事實並非如此,看下一個演算法。

第五個演算法:

void shuffle(int* data, int length)

{

for(int i=1; i<length; i++)

{

int index = Rand(0, i);

std::swap(data[i], data[index]);

}

}

一個有意思的情況出現了,這個演算法和第三種演算法非常相似,從直覺來說,似乎使數據「雜亂」的能力還要弱於第三種,但事實上,這種演算法要強於第三種。要想嚴格的證明這一點並不容易,需要一些數學功底,有興趣的朋友可以參照一下這篇論文,或者matrix67大牛的博文,也可以這樣簡單理解一下,對於n張牌的數據,實際排列的可能情況為n! 種,但第四種演算法能夠產生n^n種排列,遠遠大於實際的排列情況,而且n^n不能被n!整除,所以經過演算法四所定義的牌與牌之間的交換程序,很可能一張牌被換來換去又被換回到原來的位置,所以這個演算法不是最優的。而演算法五輸出的可能組合恰好是n!種,所以這個演算法才是完美的。

事情並沒有結束,如果真的要找一個最優的演算法,還是請出最終的冠軍吧!

第六個演算法:

void shuffle(int* data, int length)

{

std::random_shuffle(data, data+length);

}

沒錯,用c++的標准庫函數才是最優方案,事實上,std::random_shuffle在實現上也是採取了第四種方法,看來還是那句話,「不要重復製造輪子」

不想寫 - -

Ⅳ 高分求一個除去大小王52張牌的洗牌演算法

洗牌的方法很多,我喜歡用隨機選擇法,基本方法是:先把52張牌順序存放到一個數組裡面,然後產生一個1~52的隨機數,把隨機數的那張牌抽出來放到新數組的第一個位置,在原數組裡面把抽取出的牌之後的牌前移動一個位置,然後產生1~51的隨機數類似處理,直到只剩一張排。

描述如下:
VAR A1,A2:[1..52] OF POKE;
A2[1..52]=POKE1..POKE52;
FOR I=1 TO 51 DO
BEGIN
N=RANDOM(1,52-I+1)
A1[N]=A2[N]
FOR J=I TO 52 A2[J]=A2[J+1];
END
A1[52]=A2[1]

Ⅳ 幾種撲克牌洗牌演算法

洗牌的

幾種話先設定好洗牌方式幾種比方對分上下交l以及交織洗牌然撲克牌後用隨機數生成函數確定單步洗牌作牌的數量多反復幾遍即可。

  1. 的一個合理的定義就是演算法

  2. 一副撲克張牌有種陳列方式。

這樣做的好處:

給出的洗牌算演算法應該可以等概率地生成這種結果中的一種

Ⅵ java洗牌演算法為什麼改成從1到54就出錯了

for(int i=1;i<55;i+=3){

p1.add(cards.get(i));

p2.add(cards.get(i+1));

p3.add(cards.get(i+2));你的cards有54個,這個循環中當i=53,時,i+2=55,越界,所以報錯

}

Ⅶ 怎麼證明這個洗牌演算法是隨機的

有一副牌假設有N張,請設計一個隨機洗牌演算法。
解決方案:
這里只給出一個可以使用數學證明每張牌出現在任何位置概率為1/N的演算法。
Poker[N]
for (i = 0; i < N; ++i)
{
k = rand() % ( i + 1)
if (i != k)
{
switch(Poker[k], Poker[i]);
}

Ⅷ java洗牌演算法問題

你用System.out.print方法列印一個對象時,控制台會默認調用其toString方法,java就這么設計的

Ⅸ 關於牌類游戲洗牌演算法一問:怎樣才算把牌洗

1. 嵌入式洗牌法
把部分的塔羅牌拿在手中,使牌面朝下,將剩下的牌隨意插入手裡的牌,再自手中拿出一些牌,再插進去。重復這個步驟直到你覺得牌洗干凈了為止。不過這種洗牌方式非常容易折損牌的邊緣,要小心喔(有時還會刮傷手…)
2. 推擺洗牌法
將塔羅牌牌面朝下,在桌面上弄混,之後用左手的拇指將最上方的一疊牌推回左手,再用右手拇指推下方的一疊牌到右手,持續重復這個動作,直到所有的牌都被分開,之後重疊再一起並重復這些動作,直到你覺得已經洗干凈為止。不過這個方法不是很容易,你必須常常練習才不會打到手(嘿嘿嘿…)
3. 一般正常洗牌法
將塔羅牌牌面朝下,雙手以順時針或逆時針方向將牌均勻混和即可。以上介紹的三種方式均為一般常見的洗牌方法(尤其是第三項,一般市面上的中文塔羅書籍均是以此法為主,故簡略帶過),在洗牌時一般是建議受占者心中專心默念要問的問題,而占卜師擇是專心洗牌。至於是否要由受占者洗牌則是見仁見智。不過殿主向來是由自己洗牌,有時總會遇到『朋友』想幫『他的朋友』占卜,而『他的朋友』並不在現場的情況,若是非當事者洗牌不可,大概這牌也就不需要算了…
至於是否一定要使用這幾種方法來洗牌,答案是不一定。只要能將牌充分混和均勻,也不致傷到牌面就好了。
此外,在使用第三種洗牌方式時,殿主提供一些經驗分享給大家:
1.轉動時要利用指腹與手腕的力量
2.像寫書法一樣,手腕抬高
3.盡量輕柔地轉動每一張牌
4.每一張牌建議盡量轉三圈半以上(要公轉也要自轉喔)
5.注意上層的牌要與下層的牌混合均勻

熱點內容
vbtxt文件加密 發布:2024-11-16 11:47:27 瀏覽:628
mountlinux共享文件夾 發布:2024-11-16 11:42:22 瀏覽:685
杭州防潮存儲櫃 發布:2024-11-16 11:40:17 瀏覽:961
phpimplode 發布:2024-11-16 11:27:20 瀏覽:561
端游網易版我的世界決戰斗羅伺服器 發布:2024-11-16 11:14:37 瀏覽:20
byte類型c語言 發布:2024-11-16 11:07:28 瀏覽:577
androidview設置高度 發布:2024-11-16 10:52:26 瀏覽:488
cryptopythondes 發布:2024-11-16 10:52:15 瀏覽:877
多台電腦如何創建存儲伺服器 發布:2024-11-16 10:44:44 瀏覽:340
移動雲伺服器下載 發布:2024-11-16 10:37:23 瀏覽:857