【离散数学】求一个n阶群的全部子群(代码实现)

这是大二上离散数学的结课大作业。当时写这个作业比较困难,主要是因为网上没有类似的东西,最后是通过求助学长得到的解决方式(感谢学长)。

所以把自己的报告和代码(C/C++)放上来供大家参考。如有疏漏和错误,欢迎讨论纠正。

  1. 思路概述

  1. 输入群

  1. 输入群的阶数n。

  1. 输入群中所有元素之间相互运算的结果。

  1. 判断群是否为循环群

  1. 根据“素数阶群都是循环群”判断该群是否是循环群,即若n是素数,则该群为循环群,否则需要通过进一步判断确定该群是否是循环群。

  1. 若n不是素数,则寻找该群是否存在一个四阶子群与克莱因四元群同构。由于循环群的子群都是循环群,所以若存在满足上述条件的子群,该群为非循环群。

  1. 若同时满足n不是素数且该群不存在与克莱因四元群同构的子群,则通过枚举判断该群是否存在一个种子生成元。若存在,则该群是循环群,否则该群是非循环群。

  1. 求解所有子群

  1. 若该群为循环群,则利用定理10.14求解所有子群。若G=<a>是n阶循环群,则对n的每个正因子d,G恰好含有一个d 阶子群。

  1. 若该群为非循环群,则通过枚举遍历的方式求解所有子群。首先求解包含群中每一个元素和单位元的最小子群,然后将所有子群进行合并求出新子群,直至不再产生新的子群。此时所得到的子群就是该群的所有子群。

  1. 算法实现

  1. 全局变量

在算法实现过程中,我使用了一些全局变量。下面将对全局变量的含义作简单解释,以便对后续算法的理解。

        int group[SIZE][SIZE]={0};  
        //group[][]二维数组用来存储群中所有元素的运算结果  
        //例:group[1][2]代表1*2的运算结果  
        int n=0;  
        //n代表群的阶数  
        int e=0;  
        //e代表群的单位元  
        int scy=0;  
        //若n阶群是循环群,则scy代表循环群的生成元  
        //若n阶群是非循环群,则该变量不被使用  
        int mg[SIZE][SIZE]={0};  
        //若n阶群是循环群,则该变量不被使用  
        //若n阶群是非循环群,则mg[a][b]代表非循环群在求解得到的所有子群中,
        //第a个子群中的第b个元素  
  1. 功能性函数

在算法实现过程中,有部分函数使用较为广泛,在正式使用算法求解问题之前,我先对这些函数作介绍,以便后续代码的理解。

  1. 判断一个元素是否存在于一个数组中

        int in(int t,int mem[])  
        {  
        //判断t是否存在于mem数组中  
        //若t存在于mem[]数组中return 1  
        //否则return 0  
            for(int i=1;mem[i]!=0;i++)  
            {  
                if(t==mem[i])return 1;  
            }  
            return 0;  
        }   
  1. 判断两个群是否相等

        int g_equal(int a,int b)  
        {  
        //判断mg[a][]与mg[b][]两个有序数组代表的两个群是否相等  
        //若相等则return 1  
        //否则return 0  
            for(int i=1;;i++)  
            {  
                if(mg[a][i]==0&&mg[b][i]==0)return 1;  
                else  
                {  
                    if(mg[a][i]!=mg[b][i])return 0;  
                }  
            }  
        } 
  1. 主函数

主函数主要分为四部分:

  1. 输入群

  1. 找到群的单位元e

  1. 判断群是否为循环群

  1. 针对n阶群是循环群和不是循环群两种情况分别求解所有子群

        int main()  
        {  
        input(); //输入群  
        find_e();//找到群的单位元e  
        int cycle=cycle_or_no();  
        //判断输入的n阶群是否是循环群,若是,则cycle=1,否则cycle=0  
            if(cycle==1)  
            {  
                //如果群是循环群  
                //根据定理10.14求所有子群  
                cycle_sub_group();  
            }  
            else//如果是非循环群  
            {  
                no_cycle_sub_group(); //求非循环群的子群  
            }  
            return 0;  
        }
  1. 输入群

