#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;
}