这篇文章主要解决以下问题:

给出一组度数列,求解它们之间能否连成一个简单图,如何构成简单图?


前言

最近刚好在学SAS,尝试性地写了一个程序去解这个问题:给一组点的度数列,求解它们之间能否连成一个简单图。

虽然本意是写一个判断一组度数列能否简单图化的程序,完成之后发现这个程序比起判断可行性,还是在有可行解的情况下给出可行解比较快——只要你输入的度数列是能简单图化的,它给出答案的速度非常快;但如果给它一组不能简单图化的度数列(最经典的方法就是违反握手定理),它计算出结果的速度会很慢。

实现

  • 这是求解程序 (简单图分析.sas)
data degree;
    input degree @@;
    datalines;
    3 3 2 2 2
run;

proc optmodel;
    set<num> POINT;
    
    num Degree{POINT};
    
    var Link{POINT,POINT} BINARY;
    
    con Full_Link{p in POINT}:sum{q inPOINT}Link[p,q]=Degree[p];
    con No_Link{p in POINT}:Link[p,p]=0;
    con Symmetry{p in POINT,q inPOINT}: Link[p,q]=Link[q,p];
    
    max Best=sum{p in POINT,q inPOINT}link[q,p];
    
    Read data degree into POINT=[_N_] Degree;
solve;

Print Link;

quit;
  • 这是 matlab 上的问题生成程序:生成一个随机简单图 (make_simple.m)
%% 随机生成一个简单图的邻接矩阵

function a=make_simple(n)
    a=floor(rand(n,n)*2);
    [m n]=size(a);

    for i=1:m
        for j=1:n
            if i>j
               a(j,i)=a(i,j);
            end
            if i==j
               a(i,j)=0;
            end
        end
    end
end
  • 这是 matlab 上的解答验证程序:判断一个邻接矩阵是否是简单图 (is_simple.m)
%% 检验一个邻接矩阵是否为简单图
%% 如果有两个参数,自动修剪掉矩阵的第一行和第一列

function bool=is_simple(a,b)
    if nargin==2
        a=a(2:end,2:end);
    end

    ok1=0;ok2=0;ok3=0;

    if sum(sum(a<0))==0
         ok1=1;
    end

    if sum(diag(a))==0
         ok2=1;
    end

    if a==a'
         ok3=1;
    end

    if ok1*ok2*ok3==1
         bool=1;
    else
         bool=0;
    end
end

求解

假设简单图模型如下

运行求解

将实际情形与 SAS 给出的答案画出来对比,能看出两者其实是等价的。

算法测试

鉴于更高维度的简单图手工构造实在有难度,这里采用邻接矩阵的模型,用 matlab 模拟一个大规模的简单图(生成程序见上)

生成简单图的思路是:先随机生成一个 0-1矩阵,然后把它修正成一个简单图

先随机构造一个 50 个顶点的简单图

获取度数列

输入 SAS

求解结果,不到 1 秒就算出答案了

答案是不是简单图,还需要检验。还是用 matlab 测试(检验程序见上)

检验方式是:看它给出的矩阵对角线是否全 0,是否对称,是否只有 0-1两种元素。

结果证实了 SAS 所给的简单图,确实是简单图