首先输入群的阶数n。

为了表达以及计算方便,我们将群中n个元素分别用1,2,3...,n表示,并且设群的运算为*。

那么我们依次输入:

1*1,1*2,...,1*n,

2*1,2*2...,2*n,

...

n*1,n*2,...,n*n

的结果,即可完成群的输入。

        void input()  
        {  
        //输入群  
        //首先输入阶数n  
        //然后输入群中所有元素的运算结果,并将结果存在group[][]数组中  
            scanf("%d",&n)  
            for(int i=1;i<=n;i++)  
            {  
                for(int j=1;j<=n;j++)  
                {  
                    scanf("%d",&group[i][j]);  
                }  
            }  
        }
  1. 找到群的单位元e

遍历群中所有元素,寻找是否存在一个元素,能够使得其与其他元素运算得结果都与其他元素相等,即对群中任意元素x,若group[e][x]==x&&group[x][e]==x,则e为该群的单位元。

        void find_e()  
        {  
        //找得群的单位元e  
            for(int i=1;i<=n;i++)  
            {  
                for(int j=1;j<=n+1;j++)  
                {  
                    if(j==n+1)  
                    {  
                        e=i;  
                        return;  
                    }  
                    if(group[i][j]!=j||group[j][i]!=j)  
                    {  
                        break;  
                    }  
                }  
            }  
        } 
  1. 判断群是否为循环群

  1. 根据“素数阶群都是循环群”判断该群是否是循环群,即若n是素数,则该群为循环群,否则需要通过进一步判断确定该群是否是循环群。

        int prime(int n)
        {
        //判断n是否是素数
        //若是质数则return 1
        //否则return 0
            for(int i=2;i<=sqrt(n);i++)
            {
                if(n%i==0)return 0;
            }
            return 1;
        }
  1. 若n不是素数,则寻找该群是否存在一个四阶子群与克莱因四元群同构。由于循环群的子群都是循环群,所以若存在满足上述条件的子群,该群为非循环群。

判断该群是否有与克莱因四元群同构的子群:

  1. 首先筛选出所有阶为2的元素。

  1. 若阶为2的元素小于三个则不存在,若大于等于三个则继续判断。

  1. 任选3个阶为2的元素,设3个元素分别为a,b,c,若存在一组a,b,c满足a*b=b*a=c且a*c=c*a=b且b*c=c*b=a,则该四阶群与克莱因四元群同构,即n阶群有与克莱因四元群同构的子群。

        int same_with_klein()
        {
        //判断n阶群是否存在与克莱因四元群同构的子群
        //若存在return 1
        //否则return 0
        //首先筛选出所有阶为2的元素
            int grade_is_2[SIZE]={0},cnt=0;
            for(int i=1;i<=n;i++)
            {
                if(group[i][i]==e&&i!=e)grade_is_2[++cnt]=i;
            }
            //构造克莱因群的同构;
            if(cnt<3)return 0;
            for(int i=1;i<=cnt;i++)
            {
                for(int j=i+1;j<=cnt&&j!=i;j++)
                {
                    for(int k=j+1;k<=cnt&&k!=j&&k!=i;k++)
                    {
                        if(group[grade_is_2[i]][grade_is_2[j]]==grade_is_2[k]&&group[grade_is_2[j]][grade_is_2[i]]==grade_is_2[k]&&
                            group[grade_is_2[i]][grade_is_2[k]]==grade_is_2[j]&&group[grade_is_2[k]][grade_is_2[i]]==grade_is_2[j]&&
                            group[grade_is_2[j]][grade_is_2[k]]==grade_is_2[i]&&group[grade_is_2[k]][grade_is_2[j]]==grade_is_2[i])
                            return 1;
                    }
                }
            }
            return 0;
        }
  1. 若同时满足n不是素数且该群不存在与克莱因四元群同构的子群,则通过枚举判断该群是否存在一个种子生成元。若存在,则该群是循环群,否则该群是非循环群。

