• 生成组合算法


    1.    按字典序生成组合

    1.1.算法的描述

    (1)0...0开始,当字典序为1...1时,退出

    (2)求出使得ai=0的最小整数i

    (3)1代替ai并用0代替ai-1, ai-1, ..., a0

    2.    按格雷码序生成组合

    2.1.算法描述

    (1)0...0开始,到10...0结束。

    (2)t代表代码中1的个数,若t为偶数,将最后一位变换(1001)。

    (3)t为奇数,则找到最后一个1,然后把i+1位变换。

    2.2.算法分析

    通过算法分析,可以得出以下的结论

    1. t的奇偶性是交替出现的,最开始为偶数。

    2. C语言中,可以通过与一个数,将某一位变成0,通过或一个数将某变成1.

    3. 可以通过循环右移,然后检查最后一位是否为1来找到最后一个1.

    4. 通过判断最后一位是否为(二进制)1来判断是奇数还是偶数。

    3.    生成r-组合

    3.1.算法描述

    (1)算法从1..10...0开始,到0...01...1结束。

    (2)确定最大的整数k,使得ak +1<= nak + 1不是a1, a2,..., ar

    (3)打印新的r-组合a1...ak-1, ak+1,...,ak+r-k+1

    3.2.算法分析

    1. 若将a1, a2, ..,ak也用字典序表示的话,生成r-组合就想法于将r1k-r0进行排列。由于整个过程中,1的数目和0 的数目都是不变的,可以用一个长为k的字符串来表示,字符串中有r1k-r0r1k-r0的每一个排列都对应一个组合。

    2. 引入活动的1这个概念。ai=1为活动的1仅当存在aj=0j>i。直接的说法就是仅当这个1的后面有0

    按字典序生成组合的实现

    <代码>

    // 生成字典序
    void dict_gen(int *array, int len)
    {
      
    int s = 1<<len;  // 要进行 2^n 次循环 
      for(int i=0;i<s; i++)
      
    {
        
    // 递增序列刚好对应了字典序的顺序 
        print_dict(array, len, i);
        cout
    <<endl;
      }

    }


    //显示字典序 
    void print_dict(int *array, int len, int key)
    {
      
    if(len == 0 || array == NULL)
        
    return;
      
    int curr = 1;
      std::bitset
    <8> aout(key);
      cout
    <<aout<<"";
      
    for(int i=0;i<len;i++)
      
    {
        
    // curr为当前下标对应的字典序,如下标为3对应的字典序为:1000 0000
        curr = 1 << i;
        
    // key为一个综合的字典序,如 key = 0100 1100 
        
    // 如果 key在curr位为1的话,则会打印出来 
        if(key & curr)
        
    {
          cout
    //<<setw(4)
            <<array[i]<<',';
        }

      }

    }


    按格雷码序生成组合的实现

    <代码>

    #include <iostream>
    #include 
    <bitset>
    #include 
    <stdlib.h>
    using namespace std;
    using std::bitset;

    #define BIT_WIDTH 8

    // gray 码生成器 
    void gray(int *array, int len);
    // 用于找到最后一个1的位置,如果找不到,则返回-1 
    int findlast1pos(int value);
    // 用于打印gray码 
    void print_dict(int *array, int len, int key);

    void gray(int *array, int len)
    {
      
    // 最后一个数,为: 10
      int target = (1<<len - 1);
      
    // 循环次数,为:2^n 
      int total = (1<<len);
      
    // gray序的字典序,默认从0开始 
      int dict = 0;
      
    // 相加因子,用于将其中的某一位置0或置1 
      int factor = 0;
      
    // 表示1的个数是否为奇数,0为否 
      int oddFlag = 0;
      
    int v;
      
      
    for(int i=0;i<total;i++)
      

        print_dict(array, len, dict);
        
    // 如果是最后一个数,则退出 
        if(dict == target)
          
    break;
        
        
    // 如果有偶数个1  
        if(oddFlag == 0)
        
    {
          
    if(dict & 0x01)  //如果最后一位为 1,则置 0 
            dict--;
          
    else   
            dict 
    = dict | 0x01;  // 否则置1
          oddFlag = 1;
        }

        
    else  // 如果是奇数个1 
        {
          v 
    = findlast1pos(dict);  // 找到最后一个1的位置 
          if(v == -1)
            
    break;
            
          
    // 将那一位反向(0变1,1变0) 
          factor = 1<<(v+1);  
          
    if(dict & factor)
            dict 
    = dict & (~factor); // 将第v位置0 
          else
            dict 
    |= factor; // 将第v位置1 
          oddFlag = 0;
        }

        cout
    <<endl;
      }

    }


    // 算法是:将当前值反复右移并计数,直到最低位为1 
    int findlast1pos(int value)
    {
      
    int ret = -1;
      
    while(value != 0)
      
    {
        ret
    ++;
        
    if(value & 0x01)
          
    break;
        value 
    >>= 1;
      }

      
    return ret;
    }


    //显示字典序 
    void print_dict(int *array, int len, int key)
    {
      
    if(len == 0 || array == NULL)
        
    return;
      
      
    int curr = 1;
      std::bitset
    <BIT_WIDTH> aout(key);
      cout
    <<aout<<"";
      
    for(int i=0;i<len;i++)
      
    {
        
    // curr为当前下标对应的字典序,如下标为3对应的字典序为:000 0100
        curr = 1 << i;
        
    // key为一个综合的字典序,如 key = 0100 1100 
        
    // 如果 key在curr位为1的话,则会打印出来 
        if(key & curr)
        
    {
          cout
    //<<setw(4)
            <<array[i]<<',';
        }

      }

    }


    生成r-组合的实现

    <代码>

    #define NUM_1 '1'
    #define NUM_0 '0'

    /**
    * 生成r-组合的类 
    */

    class RCombination
    {
    private:
      
    char *dict;
      
    int *array;
      
    int size;
      
    int r;
    private:
        
    void initDict();
        
    void findActive1(int &activeIndex, int &count1);
        
    void adjustDict(int activeIndex, int count1);
        
    void printDict();
    public:
      RCombination();
      
    ~RCombination();
      
    void genRComb(int *array, int size, int r);
    }
    ;

    RCombination::RCombination()
    {
        size 
    = 0;
        r 
    = 0;
        dict 
    = NULL;
    }

    RCombination::
    ~RCombination()
    {
        
    if(dict != NULL)
            delete []dict;
    }


    void RCombination::genRComb(int *array, int size, int r)
    {
        
    if(array == NULL || size == 0 || r == 0)
            
    return;
      
    if(r>size)
        
    return;
        
        
    this->array = array;
        
    this->size = size;
        
    this->= r;
     
        initDict();
        
        
    int count1 = 0;
        
    int activeInex = -1;
      
      
    do{
        cout
    <<dict<<"";
        printDict();
          cout
    <<endl;
          
          findActive1(activeInex, count1);
          adjustDict(activeInex, count1);    
        }
    while(activeInex >= 0);
    }


    // 初始化基二的字典序
    void RCombination::initDict()
    {
        
    if(dict != NULL)
            delete []dict;
        
    if(size == 0)
        
    {
            dict 
    == NULL;
            
    return;
        }

        dict 
    = new char[size + 1];
        
        
    // 使用 1100初始化字典序 
        for(int i=0;i<size;i++)
        
    {
            
    if(i<r) dict[i] = NUM_1;
            
    else    dict[i] = NUM_0;
        }

        dict[size] 
    = '\0';
    }


    // 从后往前找到第一个活动的 1 
    // 活动的 1 定义如下:只要 1 后面有 0 (并不一定是紧接) 
    void RCombination::findActive1(int &activeIndex, int &count1)
    {
        
    int meet0 = 0;
        
      count1 
    = 0;
        activeIndex 
    = -1;
        
    for(int i=size-1;i>=0;i--)
        
    {
            
    if(dict[i] == NUM_0)
            
    {
                meet0 
    = 1;
            }

            
    else
            
    {
                count1
    ++;
                
    if(meet0 == 1)
                
    {
                    activeIndex 
    = i;
                    
    break;
                }

            }

        }

    }


    // 设i是从最后面开始第1个活动的 1,将 a1, a2, , ai, ai+1,  变成
    // a1, a2,ai-1, ai+1, ai+2,  
    void RCombination::adjustDict(int activeIndex, int count1)
    {
        
    if(activeIndex >= 0)
        
    {
            dict[activeIndex
    ++= NUM_0;
            
    for(;activeIndex < size; activeIndex++, count1--)
            
    {
                
    if(count1 <= 0)
                    dict[activeIndex] 
    = NUM_0;
                
    else
                    dict[activeIndex] 
    = NUM_1;
            }

        }

    }

    void RCombination::printDict()
    {
        
    if(array == NULL)
            
    return;
        
    for(int i=0;i<size;i++)
        
    {
            
    if(dict[i] == NUM_1)
                cout
    <<array[i]<<',';
        }

    }

    终于贴完了……
  • 相关阅读:
    POJ2888 Magic Bracelet [矩阵快速幂+Burnside+欧拉函数]
    数列的 GCD [计数问题]
    com组件的注册
    WCF 传输和接受大数据
    数据库中已存在名为 'View_Business' 的对象。
    windows 两个用户,默认其中一个用户登录
    用C#读取,写入ini文件
    小心得,关于串口
    未能加载文件或程序集"Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad
    无法激活服务,因为它不支持 ASP.NET 兼容性
  • 原文地址:https://www.cnblogs.com/qkhh/p/1053230.html
Copyright © 2020-2023  润新知