排列組合算法


    排列:從n個不同元素中,任取m(m<=n)個元素按照一定的順序排成一列,叫做從n個不同元素中取出m個元素的一個排列;從n個不同元素中取出m(m<=n)個元素的所有排列的個數,叫做從n個不同元素中取出m個元素的排列數,用符號A(n,m)表示。 A(n,m)=n(n-1)(n-2)……(n-m+1)= n!/(n-m)! 此外規定0!=1

    組合:從n個不同元素中,任取m(m<=n)個元素並成一組,叫做從n個不同元素中取出m個元素的一個組合;從n個不同元素中取出m(m<=n)個元素的所有組合的個數,叫做從n個不同元素中取出m個元素的組合數。用符號C(n,m) 表示。 C(n,m)=A(n,m)/m!=n!/((n-m)!*m!);  C(n,m)=C(n,n-m)。

C語言使用標志位實現

復制代碼
#include <iostream>
using namespace std;

#define MaxN 10
char used[MaxN];
int p[MaxN];
char s[MaxN];

//從n個元素中選r個進行排列
void permute(int pos,const int n,const int r)
{
    int i;
    /*如果已是第r個元素了,則可打印r個元素的排列 */
    if(pos == r)
    {
        for(i=0; i<r; i++)
            cout<<s[p[i]];
        cout<<endl;
        return;
    }
    for (i=0; i<n; i++)
    {
        if(!used[i])
        {
            /*如果第i個元素未用過*/
            /*使用第i個元素,作上已用標記,目的是使以后該元素不可用*/
            used[i] = 1;
            /*保存當前搜索到的第i個元素*/
            p[pos] = i;
            /*遞歸搜索*/
            permute(pos+1,n,r);
            /*恢復遞歸前的值,目的是使以后改元素可用*/
            used[i] = 0;
        }
    }
}

//從n個元素中選r個進行組合
void combine(int pos,int h,const int n,const int r)
{
    int i;
    /*如果已選了r個元素了,則打印它們*/
    if (pos == r)
    {
        for(i=0; i<r; i++)
            cout<<s[p[i]];
        cout<<endl;
        return;
    }
    for(i=h; i<=n-r+pos; i++) /*對於所有未用的元素*/
    {
        if (!used[i])
        {
            /*把它放置在組合中*/
            p[pos] = i;
            /*使用該元素*/
            used[i] = 1;
            /*搜索第i+1個元素*/
            combine(pos+1,i+1,n,r);
            /*恢復遞歸前的值*/
            used[i] = 0;
        }
    }
}

//產生0~2^r-1的二進制序列
void binary_sequence(int pos,const int r)
{
    int  i;
    if(pos == r)
    {
        for(i=0; i<r; i++)
            cout<<p[i];
        cout<<endl;
        return;
    }
    p[pos] = 0;
    binary_sequence(pos+1,r);
    p[pos] = 1;
    binary_sequence(pos+1,r);
}

//利用上面的二進制序列打印字符串的所有組合
//如"abc"輸出a、b、c、ab、ac、bc、abc。
void all_combine(int pos,const int r)
{
    int  i;
    if(pos == r)
    {
        for(i=0; i<r; i++)
        {
            if(p[i]==1)
                cout<<s[i];
        }
        cout<<endl;
        return;
    }
    p[pos] = 0;
    all_combine(pos+1,r);
    p[pos] = 1;
    all_combine(pos+1,r);
}

//利用r進制序列打印字符串的所有重復組合
//如"abc"輸出aaa、aab、aac、aba、abb、abc、aca、acb、acc...。
void repeative_combine(int pos,const int r)
{
    int  i;
    if(pos == r)
    {
        for(i=0; i<r; i++)
        {
            cout<<s[p[i]];
        }
        cout<<endl;
        return;
    }
    for(i=0; i<r; ++i)
    {
        p[pos] = i;
        repeative_combine(pos+1,r);
    }
}

int main()
{
    strcpy(s,"ABC");
    int n = 3;
    int r = 3;
    //permute(0,n,r);
    //combine(0,0,n,r);
    //binary_sequence(0,r);
    //cout<<"string: "<<s<<endl;
    //all_combine(0,r);
    //repeative_combine(0,r);
    return 0;
}
復制代碼

排列組合算法的遞歸實現:

復制代碼
#include <iostream>
using namespace std;

template <class Type>
void permute(Type a[], int start, int end)
{
    if(start == end)
    {
        for(int i = 0; i <= end; ++i)
        {
            cout<<a[i]<<" ";
        }
        cout<<endl;
    }
    else
    {
        for(int i = start; i <= end; ++i)
        {
            swap(a[i],a[start]);
            permute(a,start+1,end);
            swap(a[i],a[start]);
        }
    }
}

template <class Type>
void combine(Type a[], bool b[], int start, int end)
{
    if(start > end)
    {
        for(int i = 0; i <= end; ++i)
        {
            if(b[i])
                cout<<a[i]<<" ";
        }
        cout<<endl;
    }
    else
    {
        b[start] = true;
        combine(a,b,start+1,end);
        b[start] = false;
        combine(a,b,start+1,end);
    }
}