判断n阶群是否存在一个种子生成元:

  1. 枚举群中的所有元素,检验该元素是否为一个生成元;

  1. 将该元素作为生成元生成一个群,若生成的群中包含输入的n阶群中的所有元素,那么该元素是该n阶群的生成元。

  1. 如果所有元素都不是n阶群的生成元,则该n阶群没有种子生成元。

        int is_scy(int x)
        {
            //判断x是否是n阶群的种子生成元
            //若是return 1
            //否则return 0
            int exist[256]={0};
            int t=x;
            for(int i=1;i<=n;i++)
            {
                exist[group[t][x]]=1;
                t=group[t][x];
            }
            for(int i=1;i<=n;i++)
            {
                if(exist[i]==0)return 0;
            }
            return 1;
        }
  1. 判断群是否是循环群的整体算法

        int cycle_or_no()
        {
            //判断n阶群是否是循环群
            //若是循环群return 1
            //否则return 0
            if(prime(n)==1)return 1;//判断n是否为素数
            if(same_with_klein()==1)return 0;
                //判断是否有和克莱因四元群同构的子群
            //穷举寻找生成元
            for(int i=1;i<=n;i++)
            {
                if(is_scy(i)==1)
                {
                    scy=i;
                    return 1;
                }
            }
            return 0;
        }
  1. 求解所有子群

  1. 若该群为循环群,则利用定理10.14求解所有子群。

  1. 首先找到n的所有正因数d。

  1. 寻找群中阶数为n/d的元素作为d阶子群的生成元。

  1. 将n/d阶元素作为生成元求得d阶子群。

        void cycle_sub_group()
        {
            //找出所有的d阶子群
            //根据定理10.14求所有子群
            int cnt=0;
            for(int i=1;i<=n;i++)
            {
                if(n%i==0)
                    find_d_sub_group(i,++cnt);
        //求i阶子群,且该子群为求得的第cnt+1个子群
            }
            //输出一个只有单位元的子群
            printf("子群中的元素有:{%d}\n",e);
        }
        void find_d_sub_group(int d,int num)
        {
            //求n/d阶生成元产生的第num个子群并输出
            int mem[SIZE]={0};
            int t;
            int i,cnt=0;
            for(i=1;i<=n;i++)
            {
                //寻找n/d阶生成元
                if(i!=e)t=i;//此处不求只包含单位元的子群
                else continue;
                int j=0;
                for(j=1;j<n/d;j++)
                {
                    t=group[t][i];
                    if(j<n/d-1&&t==e)break;
        //避免求n/d阶群时,n/d-k阶群也满足要求的情况
                }
                if(t==e&&j==n/d)break;
            }
            //此时i是n/d阶生成元
            if(i>n)return ;//如果没找到符合要求的生成元,那么i>n
            t=i;
            mem[++cnt]=e;
            while(in(t,mem)!=1)//用生成元生成子群
            {
                mem[++cnt]=t;
                t=group[t][i];
        }
        //输出子群
            printf("子群中的元素有:{");
            for(int i=1;i<=cnt;i++)
            {
                printf("%d",mem[i]);
                if(i!=cnt)printf(",");
            }
            printf("}\n");
        }
  1. 若该群为非循环群,则通过枚举遍历的方式求解所有子群。

  1. 首先求解包含群中每一个元素和单位元的最小子群,在每次得到一个子群之后,都要和除它自己以外其他所有子群进行比较,若其与已有子群相同,那么进行去重,即删除该子群。

        void find_every_min_group()
        {
            //寻找包含每个元素的最小的群
            for(int i=1;i<=n;i++)
            {
                find_min_group(i);
            }
        }
        void find_min_group(int i)
        {
        //寻找包含元素i的最小的群
            int cnt=0;//cnt相当于一个指针,指示最后一个群的位置
            int t=i;
        mg[i][++cnt]=e;//首先把单位元放进去,因为每个子群里必定有单位元
        //mg[i]表示包含元素i的最小子群,mg是min_group的简写
            if(i==e)return;//如果这个元素是单位元则里面只有单位元直接返回
            mg[i][++cnt]=i;
            //一开始集合里只有这个元素本身和单位元
            //接从前到后遍历这个集合
            //每次把当前元素和它前面的所有元素(包含它自己)乘一遍,
            //如果出现了新元素就添加到集合的后面
            //遍历完就得到了一个最小的子群
            int now=1;
            for(;now<=cnt;now++)
            {
                for(int k=1;k<=now;k++)
                {
                    if(in(group[mg[i][now]][mg[i][k]],mg[i])!=1)
                    {
                        mg[i][++cnt]=group[mg[i][now]][mg[i][k]];
                    }
                    if(in(group[mg[i][k]][mg[i][now]],mg[i])!=1)
                    {
                        mg[i][++cnt]=group[mg[i][k]][mg[i][now]];
                    }
                }
            }
            sort(mg[i]+1,mg[i]+cnt+1);//排序以便判断群是否相等时的比较
        }
            2. 在求得所有的最小子群后,将所有子群进行合并求出新子群并将新子群进行去重处理,直至不再产生新的子群。此时所得到的子群就是该群的所有子群。
        void col(int a,int b,int num)
        {
            //将mg[a][]和mg[b][]两个群合并到mg[num][]中
            //首先把a、b中的所有元素提取出来
            int ab[SIZE]={0};
            int i=0;
            for(i=1;mg[a][i]!=0;i++)
            {
                ab[i]=mg[a][i];
            }
            int j=1;
            while(mg[b][j]!=0)
            {
                if(in(mg[b][j],ab)!=1)ab[i++]=mg[b][j];//元素的去重
                j++;
            }
            sort(ab+1,ab+i);//排序以便比较两个群是否相等
            for(int ii=1;ab[ii]!=0;ii++)
            {
                mg[num][ii]=ab[ii];//将数组ab[]复制到mg[num][]中
            }
            //下面这一段是把所有元素进行运算然后求出子群
            i--;
            int now=1;
            for(;now<=i;now++)
            {
                for(int k=1;k<=now;k++)
                {
                    if(i>=n)
                            //当子群中的元素数目已经大于等于n,那么代表该群不可扩展
                    {
                        sort(mg[num]+1,mg[num]+i+1);
                        return;
                    }
                    if(in(group[mg[num][now]][mg[num][k]],mg[num])!=1)
                    {
                        mg[num][++i]=group[mg[num][now]][mg[num][k]];
                    }
                    if(in(group[mg[num][k]][mg[num][now]],mg[num])!=1)
                    {
                        mg[num][++i]=group[mg[num][k]][mg[num][now]];
                    }
                }
            }
            sort(mg[num]+1,mg[num]+i+1);
        }
  1. 求非循环群的所有子群

        void no_cycle_sub_group()
        {
            //求非循环群的所有子群
            //先找包含每个元素的最小的群
            find_every_min_group();
            //对最小群去重
            for(int i=1;i<=n;i++)
            {
                for(int j=i+1;j<=n;j++)
                {
                    if(i==j)continue;
                    int xiangdeng=g_equal(i,j);
                    if(xiangdeng==1)
                    {
                        for(int k=1;mg[j][k]!=0;k++)
                        {
                            mg[j][k]=0;
                        }
                    }
                }
            }
            //每次再选择两个群合并,
            //新群如果没出现过就添加到群的集合
            //最后就能得到所有子群
            int sum=n;//合并群的总数
            int same=0;
            for(int i=1;i<=sum;i++)
            {
                for(int j=i+1;j<=sum;j++)
                {
                    if(i!=j)
                    {
                        col(i,j,sum+1);
                        same=0;
                        for(int k=1;k<=sum;k++)
                        {
                            if(g_equal(k,sum+1)==1)//去重
                            {
                                same=1;
                                for(int ii=1;mg[sum+1][ii]!=0;ii++)
                                {
                                    mg[sum+1][ii]=0;
                                }
                                break;
                            }
                        }
                        if(same==0)sum++;
                    }
                }
            }
            //输出所有子群
            int cnt=0;
            for(int i=1;i<=sum;i++)
            {
                if(mg[i][1]==0)continue;
                else print_sub_group_of_no_cycle(i,++cnt);
            }
        }
        void print_sub_group_of_no_cycle(int x,int cnt)
        {
            //输出mg[x][]所代表的子群,且这个子群是求得的第cnt个子群
            printf("第%d个子群中的元素有{",cnt);
            for(int i=1;mg[x][i]!=0;i++)
            {
                printf("%d",mg[x][i]);
                if(mg[x][i+1]!=0)
                    printf(",");
            }
            printf("}\n");
        }

