非标定的n阶无向图的非同构个数
特别鸣谢wp师兄跟我的讲解
问题分析:
1. 先不考虑同构,只管标定图的数量
首先我们先从标定图开始看,总共有多少个n阶标定图?
其实这就相当于固定了点数,数边的数量。共有 n ( n − 1 ) / 2 n(n-1)/2 n(n−1)/2 条边,那么总共就有 2 n ( n − 1 ) 2 2^{\frac{n(n-1)}{2}} 22n(n−1)个标定图(最后发现这个数量并没有用
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=1t∣S{Gi}∣=22n(n−1)
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=1k1∣S1i∣=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} f⋅G11=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} ∀fi∈S11, f⋅fi⋅G11=G12,同时 f i f_i fi互不相同可以推出 f ⋅ f i f\cdot f_i f⋅fi也互不相同,所以每有一个可以到自己的置换 f i f_i fi,就存在一个到 G 12 G_{12} G12的置换 f ⋅ f i f\cdot f_i f⋅fi,并且置换之间互不相同
那么也就是说, ∀ f i ∈ S 11 , f ⋅ f i ∈ S 12 \forall f_i\in S_{11},\ \ f\cdot f_i\in S_{12} ∀fi∈S11, f⋅fi∈S12,因此 ∣ S 12 ∣ ≥ ∣ S 11 ∣ |S_{12}|\ge|S_{11}| ∣S12∣≥∣S11∣
接着我们设 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} ∀gi∈S12, gi⋅G11=G12,同时对于刚刚的满足 f ⋅ G 11 = G 12 f\cdot G_{11}=G_{12} f⋅G11=G12 的映射 f f f,我们可以找到它的逆映射 f − 1 f^{-1} f−1,那么我们就有 f − 1 ⋅ G 12 = G 11 f^{-1}\cdot G_{12}=G_{11} f−1⋅G12=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} ∀gi∈S12, f−1⋅gi⋅G11=G11,同时 g i g_i gi互不相同可以推出 f − 1 ⋅ g i f^{-1}\cdot g_i f−1⋅gi也互不相同,所以每有一个可以到 G 12 G_{12} G12的置换 g i g_i gi,就存在一个到自己的置换 f − 1 ⋅ g i f^{-1}\cdot g_i f−1⋅gi,并且置换之间互不相同
那么也就是说, ∀ g i ∈ S 12 , f − 1 ⋅ g i ∈ S 11 \forall g_i\in S_{12},\ \ f^{-1}\cdot g_i\in S_{11} ∀gi∈S12, f−1⋅gi∈S11,因此 ∣ S 12 ∣ ≤ ∣ S 11 ∣ |S_{12}|\le|S_{11}| ∣S12∣≤∣S11∣
综上可得: ∣ 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∣=...=∣Sk1k1−1∣
同时,只需要将 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=1k1∣Sii∣=n!
4. 回到组与组之间
我们对第一组进行了一定的分析,得到了上面的式子 ∑ i = 1 k 1 ∣ S i i ∣ = n ! \sum_{i=1}^{k_1}|S_{ii}|=n! ∑i=1k1∣Sii∣=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=1k1∣Sii(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! ∀1≤p≤t,∑i=1kp∣Sii(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=1∑ti=1∑kp∣Sii(p)∣=t∗n!t=n!∑p=1t∑i=1kp∣Sii(p)∣
而我们回顾我们分组的过程,就可以发现
∑
p
=
1
t
∑
i
=
1
k
p
\sum_{p=1}^{t}\sum_{i=1}^{k_p}
∑p=1t∑i=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 f⋅G=G
那么在计算机中,枚举每个图的代价是巨大的,正如之前算的是
2
n
(
n
−
1
)
2
2^{\frac{n(n-1)}{2}}
22n(n−1),但是我们换一种思路,我们可以来枚举所有的置换
f
f
f,找到所有的满足
f
⋅
G
=
G
f\cdot G=G
f⋅G=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!∑f∣SG{f}∣
而枚举置换的复杂度就只有
O
(
n
!
)
O(n!)
O(n!) 了(其实和之前那个一样都是指数级的,只是那个显然更高阶
5. 计算给定了置换 f f f 之后有多少个图满足 f ⋅ G = G f\cdot G=G f⋅G=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 f′⋅ei−1=ei,就有 f ′ − 1 ⋅ e i = e i − 1 f'^{-1}\cdot e_i=e_{i-1} f′−1⋅ei=ei−1,那么根据 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} f′−1∗ek+1=ei−1 也即 e k = e i − 1 e_k=e_{i-1} ek=ei−1 那么违反了 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 f⋅G=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