非标定的n阶无向图的非同构个数

该博客详细介绍了如何计算非标定的n阶无向图的不同同构个数。通过分析标定图的数量、同构分类以及置换映射,提出了一种基于置换的计数方法。博主通过建立数学模型和计算机算法,展示了如何在O(n!)的时间复杂度内估算出同构图的数量,并提供了具体的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

非标定的n阶无向图的非同构个数

特别鸣谢wp师兄跟我的讲解

问题分析:

1. 先不考虑同构,只管标定图的数量

首先我们先从标定图开始看,总共有多少个n阶标定图?

其实这就相当于固定了点数,数边的数量。共有 n ( n − 1 ) / 2 n(n-1)/2 n(n1)/2 条边,那么总共就有 2 n ( n − 1 ) 2 2^{\frac{n(n-1)}{2}} 22n(n1)个标定图(最后发现这个数量并没有用

2. 考虑同构,并将这些标定图进行分组

接下来我们来将这些图分类,我们不妨设共有 t t t 个不同构的图记作 G 1 , G 2 , . . . , G t G_1,G_2,...,G_t G1,G2,...,Gt,然后我们再设一个集合,取出所有标定图中与其同构的图,共同放入这个集合中,记作 S { G i } S\{G_i\} S{Gi}

S { G 1 } = { G 11 , G 12 , G 13 , . . . , G 1 k 1 } S\{G_1\}=\{G_{11},G_{12},G_{13},...,G_{1k_1}\} S{G1}={G11,G12,G13,...,G1k1}

S { G 2 } = { G 21 , G 22 , G 23 , . . . , G 2 k 2 } S\{G_2\}=\{G_{21},G_{22},G_{23},...,G_{2k_2}\} S{G2}={G21,G22,G23,...,G2k2}

. . . ... ...

S { G t } = { G t 1 , G t 2 , G t 3 , . . . , G t k t } S\{G_t\}=\{G_{t1},G_{t2},G_{t3},...,G_{tk_t}\} S{Gt}={Gt1,Gt2,Gt3,...,Gtkt}

因为这些图之间都是不同的,我们可以得到 ∑ i = 1 t ∣ S { G i } ∣ = 2 n ( n − 1 ) 2 \sum_{i=1}^t|S\{G_i\}|=2^{\frac{n(n-1)}{2}} i=1tS{Gi}=22n(n1)

3. 观察被分到一起的标定图的组

然后我们取出其中一个集合进行观察,例如 S { G 1 } = { G 11 , G 12 , G 13 , . . . , G 1 k 1 } S\{G_1\}=\{G_{11},G_{12},G_{13},...,G_{1k_1}\} S{G1}={G11,G12,G13,...,G1k1}

首先给定一个 G 1 G_1 G1的非标定图,你给他任意标定,一定是与 G 1 G_1 G1同构的。或者换一个说法,对于集合中的第一个元素 G 11 G_{11} G11,你对它的顶点做任意的置换,得到的图 G 11 ′ G_{11}' G11 一定是与 G 11 G_{11} G11 同构的。

但是这里要注意,不是每次置换都会得到一个新的标定图。

例如下图中,左边为 G 11 G_{11} G11 其六种置换中有两个与自身相同的,剩下的四种置换也只能得到两个不同的标定图。因此在这个例子中 ∣ S { G 1 } ∣ = 3 |S\{G_1\}|=3 S{G1}=3

在这里插入图片描述

因此, S { G 1 } S\{G_1\} S{G1} 中就是 G 11 G_{11} G11 的置换去同构之后剩下的部分。

那么究竟有多少个置换呢?我们用集合 S i j S_{ij} Sij表示从 G 1 i G_{1i} G1i变成 G 1 j G_{1j} G1j的所有置换。例如 S 12 S_{12} S12表示从 G 11 G_{11} G11 可以映射到 G 12 G_{12} G12 的所有可能的映射

我们知道所有的置换数相加应该等于 n ! n! n!,那么我们以 G 11 G_{11} G11 为置换的作用图,可以得到:

∑ i = 1 k 1 ∣ S 1 i ∣ = n ! \sum_{i=1}^{k_1}|S_{1i}|=n! i=1k1S1i=n!

通过上面的例子观察可以发现一个奇妙但是又比较自然的规律:

∣ S 11 ∣ = ∣ S 12 ∣ = ∣ S 13 ∣ = . . . |S_{11}|=|S_{12}|=|S_{13}|=... S11=S12=S13=...

即从 G 11 G_{11} G11 出发得到自身和得到其他图的映射的数目相等。

那么接下来我们对它进行一个证明:

首先设我们的 ∣ S 11 ∣ = x . S 11 = { f 1 , f 2 , . . . , f x } |S_{11}| = x. S_{11}=\{f_1,f_2,...,f_x\} S11=x.S11={f1,f2,...,fx},即我们自己置换到自己的数量为 x x x,接着我们来尝试推 S 12 S_{12} S12 的数目

首先,我们一定会存在一个映射 f f f,使得 f ⋅ G 11 = G 12 f\cdot G_{11}=G_{12} fG11=G12

那么我们可以得到 ∀ f i ∈ S 11 ,    f ⋅ f i ⋅ G 11 = G 12 \forall f_i\in S_{11},\ \ f\cdot f_i\cdot G_{11} = G_{12} fiS11,  ffiG11=G12,同时 f i f_i fi互不相同可以推出 f ⋅ f i f\cdot f_i ffi也互不相同,所以每有一个可以到自己的置换 f i f_i fi,就存在一个到 G 12 G_{12} G12的置换 f ⋅ f i f\cdot f_i ffi,并且置换之间互不相同

那么也就是说, ∀ f i ∈ S 11 ,    f ⋅ f i ∈ S 12 \forall f_i\in S_{11},\ \ f\cdot f_i\in S_{12} fiS11,  ffiS12,因此 ∣ S 12 ∣ ≥ ∣ S 11 ∣ |S_{12}|\ge|S_{11}| S12S11

接着我们设 S 12 = { g 1 , g 2 , . . . , g y } S_{12}=\{g_1,g_2,...,g_y\} S12={g1,g2,...,gy}

那么 S 12 S_{12} S12 中的映射满足 ∀ g i ∈ S 12 ,    g i ⋅ G 11 = G 12 \forall g_i\in S_{12},\ \ g_i\cdot G_{11} = G_{12} giS12,  giG11=G12,同时对于刚刚的满足 f ⋅ G 11 = G 12 f\cdot G_{11}=G_{12} fG11=G12 的映射 f f f,我们可以找到它的逆映射 f − 1 f^{-1} f1,那么我们就有 f − 1 ⋅ G 12 = G 11 f^{-1}\cdot G_{12}=G_{11} f1G12=G11

那么我们可以得到 ∀ g i ∈ S 12 ,    f − 1 ⋅ g i ⋅ G 11 = G 11 \forall g_i\in S_{12},\ \ f^{-1}\cdot g_i\cdot G_{11} = G_{11} giS12,  f1giG11=G11,同时 g i g_i gi互不相同可以推出 f − 1 ⋅ g i f^{-1}\cdot g_i f1gi也互不相同,所以每有一个可以到 G 12 G_{12} G12的置换 g i g_i gi,就存在一个到自己的置换 f − 1 ⋅ g i f^{-1}\cdot g_i f1gi,并且置换之间互不相同

那么也就是说, ∀ g i ∈ S 12 ,    f − 1 ⋅ g i ∈ S 11 \forall g_i\in S_{12},\ \ f^{-1}\cdot g_i\in S_{11} giS12,  f1giS11,因此 ∣ S 12 ∣ ≤ ∣ S 11 ∣ |S_{12}|\le|S_{11}| S12S11

综上可得: ∣ S 11 ∣ = ∣ S 12 ∣ |S_{11}|=|S_{12}| S11=S12

同理可得:

∣ S 11 ∣ = ∣ S 12 ∣ = ∣ S 13 ∣ = . . . = ∣ S 1 k 1 ∣ |S_{11}|=|S_{12}|=|S_{13}|=...=|S_{1k_1}| S11=S12=S13=...=S1k1

∣ S 22 ∣ = ∣ S 21 ∣ = ∣ S 23 ∣ = . . . = ∣ S 2 k 1 ∣ |S_{22}|=|S_{21}|=|S_{23}|=...=|S_{2k_1}| S22=S21=S23=...=S2k1

. . . ... ...

∣ S k 1 k 1 ∣ = ∣ S k 1 1 ∣ = ∣ S k 1 2 ∣ = . . . = ∣ S k 1 k 1 − 1 ∣ |S_{k_1k_1}|=|S_{k_11}|=|S_{k_12}|=...=|S_{k_1k_1-1}| Sk1k1=Sk11=Sk12=...=Sk1k11

同时,只需要将 S i j S_{ij} Sij 中的映射全部取逆就可以得到集合 S j i S_{ji} Sji ,所以可以推出 ∣ S i j ∣ = ∣ S j i ∣ |S_{ij}|=|S_{ji}| Sij=Sji,即从图 G 1 i G_{1i} G1i 到图 G 1 j G_{1j} G1j 的映射数量一定等于从图 G 1 j G_{1j} G1j 到图 G 1 i G_{1i} G1i 的映射数量

因此我们可以将那个从 G 11 G_{11} G11出发的式子转化为

∑ i = 1 k 1 ∣ S i i ∣ = n ! \sum_{i=1}^{k_1}|S_{ii}|=n! i=1k1Sii=n!

4. 回到组与组之间

我们对第一组进行了一定的分析,得到了上面的式子 ∑ i = 1 k 1 ∣ S i i ∣ = n ! \sum_{i=1}^{k_1}|S_{ii}|=n! i=1k1Sii=n!

为了方便区分这是第几组,我们给 S S S 加上上标 ( 1 ) ^{(1)} (1) ∑ i = 1 k 1 ∣ S i i ( 1 ) ∣ = n ! \sum_{i=1}^{k_1}|S_{ii}^{(1)}|=n! i=1k1Sii(1)=n!

那么对于所有的 t t t 组同构所构成的集合,我们都可以得到一个这样的等式: ∀ 1 ≤ p ≤ t , ∑ i = 1 k p ∣ S i i ( p ) ∣ = n ! \forall 1\le p\le t,\sum_{i=1}^{k_p}|S_{ii}^{(p)}|=n! 1pt,i=1kpSii(p)=n!

那我们将这里所有的等式加起来就可以得到:
∑ p = 1 t ∑ i = 1 k p ∣ S i i ( p ) ∣ = t ∗ n ! t = ∑ p = 1 t ∑ i = 1 k p ∣ S i i ( p ) ∣ n ! \sum_{p=1}^{t}\sum_{i=1}^{k_p}|S^{(p)}_{ii}|=t*n!\\ t=\frac{\sum_{p=1}^{t}\sum_{i=1}^{k_p}|S^{(p)}_{ii}|}{n!} p=1ti=1kpSii(p)=tn!t=n!p=1ti=1kpSii(p)
而我们回顾我们分组的过程,就可以发现 ∑ p = 1 t ∑ i = 1 k p \sum_{p=1}^{t}\sum_{i=1}^{k_p} p=1ti=1kp 其实就是对所有的标定图 G G G ∣ S i i ( p ) ∣ |S^{(p)}_{ii}| Sii(p) 就是对于这个图 G G G 它的能置换到自己的置换的数目。

那么这个求和,其实就是找到所有的特定的置换与图组成的二元组 ( f , G ) (f, G) (f,G),其中 f ⋅ G = G f\cdot G=G fG=G

那么在计算机中,枚举每个图的代价是巨大的,正如之前算的是 2 n ( n − 1 ) 2 2^{\frac{n(n-1)}{2}} 22n(n1),但是我们换一种思路,我们可以来枚举所有的置换 f f f,找到所有的满足 f ⋅ G = G f\cdot G=G fG=G 的图 G G G,设 S G { f } S_G\{f\} SG{f} 表示满足的图的集合,这样我们可以得到下面的式子
t = ∑ f ∣ S G { f } ∣ n ! t=\frac{\sum_f|S_G\{f\}|}{n!} t=n!fSG{f}
而枚举置换的复杂度就只有 O ( n ! ) O(n!) O(n!) 了(其实和之前那个一样都是指数级的,只是那个显然更高阶

5. 计算给定了置换 f f f 之后有多少个图满足 f ⋅ G = G f\cdot G=G fG=G

首先我们知道图是通过边集的来定义的

而这里的点集的置换一定对应一个边集的置换

例如

{ 1 , 2 , 3 , 4 } → { 2 , 3 , 1 , 4 } \{1,2,3,4\} \rightarrow \{2,3,1,4\} {1,2,3,4}{2,3,1,4} 的点集置换 f f f 就可以得到一个相应的边集置换 f ′ f' f

{ ( 1 , 2 ) , ( 1 , 3 ) , ( 1 , 4 ) , ( 2 , 3 ) , ( 2 , 4 ) , ( 3 , 4 ) } → { ( 2 , 3 ) , ( 1 , 2 ) , ( 2 , 4 ) , ( 1 , 3 ) , ( 3 , 4 ) , ( 1 , 4 ) } \{(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)\} \rightarrow \{(2,3), (1,2), (2,4), (1,3), (3,4), (1,4)\} {(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)}{(2,3),(1,2),(2,4),(1,3),(3,4),(1,4)}

那么我们考虑这个点集所对应的满足自己映射到自己的所有的图,其实就是看这个边集置换下的能映射到自己的图:

那么我们就需要找到的图会在拥有了一条边之后,就一定拥有了其映射之后得到的另一条边,以此类推,直至形成环形。

那么我们先选择集合中的一条边 e 1 e_1 e1 对其进行边集置换得到 e 2 e_2 e2,再依次进行,直到得到第一个等于之前出现过的边 e k + 1 e_{k+1} ek+1 为止,这样我们就得到了 k k k 条不同的边构成了一个置换的循环。然后继续选择集合中不同于这 k k k 条边的其它边依次进行这个操作。

首先我们可以证明 e k + 1 = e 1 e_{k+1}=e_1 ek+1=e1

因为假设 e k + 1 = e i , ( i > 1 ) e_{k+1}=e_i,(i>1) ek+1=ei,(i>1),那么根据 f ′ ⋅ e i − 1 = e i f'\cdot e_{i-1}=e_i fei1=ei,就有 f ′ − 1 ⋅ e i = e i − 1 f'^{-1}\cdot e_i=e_{i-1} f1ei=ei1,那么根据 e i = e k + 1 e_i=e_{k+1} ei=ek+1 就有 f ′ − 1 ∗ e k + 1 = e i − 1 f'^{-1}*e_{k+1}=e_{i-1} f1ek+1=ei1 也即 e k = e i − 1 e_k=e_{i-1} ek=ei1 那么违反了 e k + 1 e_{k+1} ek+1是第一个与之前重复的假设。所以 e k + 1 e_{k+1} ek+1 一定等于 e 1 e_1 e1

例如上面这个例子:

我们先选择 ( 1 , 2 ) (1,2) (1,2) 这条边

( 1 , 2 ) → ( 2 , 3 ) → ( 1 , 3 ) → ( 1 , 2 ) {(1,2) \rightarrow (2,3) \rightarrow (1,3) \rightarrow (1,2)} (1,2)(2,3)(1,3)(1,2)

再选择 ( 1 , 4 ) (1,4) (1,4) 这条边

( 1 , 4 ) → ( 2 , 4 ) → ( 3 , 4 ) → ( 1 , 4 ) {(1,4) \rightarrow (2,4) \rightarrow (3,4) \rightarrow (1,4)} (1,4)(2,4)(3,4)(1,4)

因此我们就得到了两个长度为 3 3 3 的循环节

那么在这样一个映射下,这两个循环节里的边要么都选要么都不选,因此我们一共有 2 2 = 4 2^2=4 22=4 种方法

因此我们对于任何一个点集映射 f f f,我们可以得到它的边集映射 f ′ f' f,在 f ′ f' f 中我们可以通过遍历的方法得到它的循环节个数,设这个个数为 I ( f ′ ) I(f') I(f)

那么对于点集映射 f f f 的满足 f ⋅ G = G f\cdot G=G fG=G G G G 的个数就等于 2 I ( f ′ ) 2^{I(f')} 2I(f)

6. 最终公式

t = ∑ f 2 I ( f ′ ) n ! t=\frac{\sum_f2^{I(f')}}{n!} t=n!f2I(f)

7. 实现代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
    
const int maxn = 13;

int n;
long long sum = 0;
long long lv[maxn];
int f[maxn];
bool visEdge[maxn][maxn];
bool vis[maxn];

void calculate(){
    int cnt = 0;
    memset(visEdge, 0, sizeof(visEdge));
	for(int i = 1; i <= n; i++){
		for(int j = i+1; j <= n; j++)
        	if(!visEdge[i][j]){
                int ni = i, nj = j;
                do{
                	visEdge[ni][nj] = true;    
                	ni = f[ni], j = f[nj];
                    if(ni > nj) swap(ni, nj);
                }while(visEdge[ni][nj] == false);
        		cnt ++;
            }
    }
    long long s = 1;
    for(int i = 0; i < cnt; i++)
		s <<= 1;
    sum += s;
}

void search(int t){
	if(t == n + 1){
		calculate();
    	return ;
    }
    for(int i = 1; i <= n; i ++)
        if(!vis[i]){
			vis[i] = true;
            f[t] = i;
            search(t + 1);
            vis[i] = false;
        }
}

int main(){
	scanf("%d", &n);
    lv[0] = 1;
    for(int i = 1; i <= n; i++)
        lv[i] = lv[i - 1] * i;
    search(1);
    printf("%lld", sum/lv[n]);
    return 0;
}

/*
时间复杂度:O(n!*n^2) = O(n!)
运行结果:
1: 1
2: 2
3: 4
4: 11
5: 34
6: 156
7: 1044
8: 12346
9: 274668
10: 12005168
*/

8. 相关论文

《利用Polya定理计算图的计数》
http://qikan.cqvip.com/Qikan/Article/Detail?id=22829954
这个论文求解的方法好像更加的优秀,但是我不太懂Polya定理还有母函数这些所以没有很看懂hhh

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值