附:参考用例和运行结果截图

附:完整代码

#include <bits/stdc++.h>
using namespace std;
#define SIZE 1000
int group[SIZE][SIZE]={0};
int n=0;
int e=0;
int scy=0;
int mg[SIZE][SIZE]={0};//非循环群包含每个元素的最小的群
void input()
{
    /*
        输入群中的元素以及运算结果
        首先输入阶数n
        最后输入n*n个元素,表示运算的结果
    */
    printf("输入元素的阶数n(n<=250):");
    scanf("%d",&n);
    printf("在这里我们分别将n个元素表示为:1,2,3,4,...,n,我们假设群的运算为*,");
    printf("\n那么请你依次输入\n1*1,1*2,...,1*n,\n2*1,2*2...,2*n,\n...\nn*1,n*2,...,n*n\n的结果.\n");
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&group[i][j]);
        }
    }
}
int prime(int n)
{
    //判断n是否是质数
    for(int i=2;i<=sqrt(n);i++)
    {
        if(n%i==0)return 0;
    }
    return 1;
}
void find_e()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n+1;j++)
        {
            if(j==n+1)
            {
                e=i;
                return;
            }
            ///
            //printf("%d\n",group[i][j]);
            if(group[i][j]!=j||group[j][i]!=j)
            {
                break;
            }
        }
    }
}
int same_with_klein()
{
    //首先筛选出所有阶为2的元素
    int grade_is_2[SIZE]={0},cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(group[i][i]==e&&i!=e)grade_is_2[++cnt]=i;
    }
    //构造克莱因群的同构;
    if(cnt<3)return 0;
    for(int i=1;i<=cnt;i++)
    {
        for(int j=i+1;j<=cnt&&j!=i;j++)
        {
            for(int k=j+1;k<=cnt&&k!=j&&k!=i;k++)
            {
                if(group[grade_is_2[i]][grade_is_2[j]]==grade_is_2[k]&&group[grade_is_2[j]][grade_is_2[i]]==grade_is_2[k]&&
                    group[grade_is_2[i]][grade_is_2[k]]==grade_is_2[j]&&group[grade_is_2[k]][grade_is_2[i]]==grade_is_2[j]&&
                    group[grade_is_2[j]][grade_is_2[k]]==grade_is_2[i]&&group[grade_is_2[k]][grade_is_2[j]]==grade_is_2[i])
                    return 1;
            }
        }
    }
    return 0;
}
int is_scy(int x)
{
    int exist[256]={0};
    int t=x;
    for(int i=1;i<=n;i++)
    {
        exist[group[t][x]]=1;
        t=group[t][x];
    }
    for(int i=1;i<=n;i++)
    {
        if(exist[i]==0)return 0;
    }
    return 1;
}
int cycle_or_no()
{
    if(prime(n)==1)return 1;
    if(same_with_klein()==1)return 0;//判断是否有和克莱因群同构的子群
    //穷举寻找生成元
    for(int i=1;i<=n;i++)
    {
        if(is_scy(i)==1)
        {
            scy=i;
            return 1;
        }
    }
    return 0;
}
int in(int t,int mem[])
{
    //判断t在不在mem数组中
    for(int i=1;mem[i]!=0;i++)
    {
        if(t==mem[i])return 1;
    }
    return 0;
}
void find_d_sub_group(int d,int num)
{
    //求n/d阶生成元产生的子群
    /*
    定理10.14 设G=<a>是循环群.
    (1) G的子群仍是循环群.
    (3) 若G=<a>是n阶循环群,则对n的每个正因子d,
    eG恰好含有一个d 阶子群.那么生成元的阶数是n/d.
    */
    //根据以上定理求所有子群
    int mem[SIZE]={0};
    int t;
    int i,cnt=0;
    //printf("e=%d\n",e);
    for(i=1;i<=n;i++)
    {
        //寻找n/d阶生成元
        if(i!=e)t=i;
        else continue;
        int j=0;
        for(j=1;j<n/d;j++)
        {
            t=group[t][i];
            if(j<n/d-1&&t==e)break;
        }
        if(t==e&&j==n/d)break;//这个地方不对
    }
    //此时i就是那个生成元
    if(i>n)return ;
    t=i;
    mem[++cnt]=e;
    while(in(t,mem)!=1)
    {
        mem[++cnt]=t;
        t=group[t][i];
    }
    printf("子群中的元素有:{");
    for(int i=1;i<=cnt;i++)
    {
        printf("%d",mem[i]);
        if(i!=cnt)printf(",");
    }
    printf("}\n");
}
void cycle_sub_group()
{
    //找出所有的d阶子群
    //如果群是循环群
    /*
    定理10.14 设G=<a>是循环群.
    (1) G的子群仍是循环群.
    (3) 若G=<a>是n阶循环群,则对n的每个正因子d,
    eG恰好含有一个d 阶子群.那么生成元的阶数是n/d.
    */
    //根据以上定理求所有子群
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(n%i==0)
            find_d_sub_group(i,++cnt);
    }
    //输出一个只有单位元的子群
    printf("子群中的元素有:{%d}\n",e);
}
void find_min_group(int i)
{
    int cnt=0;
    int t=i;
    mg[i][++cnt]=e;
    if(i==e)return;//如果这个元素是单位元则里面只有单位元直接返回
    mg[i][++cnt]=i;
    //一开始集合里只有这个元素本身和单位元
    //接从前到后遍历这个集合
    //每次把当前元素和它前面的所有元素(包含它自己)乘一遍,
    //如果出现了新元素就添加到集合的后面
    //遍历完就得到了一个最小的子群
    int now=1;
    for(;now<=cnt;now++)
    {
        for(int k=1;k<=now;k++)
        {
//            printf("此时包含%d的最小的群中的元素有:",i);
//            for(int ii=1;mg[i][ii]!=0;ii++)printf("%d,",mg[i][ii]);
//            printf("\n");
            if(in(group[mg[i][now]][mg[i][k]],mg[i])!=1)
            {
                mg[i][++cnt]=group[mg[i][now]][mg[i][k]];
            }
            if(in(group[mg[i][k]][mg[i][now]],mg[i])!=1)
            {
                mg[i][++cnt]=group[mg[i][k]][mg[i][now]];
            }
        }
    }
    sort(mg[i]+1,mg[i]+cnt+1);
}
void find_every_min_group()
{
    //寻找包含每个元素的最小的群
    for(int i=1;i<=n;i++)
    {
        find_min_group(i);
    }
}
int g_equal(int a,int b)
{
    //判断两个群是否相等
//    printf("判断第%d和第%d个群是否相等\n",a,b);
//    for(int i=1;mg[a][i]!=0;i++)printf("%d,",mg[a][i]);
//    printf("这些是第%d个群中的元素\n",a);
//    for(int i=1;mg[b][i]!=0;i++)printf("%d,",mg[b][i]);
//    printf("这些是第%d个群中的元素\n",b);
    for(int i=1;;i++)
    {
        if(mg[a][i]==0&&mg[b][i]==0)return 1;
        else
        {
            if(mg[a][i]!=mg[b][i])return 0;
        }
    }
}
void col(int a,int b,int num)
{
    //将mg[a]和mg[b]两个群合并到mg[num]中
    //首先把a、b中的所有元素提取出来
    int ab[SIZE]={0};
    int i=0;
//    printf("第%d个子群的元素有:",a);
//    for(int ii=1;mg[a][ii]!=0;ii++)printf("%d ",mg[a][ii]);
//    printf("\n");
//    printf("第%d个子群的元素有:",b);
//    for(int ii=1;mg[b][ii]!=0;ii++)printf("%d ",mg[b][ii]);
//    printf("\n");
    for(i=1;mg[a][i]!=0;i++)
    {
        ab[i]=mg[a][i];
    }
    int j=1;
    while(mg[b][j]!=0)
    {
        if(in(mg[b][j],ab)!=1)ab[i++]=mg[b][j];
        j++;
    }
    sort(ab+1,ab+i);
    for(int ii=1;ab[ii]!=0;ii++)
    {
        mg[num][ii]=ab[ii];
    }
    //下面这一段是把所有元素进行运算然后求出子群
    i--;
    int now=1;
    for(;now<=i;now++)
    {
        for(int k=1;k<=now;k++)
        {
//            printf("此时第%d个群中的元素有:",num);
//            for(int ii=1;mg[num][ii]!=0;ii++)printf("%d,",mg[num][ii]);
//            printf("\n");
            if(i>=n)
            {
                sort(mg[num]+1,mg[num]+i+1);
                return;
            }
            if(in(group[mg[num][now]][mg[num][k]],mg[num])!=1)
            {
                mg[num][++i]=group[mg[num][now]][mg[num][k]];
            }
            if(in(group[mg[num][k]][mg[num][now]],mg[num])!=1)
            {
                mg[num][++i]=group[mg[num][k]][mg[num][now]];
            }
        }
    }
    sort(mg[num]+1,mg[num]+i+1);
//    printf("此时第%d个群中的元素有:",num);
//    for(int ii=1;mg[num][ii]!=0;ii++)printf("%d,",mg[num][ii]);
//    printf("\n");
}
void print_sub_group_of_no_cycle(int x,int cnt)
{
    printf("第%d个子群中的元素有{",cnt);
    for(int i=1;mg[x][i]!=0;i++)
    {
        printf("%d",mg[x][i]);
        if(mg[x][i+1]!=0)
            printf(",");
    }
    printf("}\n");
}
void no_cycle_sub_group()
{
    //非循环群的子群
    //先找包含每个元素的最小的群
    find_every_min_group();
    //对最小群去重
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            if(i==j)continue;
            int xiangdeng=g_equal(i,j);
//            if(xiangdeng)printf("第%d个和第%d个群相等\n",i,j);
//            else printf("第%d个和第%d个群不相等\n",i,j);
            if(xiangdeng==1)
            {
                for(int k=1;mg[j][k]!=0;k++)
                {
                    mg[j][k]=0;
                }
            }
        }
    }
    //每次再选择两个群合并,
    //新群如果没出现过就添加到群的集合
    //最后就能得到所有子群
    int sum=n;//合并群的总数
    int same=0;
    for(int i=1;i<=sum;i++)
    {
        for(int j=i+1;j<=sum;j++)
        {
            if(i!=j)
            {
                col(i,j,sum+1);
                same=0;
                for(int k=1;k<=sum;k++)
                {
                    if(g_equal(k,sum+1)==1)//去重
                    {
                        same=1;
                        for(int ii=1;mg[sum+1][ii]!=0;ii++)
                        {
                            mg[sum+1][ii]=0;
                        }
                        break;
                    }
                }
                if(same==0)sum++;
            }
        }
    }
    //输出所有子群
    int cnt=0;
    for(int i=1;i<=sum;i++)
    {
        if(mg[i][1]==0)continue;
        else print_sub_group_of_no_cycle(i,++cnt);
    }

}
int main()
{
    input();
    find_e();//找到群的单位元
    int cycle=cycle_or_no();
    printf("该群的单位元为:%d\n",e);
    if(cycle==1)
    {
        //如果群是循环群
        /*
            定理10.14 设G=<a>是循环群.
            (1) G的子群仍是循环群.
            (3) 若G=<a>是n阶循环群,则对n的每个正因子d,G恰好含有一个d 阶子群.那么生成元的阶数是n/d.
        */
        //根据以上定理求所有子群
        cycle_sub_group();
    }
    else//如果是非循环群
    {
        no_cycle_sub_group();
    }
    return 0;
}
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值