日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

全排列函数、组合函数

發(fā)布時(shí)間:2023/12/2 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 全排列函数、组合函数 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1

1、求一個(gè)全排列函數(shù):如p([1,2,3])輸出:

? ? ? ? ?[123],[132],[213],[231],[321],[312].

2、求一個(gè)組合函數(shù)如p([1,2,3])輸出:

? ? ? ? ?[1],[2],[3],[1,2],[2,3],[1,3],[1,2,3]

這兩問可以用偽代碼。

void swap(int *a, int *b) //交換函數(shù) {int tmp;tmp =*a;*a=*b;*b=tmp; }

以為很簡單,手寫全排序第一遍就錯(cuò)了。

func1( a[], n, m)if(m>n-1) return; endprint(a);for(i=m+1 to n-1)swap(a[m], a[i]);func1(a, n, m+1);swap(a[m], a[i]);end end

調(diào)用:func1(a, n, 0)

發(fā)現(xiàn)以a[0]開頭的只有一組,其他可以滿足,因?yàn)閕=m+1。func1的理解就是輸出a,并將a[m]分別與a[m+1]..a[n]交換后的后n-m-1個(gè)元素的排列組合。當(dāng)調(diào)用了func1(a, n, 0)時(shí),以a[0]開頭的只有一組。改為for(i=m?to n)可以嗎?

No!這時(shí)候,卻多出很多。由于m>n-1才是終止條件。即要進(jìn)入n-1層,每層有n-m+1個(gè)分支,于而每進(jìn)入一層就會(huì)print(a),這樣就會(huì)輸出的次數(shù)不等于n!了(一般情況下)

糾結(jié)了n久之后,再改,應(yīng)該當(dāng)m=n-1時(shí)才輸出。Bingo!

1 func1( a[], n, m) 2   if(m==n-1) 3    print(a); 4 return; 5 end 6   for(i=m+1 to n-1) 7     swap(a[m], a[i]); 8     func1(a, n, m+1); 9     swap(a[m], a[i]); 10   end 11 end 12 13 調(diào)用:func1(a, n, 0)

可是結(jié)果卻和題目的不完全一致。因?yàn)槭禽敵龅奈恢玫南群髥栴}。換成尾遞歸即可。

1 void func1(int a[], int m, int n) 2 { 3 for (int j=m; j<n; j++) 4 { 5 swap(&a[m], &a[j]); 6 func1(a, m+1, n); 7 swap(&a[m], &a[j]); 8 } 9 if (m==n-1) 10 { 11 for (int i=0; i<n; i++) 12 { 13 cout<<a[i]<<" "; 14 } 15 cout<<endl; 16 return; 17 } 18 }

簡單的題目也不見得簡單啊~(其實(shí)個(gè)人水平問題。。。)

2、只考慮有最大有32個(gè)數(shù)的情況:

1 void func2(int a[], int n) 2 { 3 int cnt=1; 4 5 if (n>32 || n<=0) 6 { 7 return; 8 } 9 cnt = cnt<<n; 10 11 unsigned int tmp; 12 for (unsigned int i=0; i<=cnt; i++) 13 { 14 tmp = 1; 15 for (int j=0; j<n; j++) 16 { 17 if (i & tmp) 18 { 19 cout<<a[j]<< " "; 20 } 21 tmp = tmp<<1; 22 } 23 cout<<endl; 24 } 25 }

總結(jié):

常用的排列組合算法,包括全排列算法,全組合算法,m個(gè)數(shù)選n個(gè)組合算法等。

2. 排列算法

常見的排列算法有:

(A)字典序法

(B)遞增進(jìn)位制數(shù)法

(C)遞減進(jìn)位制數(shù)法

(D)鄰位對換法

(E)遞歸法

介紹常用的兩種

(1) 字典序法

對給定的字符集中的字符規(guī)定了一個(gè)先后關(guān)系,在此基礎(chǔ)上按照順序依次產(chǎn)生每個(gè)排列。

[例]字符集{1,2,3},較小的數(shù)字較先,這樣按字典序生成的全排列是:123,132,213,231,312,321。

生成給定全排列的下一個(gè)排列 所謂一個(gè)的下一個(gè)就是這一個(gè)與下一個(gè)之間沒有字典順序中相鄰的字符串。這就要求這一個(gè)與下一個(gè)有盡可能長的共同前綴,也即變化限制在盡可能短的后綴上。

算法思想

設(shè)P是[1,n]的一個(gè)全排列。

P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn , j=max{i|Pi<Pi+1}, k=max{i|Pi>Pj} ,對換Pj,Pk,將Pj+1…Pk-1PjPk+1…Pn翻轉(zhuǎn), P’= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一個(gè)

例子:839647521的下一個(gè)排列.

從最右開始,找到第一個(gè)比右邊小的數(shù)字4(因?yàn)?<7,而7>5>2>1),再從最右開始,找到4右邊比4大的數(shù)字5(因?yàn)?>2>1而4<5),交換4、5,此時(shí)5右邊為7421,倒置為1247,即得下一個(gè)排列:839651247.用此方法寫出全排列的非遞歸算法如下

該方法支持?jǐn)?shù)據(jù)重復(fù),且在C++ STL中被采用。

(2) 遞歸法

設(shè)一組數(shù)p = {r1, r2, r3, … ,rn}, 全排列為perm(p),pn = p – {rn}。則perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), … , rnperm(pn)。當(dāng)n = 1時(shí)perm(p} = r1。

如:求{1, 2, 3, 4, 5}的全排列