int main()
{
    int p[3]={1,2,3};
    int N = 3;
    cout<<"permute:"<<endl;
    permute(p,0,N-1);
    cout<<"combine:"<<endl;
    bool b[3];
    combine(p,b,0,N-1);

    return 0;
}
復制代碼

排列算法的迭代實現

C++ STL中提供了next_permutationprev_permutation算法。因為next_permutationprev_permutation實際上是一樣的,因此只描述next_permutation算法。next_permutation()函數的作用是取下一個排列組合。考慮{a,b,c}的全排列:abc,acb,bac,bca,cab,cba,以“bac”作為參考,那么next_permutation()所得到的下一個排列組合是bca,prev_permutation()所得到的前一個排列組合是“acb”,之於“前一個”和“后一個”,是按字典進行排序的。

next_permutation()算法描述:

  1. 從str的尾端開始逆着尋找相鄰的元素,*i和*ii,滿足*i<*ii;
  2. 接着,又從str的尾端開始逆着尋找一元素,*j,滿足*i>*j(*i從步驟一中得到);
  3. swap(*i,*j);
  4. 將*ii之后(包括*ii)的所有元素逆轉。

舉個例子,需要找到“01324”的下一個排列,找到*i=2,*ii=4,*j=4,下一個排列即“01342”。再來找到“abfedc”的下一個排列,找到*i=b,*ii=f,*j=c,swap操作過后為“acfedb”,逆轉操作過后為“acbdef”。

復制代碼
//求階乘
int factorial(int n)
{
    if(n == 1)  return 1;
    return n*factorial(n-1);
}

template <class Type>
void print(Type a, int n)
{
    for(int i = 0; i < n; ++i)
        cout<<a[i]<<" ";
    cout<<endl;
}

template <class Type>
void perm2(Type a, int n)
{
    int i,ii,j;
    int cnt = 1;
    print(a,n);
    int num = factorial(n);

    // STL <algorithm> next_permutation()函數的核心算法
    while(++cnt <= num)
    {
        i = n - 2;
        ii = n - 1;
        j = ii;
        while(a[i] >= a[ii]) --i,--ii;   //find *i and *ii
        while(a[i] >= a[j])  --j;        //find *j
        swap(a[i],a[j]);     //STL swap
        reverse(a+ii,a+n);   //STL reverse
        print(a,n);
    }
}
復制代碼

     排列:從n個不同元素中,任取m(m<=n)個元素按照一定的順序排成一列,叫做從n個不同元素中取出m個元素的一個排列;從n個不同元素中取出m(m<=n)個元素的所有排列的個數,叫做從n個不同元素中取出m個元素的排列數,用符號A(n,m)表示。 A(n,m)=n(n-1)(n-2)……(n-m+1)= n!/(n-m)! 此外規定0!=1

    組合:從n個不同元素中,任取m(m<=n)個元素並成一組,叫做從n個不同元素中取出m個元素的一個組合;從n個不同元素中取出m(m<=n)個元素的所有組合的個數,叫做從n個不同元素中取出m個元素的組合數。用符號C(n,m) 表示。 C(n,m)=A(n,m)/m!=n!/((n-m)!*m!);  C(n,m)=C(n,n-m)。

C語言使用標志位實現

復制代碼
#include <iostream>
using namespace std;

#define MaxN 10
char used[MaxN];
int p[MaxN];
char s[MaxN];

//從n個元素中選r個進行排列
void permute(int pos,const int n,const int r)
{
    int i;
    /*如果已是第r個元素了,則可打印r個元素的排列 */
    if(pos == r)
    {
        for(i=0; i<r; i++)
            cout<<s[p[i]];
        cout<<endl;
        return;
    }
    for (i=0; i<n; i++)
    {
        if(!used[i])
        {
            /*如果第i個元素未用過*/
            /*使用第i個元素,作上已用標記,目的是使以后該元素不可用*/
            used[i] = 1;
            /*保存當前搜索到的第i個元素*/
            p[pos] = i;
            /*遞歸搜索*/
            permute(pos+1,n,r);
            /*恢復遞歸前的值,目的是使以后改元素可用*/
            used[i] = 0;
        }
    }
}

//從n個元素中選r個進行組合
void combine(int pos,int h,const int n,const int r)
{
    int i;
    /*如果已選了r個元素了,則打印它們*/
    if (pos == r)
    {
        for(i=0; i<r; i++)
            cout<<s[p[i]];
        cout<<endl;
        return;
    }
    for(i=h; i<=n-r+pos; i++) /*對於所有未用的元素*/
    {
        if (!used[i])
        {
            /*把它放置在組合中*/
            p[pos] = i;
            /*使用該元素*/
            used[i] = 1;
            /*搜索第i+1個元素*/
            combine(pos+1,i+1,n,r);
            /*恢復遞歸前的值*/
            used[i] = 0;
        }
    }
}

