c洗牌演算法
❶ c語言 1到1000隨機排列
聲明一個數組,然後用循環將1000個數字順序寫入,再打亂數組元素
原來有人先回答了。。那我加上打亂1000個數組的函數吧 我用c++來著 不過貌似和c差不多
void xipai(int a[],int b)
{
for(int i=0,j,k;i<b;i++)
{
Sleep(10);
srand(clock());
j=rand()%1000;
k=a[i];
a[i]=a[j];
a[j]=k;
}
}
這是我上次寫的洗牌演算法。。。。。 你可以套用 改幾個地方就行
❷ 用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在實現上也是採取了第四種方法,看來還是那句話,「不要重復製造輪子」
不想寫 - -
❸ C語言 洗牌演算法
/*洗牌程序:用任何語言,隨機分配52張撲克牌到52個位置上,每個位置只容許放一張牌
用1-13表示紅心A--K
14-26表示黑桃A,2,3-,Q,K
27-39表示方塊A,2,3-,Q,K
40-52表示黑桃A,2,3-,Q,K
也就是生成1-52不重復的隨機數,放到數組中*/
#include<iomanip.h>
#include<stdlib.h>
#include<time.h>
const int N=52;
static int a[N];
int create(int n)
{
return (1+rand()%52);
}
int main()
{
int i,j;
srand(time(0));
for(i=0;i<N;++i)
{
a[i]=create(N);
for(j=0;j<i;++j)
{
if(a[j]==a[i])
{
a[i]=(a[i]+1)%52;
}
}
cout<<setw(5)<<a[i];
}
cout<<endl;
return 0;
}
❹ VBA洗牌法原理誰能文字闡釋一下
我找了此資料,一起共享
下面直接介紹【經典數組洗牌法】的演算法原理:
假如你有一個布袋或者抽屜,裡面有m=10個不同號碼的球,
你要隨機抽取,並保證不重復……
那麼正確的做法是:
1.每次從布袋中隨機抽取一個球;注意到rnd()函數的正確抽取目數應該是=m
2.抽取出來的這一個球要另外放置開;
【如果不另外放置,而只是記下號碼後再把球返回布袋,接下來就無法保證這個已經被抽到過的球又被重復抽到。】
而這個,就是1樓代碼中沒有考慮到而產生的重大bug
3.繼續從布袋中隨機抽取另一個球;
注意到此時布袋中剩餘球的數量少了一個是m-1了,因此rnd()函數的正確抽取目數應該是=m-1
4.抽取出來的這第2個球和已經抽取出來的第1個球放置在一齊,並且按新的序列排放。
5.重復以上隨機抽取過程,注意到關鍵是:
a.每次抽取的母數即剩餘球數要遞減1個
b.每次抽取出來的新球要分開放置,不能放回布袋!
c.新抽取出來的球,要和前面已經取出的球按新的序列整齊排放。
d.剩下最最重要一點,但是看到這里好多人都可能不會意識到的一個問題:
布袋中剩餘球如何放置?
即,假定布袋中球也是像放置在抽屜中那樣,有序地排放著的,
那麼每次抽走一個球,必然留下一個空格……【這就很麻煩了!】
因為大家知道,實際上數組中用rnd()函數只能是返回一個一定區間內的值及數組位置,
而如果留有空位的話,隨機性就無法保證高效……萬一抽到空格怎麼辦?難道重新再抽一次?
如果抽到只剩最後一個求時,則原先的布袋/抽屜中,將留下9個空格,則每次隨機函數的計算結果,將有90%的概率仍舊抽到空格……
這就完蛋了。
【經典數組洗牌法】的真正原理是:
1.從m個值中【隨機確定一個位置r】(利用Rnd()隨機函數計算,具體演算法是【以剩餘數m為母數】區間進行隨機值計算並取整返回位置)
2.把這個位置即要被抽取的元素(球)先取出拿在手中【存入臨時變數t】,【騰出一個空位】。
3.把數組的【第1位置】(Lbound)元素拿出來,【放入剛才騰出的r空位】。並隨即【騰出】了數組第一位置作為【新的空位】
(也可以以數組的最末位置(UBound)作為開始位置進行處理,具體演算法代碼就不太一樣了)
4.把上述第2步驟取出的、存入了臨時變數t的元素(球),准確地放入【新的空位】,即數組第一位置。
這個數組第一位置中的【新元素】,就是已經被有效抽取的第1個不重復值。
然後,繼續
1.抽第2個數時,以剩餘母數m-1作為隨機計算區間而返回一個隨機值並計算取整返回第2個不重復的隨機位置r
2.把這個位置r即要被抽取的第2個元素(球)先取出拿在手中【存入臨時變數t】,【騰出一個空位】。
3.把數組的【第2位置】元素拿出來,【放入剛才騰出的r空位】。並隨即【騰出】了數組第2位置作為【新的空位】
(也可以以數組的最末倒數第2位置進行處理,具體演算法代碼就不太一樣了)
4.把上述第2步驟取出的、存入了臨時變數t的元素(球),准確地放入【新的空位】,即數組第2位置。
這個數組第2位置中的【新元素】,就是已經被有效抽取的第2個不重復值。
以上述方式反復進行,抽取、置換,存放,直到最後一個,也不會產生重復抽取了。
下面是【經典數組洗牌法】實際代碼中最簡單的代碼例子:
對於一個下標1開始的一維數組,從中隨機抽取n個元素返回。
SubGetRnd(arr,n)
Randomize
Fori=1Ton'正序洗牌1ton簡化代碼
r=Int(Rnd()*(n-i+1))+i
t=arr(r):arr(r)=arr(i):arr(i)=t'下標1開始一維代碼
Next
EndSub
'正序洗牌1ton簡化代碼詳解:
SubGetRnd(arr,n)
Randomize'隨機種子初始化,保證每次代碼運行或打開文件時出現的隨機序列是和上次文件保存/運行時不同的序列。
Fori=1Ton'遍歷1ton
r=Int(Rnd()*(n-i+1))+i、
'按每次剩餘母數(n-i+1)作為隨機計算區間,計算Rnd()*(n-i+1)然後用int()函數去整,得到剩餘數中的隨機位置。
'緊接著,這個隨機位置後面【+i】處理,轉化成從新的起點i開始的隨機位置r。即不再包括已經抽取出的結果,避免重復。
t=arr(r)'把這個隨機位置r中的元素取出,存入臨時變數t
arr(r)=arr(i)'把【第i個】位置中的元素【放入剛才騰出的r空位】(實際數據操作時並沒有騰出,而只是用新的值直接覆蓋掉。)
arr(i)=t'把上面剛剛【騰出的i空位】放入剛才存放在臨時變數t中的當前最新抽取元素,完成一次抽取過程。
Next'循環抽取、置換、存貯抽取結果
EndSub
❺ 急求 用C程序表示一副撲克牌
對我的回答有什麼疑問或要求,可以Hi我。
//一副牌升級,模擬發牌程序,供參考:
//第五家的牌即為底牌
//大小王的ASCII我的VC中顯示不出來,所以用「大」「小」代替
//演算法:用隨機數模擬洗牌。產生兩個1~54之間的隨機數,然後交換對應的兩張牌。
//洗牌模塊非原創
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
int i,temp,r;
int pk[54]={501,502,
101,102,103,104,105,106,107,108,109,110,111,112,113,
201,202,203,204,205,206,207,208,209,210,211,212,213,
301,302,303,304,305,306,307,308,309,310,311,312,313,
401,402,403,404,405,406,407,408,409,410,411,412,413};
printf("\n");
srand((unsigned int)time(NULL));
for(i=0;i<53;i++) //洗牌
{
r=rand()%(53-i)+i+1;//加1為了洗最後一張牌
temp=pk[i];
pk[i]=pk[r];
pk[r]=temp;
}
int k=1;
for(i=0;i<54;i++)//輸出每家的牌
{
if(i%12==0)printf("\n第%d家的牌:",k++);
switch(pk[i]/100)
{
case 1:printf(" \003");break;//黑桃
case 2:printf(" \006");break;//紅桃
case 3:printf(" \005");break;//梅花
case 4:printf(" \004");break;//方塊
case 5:
switch(pk[i]%100)
{
case 1:printf(" 大 ");break;//大王
case 2:printf(" 小 ");break;//小王
}
}
switch(pk[i]%100)
{
case 1:if(pk[i]/100!=5)printf("%-2c",'A');break;
case 2:if(pk[i]/100!=5)printf("%-2d",pk[i]%100);break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:printf("%-2d",pk[i]%100);break;
case 11:printf("%-2c",'J');break;
case 12:printf("%-2c",'Q');break;
case 13:printf("%-2c",'K');break;
}
}
printf("\n");
return 0;
}
❻ 兄弟能幫忙解決個c語言問題自動發牌 幫我看下我的程序具體怎麼才能實現按先分花色,每種花色再按大小排
你的演算法搞得太復雜了!給puke按花色和大小加上一個編號從0~51,每張牌對應一個編號。在程序里直接對編號操作(發牌、洗牌、排序等)就容易多了。
❼ C語言問題.急!
還原需要16次,不是20次,源碼和輸出結果如下:
/****************************
模擬洗牌-完美洗牌法
****************************/
voidmain()
{
inti,j;
inta[52],b[52];
charpoker[52][4]={
"AH","2H","3H","4H","5H","6H","7H","8H","9H","10H","JH","QH","KH",
"AC","2C","3C","4C","5C","6C","7C","8C","9C","10C","JC","QC","KC",
"AD","2D","3D","4D","5D","6D","7D","8D","9D","10D","JD","QD","KD",
"AS","2S","3S","4S","5S","6S","7S","8S","9S","10S","JS","QS","KS"};
for(i=0;i<52;i++)a[i]=i;
for(j=0;j<52;j++)
{
printf("%s",poker[a[j]]);
if(j%13==12)printf("\n");
}
printf("----\n");
for(i=0;i<16;i++)
{
for(j=0;j<26;j++)
{
b[2*j]=a[j];
}
for(j=26;j<52;j++)
{
b[2*(j-26)+1]=a[j];
}
for(j=0;j<52;j++)
{
printf("%s",poker[b[j]]);
if(j%13==12)printf("\n");
}
printf("----\n");
for(j=0;j<52;j++)a[j]=b[j];
}
getch();
}
輸出結果:
AH2H3H4H5H6H7H8H9H10HJHQHKH
AC2C3C4C5C6C7C8C9C10CJCQCKC
AD2D3D4D5D6D7D8D9D10DJDQDKD
AS2S3S4S5S6S7S8S9S10SJSQSKS
----
AHAD2H2D3H3D4H4D5H5D6H6D7H
7D8H8D9H9D10H10DJHJDQHQDKHKD
ACAS2C2S3C3S4C4S5C5S6C6S7C
7S8C8S9C9S10C10SJCJSQCQSKCKS
----
AHACADAS2H2C2D2S3H3C3D3S4H
4C4D4S5H5C5D5S6H6C6D6S7H7C
7D7S8H8C8D8S9H9C9D9S10H10C10D
10SJHJCJDJSQHQCQDQSKHKCKDKS
----
AH7DAC7SAD8HAS8C2H8D2C8S2D
9H2S9C3H9D3C9S3D10H3S10C4H10D
4C10S4DJH4SJC5HJD5CJS5DQH5S
QC6HQD6CQS6DKH6SKC7HKD7CKS
----
AH4C7D10SAC4D7SJHAD4S8HJCAS
5H8CJD2H5C8DJS2C5D8SQH2D5S
9HQC2S6H9CQD3H6C9DQS3C6D9S
KH3D6S10HKC3S7H10CKD4H7C10DKS
----
AH9H4CQC7D2S10S6HAC9C4DQD7S
3HJH6CAD9D4SQS8H3CJC6DAS9S
5HKH8C3DJD6S2H10H5CKC8D3SJS
7H2C10C5DKD8S4HQH7C2D10D5SKS
----
AH5H9HKH4C8CQC3D7DJD2S6S10S
2H6H10HAC5C9CKC4D8DQD3S7SJS
3H7HJH2C6C10CAD5D9DKD4S8SQS
4H8HQH3C7CJC2D6D10DAS5S9SKS
----
AH3H5H7H9HJHKH2C4C6C8C10CQC
AD3D5D7D9DJDKD2S4S6S8S10SQS
2H4H6H8H10HQHAC3C5C7C9CJCKC
2D4D6D8D10DQDAS3S5S7S9SJSKS
----
AH2H3H4H5H6H7H8H9H10HJHQHKH
AC2C3C4C5C6C7C8C9C10CJCQCKC
AD2D3D4D5D6D7D8D9D10DJDQDKD
AS2S3S4S5S6S7S8S9S10SJSQSKS
----
AHAD2H2D3H3D4H4D5H5D6H6D7H
7D8H8D9H9D10H10DJHJDQHQDKHKD
ACAS2C2S3C3S4C4S5C5S6C6S7C
7S8C8S9C9S10C10SJCJSQCQSKCKS
----
AHACADAS2H2C2D2S3H3C3D3S4H
4C4D4S5H5C5D5S6H6C6D6S7H7C
7D7S8H8C8D8S9H9C9D9S10H10C10D
10SJHJCJDJSQHQCQDQSKHKCKDKS
----
AH7DAC7SAD8HAS8C2H8D2C8S2D
9H2S9C3H9D3C9S3D10H3S10C4H10D
4C10S4DJH4SJC5HJD5CJS5DQH5S
QC6HQD6CQS6DKH6SKC7HKD7CKS
----
AH4C7D10SAC4D7SJHAD4S8HJCAS
5H8CJD2H5C8DJS2C5D8SQH2D5S
9HQC2S6H9CQD3H6C9DQS3C6D9S
KH3D6S10HKC3S7H10CKD4H7C10DKS
----
AH9H4CQC7D2S10S6HAC9C4DQD7S
3HJH6CAD9D4SQS8H3CJC6DAS9S
5HKH8C3DJD6S2H10H5CKC8D3SJS
7H2C10C5DKD8S4HQH7C2D10D5SKS
----
AH5H9HKH4C8CQC3D7DJD2S6S10S
2H6H10HAC5C9CKC4D8DQD3S7SJS
3H7HJH2C6C10CAD5D9DKD4S8SQS
4H8HQH3C7CJC2D6D10DAS5S9SKS
----
AH3H5H7H9HJHKH2C4C6C8C10CQC
AD3D5D7D9DJDKD2S4S6S8S10SQS
2H4H6H8H10HQHAC3C5C7C9CJCKC
2D4D6D8D10DQDAS3S5S7S9SJSKS
----
AH2H3H4H5H6H7H8H9H10HJHQHKH
AC2C3C4C5C6C7C8C9C10CJCQCKC
AD2D3D4D5D6D7D8D9D10DJDQDKD
AS2S3S4S5S6S7S8S9S10SJSQSKS
----
❽ C語言 洗牌
下面是正確的代碼,沒有用鏈表,通過4個數組來做的,必要的注釋我都加了。不理解可以問我。
cout相當於printf,cin相當於scanf。
#include "iostream"
using namespace std;
int main()
{
int num;
cout << "請輸入特定數n:";
cin >> num;
int *arry = new int[2*num];
int *temp = new int[2*num];
int *t1 = new int[num];
int *t2 = new int[num];
//初始化數組
for(int j=0;j<2*num;j++)
{
arry[j] = temp[j] = j;
}
//以下是循環部分
bool gogo = true;
int count = 0;
while(gogo)
{
//更新奇、偶數組
for(int i=0; i<2*num; i++)
{
if(i<num)
t1[i] = temp[i];
else
t2[(i-num)] = temp[i];
}
//重組temp數組
for(i=0; i<2*num; i++)
{
if(i%2==0)
temp[i] = t2[i/2] ;
else
temp[i] = t1[(i-1)/2];
}
//判斷重組後的數組temp是否和原來的數組一樣
for(i=0; i<2*num; i++)
{
if(arry[i] != temp[i])
{
break;
}
}
//如果完全相同,則此時 i==2n;
if(i==2*num)
gogo = false;
count++;
}
cout << count << "次後數組恢復到原來的次序。";
return 0;
}
❾ Objective-C 語言實現 Knuth 洗牌演算法
沒做過這個,但是你可以找c語言的代碼來用,完全支持
❿ 如何用C語言產生不重復的0到9之間的隨機數
剛調試了下,弄不明白的是,為什麼SZ[10]明明不存在卻永遠是產生的r對應輸出的數。。。
。。。。又研究了20分鍾,終於弄懂了。。。實際上應該是9更標准一些,雖然10不會溢出。。。rang()%(10-i)第一個從0-9里選,然後選過的消失。。。最後一個為9
第二次從0-8里選,選過的消失。。。最後2個為9,以後永遠都選不上第8和第九。無論9出沒出現過,以此類推。最終全部為九,其餘消失。
如果為10的話,其實也影響不了,因為最後幾個數永遠都不會取到、
採納了吧。。。。。。
額,其實最標準的應該把那個地方改成9-i,把沒選的數字往前推,但是已經出現的過的就不必推了。。。