1、首先看最后兩個(gè)數(shù)4, 5。 它們的全排列為4 5和5 4, 即以4開頭的5的全排列和以5開頭的4的全排列。

由于一個(gè)數(shù)的全排列就是其本身,從而得到以上結(jié)果。

2、再看后三個(gè)數(shù)3, 4, 5。它們的全排列為3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六組數(shù)。

即以3開頭的和4,5的全排列的組合、以4開頭的和3,5的全排列的組合和以5開頭的和3,4的全排列的組合.

以上的算法1的即是使用這個(gè)方法。

3組合算法

3.1 全組合

在此介紹二進(jìn)制轉(zhuǎn)化法,即,將每個(gè)組合與一個(gè)二進(jìn)制數(shù)對應(yīng)起來,枚舉二進(jìn)制的同時(shí),枚舉每個(gè)組合。如字符串:abcde

00000 <– –> null

00001<– –> e

00010 <– –> d

… …

11111 <– –> abcde

以上的算法2的即是使用這個(gè)方法。

3.2 從n中選m個(gè)數(shù)

(1) 遞歸

a. 首先從n個(gè)數(shù)中選取編號(hào)最大的數(shù),然后在剩下的n-1個(gè)數(shù)里面選取m-1個(gè)數(shù),直到從n-(m-1)個(gè)數(shù)中選取1個(gè)數(shù)為止。

b. 從n個(gè)數(shù)中選取編號(hào)次小的一個(gè)數(shù),繼續(xù)執(zhí)行1步,直到當(dāng)前可選編號(hào)最大的數(shù)為m。

下面是遞歸方法的實(shí)現(xiàn):

1 /// 求從數(shù)組a[1..n]中任選m個(gè)元素的所有組合。 2 3 /// a[1..n]表示候選集,n為候選集大小,n>=m>0。 4 5 /// b[1..M]用來存儲(chǔ)當(dāng)前組合中的元素(這里存儲(chǔ)的是元素下標(biāo)), 6 7 /// 常量M表示滿足條件的一個(gè)組合中元素的個(gè)數(shù),M=m,這兩個(gè)參數(shù)僅用來輸出結(jié)果。 8 9 void combine( int a[], int n, int m, int b[], const int M ) 10 11 { 12 13 for(int i=n; i>=m; i--) // 注意這里的循環(huán)范圍 14 15 { 16 17 b[m-1] = i - 1; 18 19 if (m > 1) 20 21 combine(a,i-1,m-1,b,M); 22 23 else // m == 1, 輸出一個(gè)組合 24 25 { 26 27 for(int j=M-1; j>=0; j--) 28 29 cout << a[b[j]] << " "; 30 31 cout << endl; 32 33 } 34 35 } 36 37 } 1 void func19(int m, int n) //非遞歸 2 { 3 int *arr; 4 int i, pos; 5 6 if (m<n) 7 { 8 return ; 9 } 10 11 arr = new int[n]; 12 13 for (i=0; i<n; ++i) 14 { 15 arr[i] = i+1; 16 } 17 18 for (i=0; i<n; ++i) 19 { 20 cout<<arr[i]<<" "; 21 } 22 cout<<endl; 23 24 pos = n-1; 25 while(1) 26 { 27 if (arr[n-1] == m ) 28 { 29 pos--; 30 } 31 else 32 { 33 pos = n-1; 34 } 35 36 arr[pos] ++; 37 38 for (i=pos+1; i<n; i++) 39 { 40 arr[i] = arr[i-1]+1; 41 } 42 43 for (i=0; i<n; ++i) 44 { 45 cout<<arr[i]<<" "; 46 } 47 cout<<endl; 48 49 if (arr[0] == m-n+1) 50 { 51 break; 52 } 53 } 54 }

?

(2) 01轉(zhuǎn)換法

本程序的思路是開一個(gè)數(shù)組,其下標(biāo)表示1到n個(gè)數(shù),數(shù)組元素的值為1表示其代表的數(shù)被選中,為0則沒選中。

首先初始化,將數(shù)組前n個(gè)元素置1,表示第一個(gè)組合為前n個(gè)數(shù)。

然后從左到右掃描數(shù)組元素值的“10”組合,找到第一個(gè)“10”組合后將其變?yōu)椤?1”組合,同時(shí)將其左邊的所有“1”全部移動(dòng)到數(shù)組的最左端。

當(dāng)?shù)谝粋€(gè)“1”移動(dòng)到數(shù)組的n-m的位置,即n個(gè)“1”全部移動(dòng)到最右端時(shí),就得到了最后一個(gè)組合。

例如求5中選3的組合:

1 1 1 0 0 //1,2,31 1 0 1 0 //1,2,41 0 1 1 0 //1,3,40 1 1 1 0 //2,3,41 1 0 0 1 //1,2,51 0 1 0 1 //1,3,50 1 1 0 1 //2,3,51 0 0 1 1 //1,4,50 1 0 1 1 //2,4,50 0 1 1 1 //3,4,5

4. 參考資料

(1) http://www.cnblogs.com/nokiaguy/archive/2008/05/11/1191914.html

(2) http://comic.sjtu.edu.cn/thucs/GD_jsj_025y/text/chapter01/sec05/default.htm

(3) http://blog.csdn.net/sharpdew/archive/2006/05/25/755074.aspx

(4) http://xiaomage.blog.51cto.com/293990/74094

以上總結(jié)源于:董的博客

轉(zhuǎn)載于:https://www.cnblogs.com/legendmaner/archive/2013/03/19/2969115.html

總結(jié)

以上是生活随笔為你收集整理的全排列函数、组合函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。