//產生0~2^r-1的二進制序列
void binary_sequence(int pos,const int r)
{
    int  i;
    if(pos == r)
    {
        for(i=0; i<r; i++)
            cout<<p[i];
        cout<<endl;
        return;
    }
    p[pos] = 0;
    binary_sequence(pos+1,r);
    p[pos] = 1;
    binary_sequence(pos+1,r);
}

//利用上面的二進制序列打印字符串的所有組合
//如"abc"輸出a、b、c、ab、ac、bc、abc。
void all_combine(int pos,const int r)
{
    int  i;
    if(pos == r)
    {
        for(i=0; i<r; i++)
        {
            if(p[i]==1)
                cout<<s[i];
        }
        cout<<endl;
        return;
    }
    p[pos] = 0;
    all_combine(pos+1,r);
    p[pos] = 1;
    all_combine(pos+1,r);
}

//利用r進制序列打印字符串的所有重復組合
//如"abc"輸出aaa、aab、aac、aba、abb、abc、aca、acb、acc...。
void repeative_combine(int pos,const int r)
{
    int  i;
    if(pos == r)
    {
        for(i=0; i<r; i++)
        {
            cout<<s[p[i]];
        }
        cout<<endl;
        return;
    }
    for(i=0; i<r; ++i)
    {
        p[pos] = i;
        repeative_combine(pos+1,r);
    }
}

int main()
{
    strcpy(s,"ABC");
    int n = 3;
    int r = 3;
    //permute(0,n,r);
    //combine(0,0,n,r);
    //binary_sequence(0,r);
    //cout<<"string: "<<s<<endl;
    //all_combine(0,r);
    //repeative_combine(0,r);
    return 0;
}
復制代碼

排列組合算法的遞歸實現:

復制代碼
#include <iostream>
using namespace std;

template <class Type>
void permute(Type a[], int start, int end)
{
    if(start == end)
    {
        for(int i = 0; i <= end; ++i)
        {
            cout<<a[i]<<" ";
        }
        cout<<endl;
    }
    else
    {
        for(int i = start; i <= end; ++i)
        {
            swap(a[i],a[start]);
            permute(a,start+1,end);
            swap(a[i],a[start]);
        }
    }
}

template <class Type>
void combine(Type a[], bool b[], int start, int end)
{
    if(start > end)
    {
        for(int i = 0; i <= end; ++i)
        {
            if(b[i])
                cout<<a[i]<<" ";
        }
        cout<<endl;
    }
    else
    {
        b[start] = true;
        combine(a,b,start+1,end);
        b[start] = false;
        combine(a,b,start+1,end);
    }
}

int main()
{
    int p[3]={1,2,3};
    int N = 3;
    cout<<"permute:"<<endl;
    permute(p,0,N-1);
    cout<<"combine:"<<endl;
    bool b[3];
    combine(p,b,0,N-1);

    return 0;
}
復制代碼

排列算法的迭代實現

C++ STL中提供了next_permutationprev_permutation算法。因為next_permutationprev_permutation實際上是一樣的,因此只描述next_permutation算法。next_permutation()函數的作用是取下一個排列組合。考慮{a,b,c}的全排列:abc,acb,bac,bca,cab,cba,以“bac”作為參考,那么next_permutation()所得到的下一個排列組合是bca,prev_permutation()所得到的前一個排列組合是“acb”,之於“前一個”和“后一個”,是按字典進行排序的。

next_permutation()算法描述:

  1. 從str的尾端開始逆着尋找相鄰的元素,*i和*ii,滿足*i<*ii;
  2. 接着,又從str的尾端開始逆着尋找一元素,*j,滿足*i>*j(*i從步驟一中得到);
  3. swap(*i,*j);
  4. 將*ii之后(包括*ii)的所有元素逆轉。

舉個例子,需要找到“01324”的下一個排列,找到*i=2,*ii=4,*j=4,下一個排列即“01342”。再來找到“abfedc”的下一個排列,找到*i=b,*ii=f,*j=c,swap操作過后為“acfedb”,逆轉操作過后為“acbdef”。

復制代碼
//求階乘
int factorial(int n)
{
    if(n == 1)  return 1;
    return n*factorial(n-1);
}

template <class Type>
void print(Type a, int n)
{
    for(int i = 0; i < n; ++i)
        cout<<a[i]<<" ";
    cout<<endl;
}

template <class Type>
void perm2(Type a, int n)
{
    int i,ii,j;
    int cnt = 1;
    print(a,n);
    int num = factorial(n);

    // STL <algorithm> next_permutation()函數的核心算法
    while(++cnt <= num)
    {
        i = n - 2;
        ii = n - 1;
        j = ii;
        while(a[i] >= a[ii]) --i,--ii;   //find *i and *ii
        while(a[i] >= a[j])  --j;        //find *j
        swap(a[i],a[j]);     //STL swap
        reverse(a+ii,a+n);   //STL reverse
        print(a,n);
    }
}
復制代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM