igraph 参考手册

用于使用 igraph C 库

搜索手册

第 22 章. 最大流、最小割及相关度量

1. 最大流

1.1. igraph_maxflow — 一对顶点之间的最大网络流。

igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value,
                              igraph_vector_t *flow, igraph_vector_int_t *cut,
                              igraph_vector_int_t *partition, igraph_vector_int_t *partition2,
                              igraph_integer_t source, igraph_integer_t target,
                              const igraph_vector_t *capacity,
                              igraph_maxflow_stats_t *stats);

此函数实现 Goldberg-Tarjan 算法来计算有向图或无向图中最大流的值。该算法在 Andrew V. Goldberg, Robert E. Tarjan: A New Approach to the Maximum-Flow Problem, Journal of the ACM, 35(4), 921-940, 1988 https://doi.org/10.1145/48014.61051 中给出。

该函数的输入是一个图,一个实数向量表示边的容量,以及图中的两个顶点,源和目标。流是一个将正实数分配给边的函数,并满足两个要求:(1)流值小于边的容量,并且(2)在除源和目标之外的每个顶点,流入流(即,流入边上的流的总和)与流出流(即,流出边上的流的总和)相同。流的值是目标顶点的流入流。最大流是具有最大值的流。

参数: 

:

输入图,可以是有向图或无向图。

value:

指向实数的指针,最大流的值将放置在此处,除非它是空指针。

flow:

如果不是空指针,则它必须是指向已初始化向量的指针。向量将被调整大小,并且每条边上的流将按照边 ID 的顺序放置在其中。对于无向图,此参数有点棘手,因为对于这些图,流方向不是由边方向预先确定的。对于这些图,flow 向量的元素可以是负数,这意味着流从较大的顶点 ID 流向较小的顶点 ID。正值意味着流从较小的顶点 ID 流向较大的顶点 ID。

cut:

空指针或指向已初始化向量的指针。如果不是空指针,则与最大流对应的最小割将存储在此处,即,属于最小割的所有边 ID 都存储在向量中。

partition:

空指针或指向已初始化向量的指针。如果不是空指针,则与最大流对应的最小割的第一个分区将放置在此处。第一个分区始终是包含源顶点的分区。

partition2:

空指针或指向已初始化向量的指针。如果不是空指针,则与最大流对应的最小割的第二个分区将放置在此处。第二个分区始终是包含目标顶点的分区。

来源:

源顶点的 ID。

目标:

目标顶点的 ID。

capacity:

包含边容量的向量。如果为 NULL,则认为每条边的容量为 1.0。

stats:

算法执行的不同操作的次数存储在此处。

返回值: 

错误代码。

时间复杂度:O(|V|^3)。在实践中它快得多,但我无法证明我使用的数据结构有更好的下限。事实上,这个实现比 B. V. Cherkassky 和 A. V. Goldberg: On implementing the push-relabel method for the maximum flow problem, (Algorithmica, 19:390--410, 1997) 中讨论的 hi_pr 实现在我尝试过的所有图类上运行得更快。

另请参阅: 

示例 22.1.  文件 examples/simple/flow.c

#include <igraph.h>

int main(void) {

    igraph_t g;
    igraph_real_t flow;
    igraph_vector_t capacity;
    igraph_integer_t source, target;
    FILE *infile;
    igraph_maxflow_stats_t stats;

    igraph_vector_init(&capacity, 0);

    /***************/
    infile = fopen("ak-4102.max", "r");
    igraph_read_graph_dimacs_flow(
        &g, infile, 0, 0, &source, &target, &capacity, IGRAPH_DIRECTED);
    fclose(infile);

    igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats);

    if (flow != 8207) {
        return 1;
    }
    igraph_destroy(&g);
    /***************/

    /*   /\***************\/ */
    /*   infile=fopen("ak-8198.max", "r"); */
    /*   igraph_read_graph_dimacs_flow(&g, infile, 0, 0, &source, &target, &capacity, */
    /*             IGRAPH_DIRECTED); */
    /*   fclose(infile); */

    /*   t=timer(); */
    /*   igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats); */
    /*   t=timer()-t; */
    /*   printf("8198: %g (time %.10f)\n", flow, t); */
    /*   igraph_destroy(&g); */
    /*   /\***************\/ */

    /*   /\***************\/ */
    /*   infile=fopen("ak-16390.max", "r"); */
    /*   igraph_read_graph_dimacs_flow(&g, infile, 0, 0, &source, &target, &capacity, */
    /*             IGRAPH_DIRECTED); */
    /*   fclose(infile); */

    /*   t=timer(); */
    /*   igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats); */
    /*   t=timer()-t; */
    /*   printf("16390: %g (time %.10f)\n", flow, t); */
    /*   igraph_destroy(&g); */
    /*   /\***************\/ */

    /*   /\***************\/ */
    /*   infile=fopen("ak-32774.max", "r"); */
    /*   igraph_read_graph_dimacs_flow(&g, infile, 0, 0, &source, &target, &capacity, */
    /*             IGRAPH_DIRECTED); */
    /*   fclose(infile); */

    /*   t=timer(); */
    /*   igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats); */
    /*   t=timer()-t; */
    /*   printf("32774: %g (time %.10f)\n", flow, t); */
    /*   igraph_destroy(&g); */
    /*   /\***************\/ */

    /*   /\***************\/ */
    /*   infile=fopen("ak-65542.max", "r"); */
    /*   igraph_read_graph_dimacs_flow(&g, infile, 0, 0, &source, &target, &capacity, */
    /*             IGRAPH_DIRECTED); */
    /*   fclose(infile); */

    /*   t=timer(); */
    /*   igraph_maxflow_value(&g, &flow, source, target, &capacity, &stats); */
    /*   t=timer()-t; */
    /*   printf("65542: %g (time %.10f)\n", flow, t); */
    /*   igraph_destroy(&g); */
    /*   /\***************\/ */

    igraph_vector_destroy(&capacity);

    return 0;
}


示例 22.2.  文件 examples/simple/flow2.c

#include <igraph.h>

int main(void) {

    igraph_t g;
    igraph_real_t flow_value;
    igraph_vector_int_t cut;
    igraph_vector_t capacity;
    igraph_vector_int_t partition, partition2;
    igraph_vector_t flow;
    igraph_integer_t i;
    igraph_maxflow_stats_t stats;

    igraph_small(&g, 6, IGRAPH_DIRECTED,
                 0, 1, 1, 2, 2, 3, 0, 5, 5, 4, 4, 3, 3, 0, -1);
    igraph_vector_init_int_end(&capacity, -1, 3, 1, 2, 10, 1, 3, 2, -1);
    igraph_vector_int_init(&cut, 0);
    igraph_vector_int_init(&partition, 0);
    igraph_vector_int_init(&partition2, 0);
    igraph_vector_init(&flow, 0);

    igraph_maxflow(&g, &flow_value, &flow, &cut, &partition, &partition2,
                   /*source=*/ 0, /*target=*/ 2, &capacity, &stats);

    igraph_integer_t nc = igraph_vector_int_size(&cut);
    printf("flow value: %g\n", (double) flow_value);
    printf("flow: ");
    igraph_vector_print(&flow);
    printf("first partition:  ");
    igraph_vector_int_print(&partition);
    printf("second partition: ");
    igraph_vector_int_print(&partition2);
    printf("edges in the cut: ");
    for (i = 0; i < nc; i++) {
        igraph_integer_t edge = VECTOR(cut)[i];
        igraph_integer_t from = IGRAPH_FROM(&g, edge);
        igraph_integer_t to  = IGRAPH_TO(&g, edge);
        printf("%" IGRAPH_PRId "-%" IGRAPH_PRId " (%g), ", from, to, VECTOR(capacity)[edge]);
    }
    printf("\n");

    igraph_vector_int_destroy(&cut);
    igraph_vector_int_destroy(&partition2);
    igraph_vector_int_destroy(&partition);
    igraph_vector_destroy(&capacity);
    igraph_vector_destroy(&flow);
    igraph_destroy(&g);

    return 0;
}


1.2. igraph_maxflow_value — 使用推送/重标记算法计算网络中的最大流。

igraph_error_t igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value,
                                    igraph_integer_t source, igraph_integer_t target,
                                    const igraph_vector_t *capacity,
                                    igraph_maxflow_stats_t *stats);

此函数实现 Goldberg-Tarjan 算法来计算有向图或无向图中最大流的值。该算法在 Andrew V. Goldberg, Robert E. Tarjan: A New Approach to the Maximum-Flow Problem, Journal of the ACM, 35(4), 921-940, 1988 https://doi.org/10.1145/48014.61051 中给出。

该函数的输入是一个图,一个实数向量表示边的容量,以及图中的两个顶点,源和目标。流是一个将正实数分配给边的函数,并满足两个要求:(1)流值小于边的容量,并且(2)在除源和目标之外的每个顶点,流入流(即,流入边上的流的总和)与流出流(即,流出边上的流的总和)相同。流的值是目标顶点的流入流。最大流是具有最大值的流。

根据 Ford 和 Fulkerson 的一个定理(L. R. Ford Jr. 和 D. R. Fulkerson. Maximal flow through a network. Canadian J. Math., 8:399-404, 1956.),两个顶点之间的最大流与它们之间的最小割相同(也称为最小 s-t 割)。所以 igraph_st_mincut_value() 在所有情况下都给出与 igraph_maxflow_value() 相同的结果。

请注意,最大流的值与图中的最小割相同。

参数: 

:

输入图,可以是有向图或无向图。

value:

指向实数的指针,结果将放置在此处。

来源:

源顶点的 ID。

目标:

目标顶点的 ID。

capacity:

包含边容量的向量。如果为 NULL,则认为每条边的容量为 1.0。

stats:

算法执行的不同操作的次数存储在此处。

返回值: 

错误代码。

时间复杂度:O(|V|^3)。

另请参阅: 

igraph_maxflow() 用于计算实际流。 igraph_mincut_value(), igraph_edge_connectivity(), igraph_vertex_connectivity() 用于基于最大流的属性。

1.3. igraph_dominator_tree — 计算流图的支配树。

igraph_error_t igraph_dominator_tree(const igraph_t *graph,
                                     igraph_integer_t root,
                                     igraph_vector_int_t *dom,
                                     igraph_t *domtree,
                                     igraph_vector_int_t *leftout,
                                     igraph_neimode_t mode);

流图是一个有向图,具有一个特定的起始(或根)顶点 r,这样对于任何顶点 v,都存在从 r 到 v 的路径。如果从 r 到 w 的每条路径都包含 v,则顶点 v 支配另一个顶点 w(不等于 v)。如果 v 支配 w 并且 w 的每个其他支配者都支配 v,则顶点 v 是 w 的直接支配者,v=idom(w)。边 {(idom(w), w)| w 不是 r} 形成一个有向树,以 r 为根,称为图的支配树。当且仅当 v 是支配树中 w 的祖先时,顶点 v 支配顶点 w。

此函数实现 Lengauer-Tarjan 算法来构造有向图的支配树。有关详细信息,请参阅 Thomas Lengauer, Robert Endre Tarjan: A fast algorithm for finding dominators in a flowgraph, ACM Transactions on Programming Languages and Systems (TOPLAS) I/1, 121--141, 1979. https://doi.org/10.1145/357062.357071

参数: 

:

一个有向图。如果它不是流图,并且包含一些无法从根顶点到达的顶点,则这些顶点将被收集在 leftout 向量中。

root:

根(或源)顶点的 ID,这将是树的根。

dom:

指向已初始化向量或空指针的指针。如果不是空指针,则每个顶点的直接支配者将存储在此处。对于无法从根到达的顶点,此处存储 -2。对于根顶点本身,将添加 -1

domtree:

指向未初始化 igraph_tNULL 的指针。如果不是空指针,则支配树将返回到此处。该图包含无法从根到达的顶点(如果有),这些顶点将是孤立的。图和顶点属性被保留,但边属性被丢弃。

leftout:

指向已初始化向量对象的指针,或 NULL。如果不是 NULL,则无法从根顶点到达(因此不是支配树的一部分)的顶点的 ID 将存储在此处。

模式:

恒定值,必须是 IGRAPH_INIGRAPH_OUT。如果它是 IGRAPH_IN,则所有方向都被视为与输入图中原始方向相反。

返回值: 

错误代码。

时间复杂度:非常接近 O(|E|+|V|),在边和顶点的数量上是线性的。更准确地说,它是 O(|V|+|E|alpha(|E|,|V|)),其中 alpha(|E|,|V|) 是 Ackermann 函数的函数逆。

示例 22.3.  文件 examples/simple/dominator_tree.c

#include <igraph.h>
#include <stdio.h>

int main(void) {
    igraph_t g, domtree;
    igraph_vector_int_t dom;
    igraph_vector_int_t leftout;

    igraph_vector_int_init(&dom, 0);
    igraph_vector_int_init(&leftout, 0);

    igraph_small(&g, 10, IGRAPH_DIRECTED,
                 0, 9,
                 1, 0, 1, 2,
                 2, 3, 2, 7,
                 3, 1,
                 4, 1, 4, 3,
                 5, 2, 5, 3, 5, 4, 5, 8,
                 6, 5, 6, 9,
                 8, 7,
                 -1);

    igraph_dominator_tree(&g, /*root=*/ 9, &dom, &domtree,
                          &leftout, /*mode=*/ IGRAPH_IN);
    igraph_vector_int_print(&dom);
    igraph_vector_int_print(&leftout);
    igraph_write_graph_edgelist(&domtree, stdout);

    igraph_vector_int_destroy(&dom);
    igraph_vector_int_destroy(&leftout);
    igraph_destroy(&domtree);
    igraph_destroy(&g);

    return 0;
}


1.4. igraph_maxflow_stats_t — 保存来自推送-重标记最大流求解器的统计数据的数据结构。

typedef struct {
    igraph_integer_t nopush, norelabel, nogap, nogapnodes, nobfs;

参数: 

nopush:

执行的推送操作次数。

norelabel:

执行的重标记操作次数。

nogap:

使用间隙启发式的次数。

nogapnodes:

由于间隙启发式而从进一步计算中省略的顶点总数。

nobfs:

运行反向 BFS 以将良好值分配给高度函数的次数。这包括在整个算法之前运行的初始运行,因此它始终至少为 1。

2. 割和最小割

2.1. igraph_st_mincut — 源顶点和目标顶点之间的最小割。

igraph_error_t igraph_st_mincut(const igraph_t *graph, igraph_real_t *value,
                                igraph_vector_int_t *cut, igraph_vector_int_t *partition,
                                igraph_vector_int_t *partition2,
                                igraph_integer_t source, igraph_integer_t target,
                                const igraph_vector_t *capacity);

查找在所有断开源顶点和目标顶点的边集中总容量最小的边集。

通过调用 igraph_maxflow() 使用最大流技术执行计算。

参数: 

:

输入图。

value:

指向实变量的指针,割的值存储在此处。

cut:

指向已初始化向量的指针,包含在割中的边 ID 存储在此处。如果此参数是空指针,则忽略它。

partition:

指向已初始化向量的指针,割的第一个分区中顶点的顶点 ID 存储在此处。第一个分区始终是包含源顶点的分区。如果此参数是空指针,则忽略它。

partition2:

指向已初始化向量的指针,割的第二个分区中顶点的顶点 ID 存储在此处。第二个分区始终是包含目标顶点的分区。如果此参数是空指针,则忽略它。

来源:

整数,源顶点的 ID。

目标:

整数,目标顶点的 ID。

capacity:

包含边容量的向量。如果为空指针,则认为每条边的容量为 1.0。

返回值: 

错误代码。

另请参阅: 

时间复杂度:请参阅 igraph_maxflow()

2.2. igraph_st_mincut_value — 图中的最小 s-t 割。

igraph_error_t igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *value,
                                      igraph_integer_t source, igraph_integer_t target,
                                      const igraph_vector_t *capacity);

加权(=值)图中的最小 s-t 割是从图中删除以消除从给定顶点(source)到另一个顶点(target)的所有路径所需的最小总边权重。在有向图中考虑有向路径,在无向图中考虑无向路径。

已知两个顶点之间的最小 s-t 割与这两个顶点之间的最大流相同。因此,此函数调用 igraph_maxflow_value() 进行计算。

参数: 

:

输入图。

value:

指向实变量的指针,结果将存储在此处。

来源:

源顶点的 ID。

目标:

目标顶点的 ID。

capacity:

指向容量向量的指针,它应该包含非负数,并且其长度应与图中边的数量相同。它可以是空指针,然后每条边都有单位容量。

返回值: 

错误代码。

时间复杂度:O(|V|^3),另请参见 igraph_maxflow_value() 的讨论,|V| 是顶点的数量。

2.3. igraph_all_st_cuts — 列出有向图中两个顶点之间的所有边割

igraph_error_t igraph_all_st_cuts(const igraph_t *graph,
                                  igraph_vector_int_list_t *cuts,
                                  igraph_vector_int_list_t *partition1s,
                                  igraph_integer_t source,
                                  igraph_integer_t target);

此函数列出源顶点和目标顶点之间的所有边割。每个割恰好列出一次。实现的算法在 JS Provan 和 DR Shier: A Paradigm for listing (s,t)-cuts in graphs, Algorithmica 15, 351--372, 1996 中进行了描述。

参数: 

:

输入图,必须是有向图。

:

整数向量的已初始化列表,割存储在此处。每个向量将包含割中边的 ID。如果此参数是空指针,则忽略它。

partition1s:

整数向量的已初始化列表,生成实际边割的顶点集列表存储在此处。每个向量都包含一组顶点 ID。如果 X 是这样一个集合,则从 X 到 X 的补集的所有边都在图中形成(s,t)边割。如果此参数是空指针,则忽略它。

来源:

源顶点的 ID。

目标:

目标顶点的 ID。

返回值: 

错误代码。

时间复杂度:O(n(|V|+|E|)),其中 |V| 是顶点的数量,|E| 是边的数量,n 是割的数量。

2.4. igraph_all_st_mincuts — 有向图的所有最小 s-t 割。

igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value,
                                     igraph_vector_int_list_t *cuts,
                                     igraph_vector_int_list_t *partition1s,
                                     igraph_integer_t source,
                                     igraph_integer_t target,
                                     const igraph_vector_t *capacity);

此函数列出有向图中两个顶点之间的所有边割,并具有最小的总容量。可能多个割可能具有相同的总容量,尽管在加权图中通常只有一个最小割。建议提供整数值的容量。否则,由于数值舍入误差,可能无法检测到所有最小割。实现的算法在 JS Provan 和 DR Shier: A Paradigm for listing (s,t)-cuts in graphs, Algorithmica 15, 351--372, 1996 中进行了描述。

参数: 

:

输入图,必须是有向图。

value:

指向实数的指针或 NULL。最小割的值存储在此处,除非它是空指针。

:

指向整数向量的已初始化列表的指针或 NULL。割作为顶点 ID 列表存储在此处。

partition1s:

指向整数向量的已初始化列表的指针或 NULL。生成实际边割的顶点集列表存储在此处。每个向量都包含一组顶点 ID。如果 X 是这样一个集合,则从 X 到 X 的补集的所有边都在图中形成(s,t)边割。

来源:

源顶点的 ID。

目标:

目标顶点的 ID。

capacity:

边容量向量。所有容量必须严格为正。如果这是空指针,则假定所有边的容量均为 1。

返回值: 

错误代码。

时间复杂度:O(n(|V|+|E|))+O(F),其中 |V| 是顶点的数量,|E| 是边的数量,n 是割的数量;O(F) 是最大流算法的时间复杂度,请参见 igraph_maxflow()

示例 22.4.  文件 examples/simple/igraph_all_st_mincuts.c

#include <igraph.h>

int main(void) {

    igraph_t g;
    igraph_vector_int_list_t partitions;
    igraph_vector_int_list_t cuts;
    igraph_real_t value;

    igraph_small(&g, 5, IGRAPH_DIRECTED,
                 0, 1, 1, 2, 2, 3, 3, 4,
                 -1);

    igraph_vector_int_list_init(&partitions, 0);
    igraph_vector_int_list_init(&cuts, 0);
    igraph_all_st_mincuts(&g, &value, &cuts, &partitions,
                          /*source=*/ 0, /*target=*/ 4,
                          /*capacity=*/ 0);

    igraph_integer_t i, e, m, n = igraph_vector_int_list_size(&partitions);
    printf("Found %" IGRAPH_PRId " cuts, value: %g\n", n, value);
    for (i = 0; i < n; i++) {
        igraph_vector_int_t *vec = igraph_vector_int_list_get_ptr(&partitions, i);
        igraph_vector_int_t *vec2 = igraph_vector_int_list_get_ptr(&cuts, i);
        printf("Partition %" IGRAPH_PRId ": ", i);
        igraph_vector_int_print(vec);
        if (vec2) {
            printf("Cut %" IGRAPH_PRId ":\n", i);
            m = igraph_vector_int_size(vec2);
            for (e = 0; e < m; e++) {
                igraph_integer_t from = IGRAPH_FROM(&g, VECTOR(*vec2)[e]), to = IGRAPH_TO(&g, VECTOR(*vec2)[e]);
                printf("  %" IGRAPH_PRId " -> %" IGRAPH_PRId "\n", from, to);
            }
        }
    }

    igraph_vector_int_list_destroy(&partitions);
    igraph_vector_int_list_destroy(&cuts);
    printf("\n");
    igraph_destroy(&g);

    return 0;
}


2.5. igraph_mincut — 计算图中的最小割。

igraph_error_t igraph_mincut(const igraph_t *graph,
                             igraph_real_t *value,
                             igraph_vector_int_t *partition,
                             igraph_vector_int_t *partition2,
                             igraph_vector_int_t *cut,
                             const igraph_vector_t *capacity);

此函数计算图中的最小割。最小割是需要删除以断开图的最小边集。使用边的权重(capacity)计算最小值,因此计算具有最小总容量的割。

对于有向图,使用基于计算 2|V|-2 个最大流的实现。对于无向图,我们使用 Stoer-Wagner 算法,如 M. Stoer 和 F. Wagner: A simple min-cut algorithm, Journal of the ACM, 44 585-591, 1997 中所述。

Gregory Benison 首次实现了无向图的实际割计算,谢谢 Greg。

参数: 

:

输入图。

value:

指向浮点数的指针,割的值将存储在此处。

partition:

指向已初始化向量的指针,分离图后,第一个分区中顶点的 ID 将存储在此处。向量将根据需要调整大小。如果此参数为 NULL 指针,则忽略它。

partition2:

指向已初始化向量的指针,第二个分区中顶点的 ID 将存储在此处。向量将根据需要调整大小。如果此参数为 NULL 指针,则忽略它。

cut:

指向已初始化向量的指针,割中边的 ID 将存储在此处。如果此参数为 NULL 指针,则忽略它。

capacity:

一个数字向量,给出边的容量。如果为空指针,则所有边的容量均为 1。

返回值: 

错误代码。

另请参阅: 

igraph_mincut_value(),一个更简单的界面,仅用于计算割的值。

时间复杂度:对于有向图,它是 O(|V|^4),但请参见 igraph_maxflow() 的备注。对于无向图,它是 O(|V||E|+|V|^2 log|V|)。 |V| 和 |E| 分别是顶点和边的数量。

示例 22.5.  文件 examples/simple/igraph_mincut.c

#include <igraph.h>

int print_mincut(const igraph_t *graph, igraph_real_t value,
                 const igraph_vector_int_t *partition,
                 const igraph_vector_int_t *partition2,
                 const igraph_vector_int_t *cut,
                 const igraph_vector_t *capacity) {

    igraph_integer_t i, nc = igraph_vector_int_size(cut);
    igraph_bool_t directed = igraph_is_directed(graph);

    printf("mincut value: %g\n", (double) value);
    printf("first partition:  ");
    igraph_vector_int_print(partition);
    printf("second partition: ");
    igraph_vector_int_print(partition2);
    printf("edges in the cut: ");
    for (i = 0; i < nc; i++) {
        igraph_integer_t edge = VECTOR(*cut)[i];
        igraph_integer_t from = IGRAPH_FROM(graph, edge);
        igraph_integer_t to  = IGRAPH_TO  (graph, edge);
        if (!directed && from > to) {
            igraph_integer_t tmp = from;
            from = to;
            to = tmp;
        }
        printf("%" IGRAPH_PRId "-%" IGRAPH_PRId " (%g), ", from, to, VECTOR(*capacity)[edge]);
    }
    printf("\n");

    return 0;
}

int main(void) {

    igraph_t g;
    igraph_vector_int_t partition, partition2, cut;
    igraph_vector_t weights;
    igraph_real_t value;

    igraph_vector_int_init(&partition, 0);
    igraph_vector_int_init(&partition2, 0);
    igraph_vector_int_init(&cut, 0);

    /* -------------------------------------------- */

    igraph_small(&g, 0, IGRAPH_UNDIRECTED,
                 0, 1, 0, 4, 1, 2, 1, 4, 1, 5, 2, 3, 2, 6, 3, 6, 3, 7, 4, 5, 5, 6, 6, 7,
                 -1);
    igraph_vector_init_int_end(&weights, -1, 2, 3, 3, 2, 2, 4, 2, 2, 2, 3, 1, 3, -1);

    igraph_mincut(&g, &value, &partition, &partition2, &cut, &weights);
    print_mincut(&g, value, &partition, &partition2, &cut, &weights);

    igraph_vector_destroy(&weights);
    igraph_destroy(&g);

    /* -------------------------------------------- */

    igraph_small(&g, 6, IGRAPH_DIRECTED,
                 0, 1, 1, 2, 2, 3, 0, 5, 5, 4, 4, 3, 3, 0, -1);
    igraph_vector_init_int_end(&weights, -1, 3, 1, 2, 10, 1, 3, 2, -1);

    igraph_mincut(&g, &value, &partition, &partition2, &cut, &weights);
    print_mincut(&g, value, &partition, &partition2, &cut, &weights);

    igraph_vector_destroy(&weights);
    igraph_destroy(&g);

    /* -------------------------------------------- */

    igraph_small(&g, 5, IGRAPH_DIRECTED,
                 4, 3, 3, 2, 2, 1, 1, 0,
                 -1);
    igraph_vector_init_int_end(&weights, -1, 1, 1, 1, 1, -1);
    igraph_mincut(&g, &value, &partition, &partition2, &cut, &weights);
    print_mincut(&g, value, &partition, &partition2, &cut, &weights);

    igraph_vector_destroy(&weights);
    igraph_destroy(&g);

    /* -------------------------------------------- */

    igraph_vector_int_destroy(&cut);
    igraph_vector_int_destroy(&partition2);
    igraph_vector_int_destroy(&partition);

    return 0;
}


2.6. igraph_mincut_value — 图中的最小边割。

igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res,
                                   const igraph_vector_t *capacity);

图中的最小边割是需要从图中删除以使图强连通的边的最小总权重。(如果原始图不是强连通的,则此值为零。)请注意,在无向图中,强连通性与弱连通性相同。

可以使用最大流技术计算最小割,尽管当前实现仅对有向图执行此操作,并且对无向图使用单独的非基于流的实现。请参见 Mechthild Stoer 和 Frank Wagner: A simple min-cut algorithm, Journal of the ACM 44 585--591, 1997。对于有向图,在固定顶点和图中的所有其他顶点之间计算最大流,并且在两个方向上都执行此操作。然后取最小值以获得最小割。

参数: 

:

输入图。

res:

指向实变量的指针,结果将存储在此处。

capacity:

指向容量向量的指针,它应包含与图中边数相同的非负数。如果为空指针,则所有边的容量均为 1。

返回值: 

错误代码。

另请参阅: 

时间复杂度:无向图为 O(log(|V|)*|V|^2),有向图为 O(|V|^4),但请参见 igraph_maxflow_value() 文档中的讨论。

2.7. igraph_gomory_hu_tree — 图的 Gomory-Hu 树。

igraph_error_t igraph_gomory_hu_tree(const igraph_t *graph,
                                     igraph_t *tree,
                                     igraph_vector_t *flows,
                                     const igraph_vector_t *capacity);

Gomory-Hu 树是图中所有最大流(或最小割)值的简洁表示。树的顶点与原始图中的顶点完全对应,顺序相同。Gomory-Hu 树的边用流值注释。原始图中任意 (u,v) 顶点对之间的最大流(或最小割)的值然后由 Gomory-Hu 树中 u 和 v 之间的最短路径上的最小流值(即边注释)给出。

此实现使用 Gusfield 的算法来构造 Gomory-Hu 树。有关更多详细信息,请参见以下论文

参考

Gusfield D: Very simple methods for all pairs network flow analysis. SIAM J Comput 19(1):143-155, 1990 https://doi.org/10.1137/0219009.

参数: 

:

输入图。

tree:

指向未初始化的图的指针;结果将存储在此处。

flows:

指向未初始化向量的指针;与 Gomory-Hu 树中每条边对应的流值将在此处返回。如果您对流值不感兴趣,可以在此处传递 NULL 指针。

capacity:

包含边容量的向量。如果为 NULL,则认为每条边的容量为 1.0。

返回值: 

错误代码。

时间复杂度:O(|V|^4),因为它在顶点零和每个其他顶点之间执行最大流计算,而最大流为 O(|V|^3)。

另请参阅: 

3. 连通性

3.1. igraph_st_edge_connectivity — 一对顶点的边连通度。

igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph,
                                           igraph_integer_t *res,
                                           igraph_integer_t source,
                                           igraph_integer_t target);

两个顶点(sourcetarget)的边连通度是必须从图中删除以消除从 sourcetarget 的所有路径的最小边数。

此函数使用最大流算法来计算边连通度。

参数: 

:

输入图,必须是有向图。

res:

指向整数的指针,结果将存储在此处。

来源:

源顶点的 ID。

目标:

目标顶点的 ID。

返回值: 

错误代码。

时间复杂度:O(|V|^3)。

另请参阅: 

3.2. igraph_edge_connectivity — 图中的最小边连通度。

igraph_error_t igraph_edge_connectivity(const igraph_t *graph,
                                        igraph_integer_t *res,
                                        igraph_bool_t checks);

这是图表中所有顶点对的边连通度的最小值。

图的边连通度与 Douglas R. White 和 Frank Harary: The cohesiveness of blocks in social networks: node connectivity and conditional density, Sociological Methodology 31:305--359, 2001 https://doi.org/10.1111/0081-1750.00098 中定义的组粘附力相同。

参数: 

:

输入图。

res:

指向整数的指针,结果将存储在此处。

checks:

布尔常量。是否检查图是否已连接以及顶点的度数。如果图未(强)连接,则连通性显然为零。否则,如果最小度数为 1,则边连通性也为 1。执行这些检查是个好主意,因为与连通性计算本身相比,它们可以快速完成。彼得·麦克马洪 (Peter McMahan) 提出了这些建议,谢谢彼得。

返回值: 

错误代码。

时间复杂度:无向图为 O(log(|V|)*|V|^2),有向图为 O(|V|^4),但请参见 igraph_maxflow_value() 文档中的讨论。

另请参阅: 

3.3. igraph_st_vertex_connectivity — 一对顶点的顶点连通度。

igraph_error_t igraph_st_vertex_connectivity(
        const igraph_t *graph,
        igraph_integer_t *res,
        igraph_integer_t source,
        igraph_integer_t target,
        igraph_vconn_nei_t neighbors);

两个顶点(sourcetarget)的顶点连通度是必须删除的最小顶点数,以消除从 sourcetarget 的所有路径。在有向图中考虑有向路径。

一对顶点的顶点连通度与从源到目标的不同(即节点独立)路径的数量相同,前提是它们之间没有直接边。

当前实现使用最大流计算来获得结果。

参数: 

:

输入图。

res:

指向整数的指针,结果将存储在此处。

来源:

源顶点的 ID。

目标:

目标顶点的 ID。

neighbors:

一个常量,用于给出如果两个顶点连接时该怎么做。可能的值:IGRAPH_VCONN_NEI_ERROR,停止并显示错误消息,IGRAPH_VCONN_NEI_NEGATIVE,返回 -1。IGRAPH_VCONN_NEI_NUMBER_OF_NODES,返回节点数。IGRAPH_VCONN_NEI_IGNORE,忽略两个顶点已连接的事实,并计算消除除 sourcevertex 之间的平凡(直接)路径之外的所有路径所需的顶点数。

返回值: 

错误代码。

时间复杂度:O(|V|^3),但请参见 igraph_maxflow_value() 的讨论。

另请参阅: 

3.4. igraph_vertex_connectivity — 图的顶点连通度。

igraph_error_t igraph_vertex_connectivity(
        const igraph_t *graph, igraph_integer_t *res,
        igraph_bool_t checks);

图的顶点连通度是图中每对顶点的最小顶点连通度。

图的顶点连通度与 Douglas R. White 和 Frank Harary: The cohesiveness of blocks in social networks: node connectivity and conditional density, Sociological Methodology 31:305--359, 2001 https://doi.org/10.1111/0081-1750.00098 中定义的组内聚力相同。

参数: 

:

输入图。

res:

指向整数的指针,结果将存储在此处。

checks:

布尔常量。是否检查图是否已连接或完整以及顶点的度数。如果图未(强)连接,则连通性显然为零。否则,如果最小度数为 1,则顶点连通性也为 1。如果图是完整的,则连通性是顶点数减 1。执行这些检查是个好主意,因为与连通性计算本身相比,它们可以快速完成。彼得·麦克马洪 (Peter McMahan) 提出了这些建议,谢谢彼得。

返回值: 

错误代码。

时间复杂度:O(|V|^5)。

另请参阅: 

4. 边不相交路径和顶点不相交路径

4.1. igraph_edge_disjoint_paths — 两个顶点之间的最大边不相交路径数。

igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph,
                                          igraph_integer_t *res,
                                          igraph_integer_t source,
                                          igraph_integer_t target);

如果两个顶点之间的一组路径不共享任何边,则称为边不相交。此函数使用最大流技术计算最大边不相交路径数。在有向图中考虑有向路径。

请注意,不相交路径的数量与使用统一边权重的两个顶点的边连通度相同。

参数: 

:

输入图,可以是定向图或无向图。

res:

指向整数变量的指针,结果将存储在此处。

来源:

源顶点的 ID。

目标:

目标顶点的 ID。

返回值: 

错误代码。

时间复杂度:O(|V|^3),但请参见 igraph_maxflow_value() 的讨论。

另请参阅: 

4.2. igraph_vertex_disjoint_paths — 两个顶点之间最大数量的顶点不相交路径。

igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph,
                                            igraph_integer_t *res,
                                            igraph_integer_t source,
                                            igraph_integer_t target);

如果两个顶点之间的路径除了端点外没有共享顶点,则称这些路径是不相交的。此函数计算可以在源顶点和目标顶点之间构建的最大此类路径数。计算是通过使用最大流技术进行的。

当从源顶点到目标顶点没有边时,顶点不相交路径的数量与两个顶点的顶点连通性相同。当存在一些边时,每个边都会贡献一条额外的路径。

参数: 

:

输入图。

res:

指向整数变量的指针,结果将存储在此处。

来源:

源顶点的 ID。

目标:

目标顶点的 ID。

返回值: 

错误代码。

时间复杂度:O(|V|^3)。

另请参阅: 

5. 图的粘附性和凝聚性

5.1. igraph_adhesion — 图的粘附性,这(几乎)与边连通性相同。

igraph_error_t igraph_adhesion(const igraph_t *graph,
                               igraph_integer_t *res,
                               igraph_bool_t checks);

此数量由 White 和 Harary 在 The cohesiveness of blocks in social networks: node connectivity and conditional density, (Sociological Methodology 31:305--359, 2001) 中定义,基本上它是具有均匀边权重的图的边连通性。

参数: 

:

输入图,可以是有向图或无向图。

res:

指向整数的指针,结果将存储在此处。

checks:

布尔常量。是否检查图是否已连接以及顶点的度。如果图未(强)连接,则粘附性显然为零。否则,如果最小度为 1,则粘附性也为 1。执行这些检查是一个好主意,因为与边连通性计算本身相比,它们可以快速完成。它们由 Peter McMahan 提出,感谢 Peter。 *

返回值: 

错误代码。

时间复杂度:无向图为 O(log(|V|)*|V|^2),有向图为 O(|V|^4),但请参见 igraph_maxflow_value() 文档中的讨论。

另请参阅: 

5.2. igraph_cohesion — 图的凝聚性,这与顶点连通性相同。

igraph_error_t igraph_cohesion(const igraph_t *graph,
                               igraph_integer_t *res,
                               igraph_bool_t checks);

此数量由 White 和 Harary 在 社会网络中块的凝聚力:节点连通性和条件密度, (Sociological Methodology 31:305--359, 2001) 中定义,它与图的顶点连通性相同。

参数: 

:

输入图。

res:

指向整数变量的指针,结果将存储在此处。

checks:

布尔常量。是否检查图是否已连接以及顶点的度。如果图未(强)连接,则凝聚力显然为零。否则,如果最小度为 1,则凝聚力也为 1。执行这些检查是一个好主意,因为与顶点连通性计算本身相比,它们可以快速完成。它们由 Peter McMahan 提出,感谢 Peter。

返回值: 

错误代码。

时间复杂度:O(|V|^4),|V| 是顶点数。实际上它更像是 O(|V|^2),请参阅 igraph_maxflow_value()

另请参阅: 

6. 凝聚块

6.1. igraph_cohesive_blocks — 识别图的分层凝聚块结构。

igraph_error_t igraph_cohesive_blocks(const igraph_t *graph,
                           igraph_vector_int_list_t *blocks,
                           igraph_vector_int_t *cohesion,
                           igraph_vector_int_t *parent,
                           igraph_t *block_tree);

凝聚块是一种基于其结构凝聚力(或顶点连通性)确定图顶点分层子集的方法。对于给定的图 G,如果 S 没有顶点连通性大于或等于 k 的超集,则称其顶点子集 S 是最大 k 凝聚的。凝聚块是一种过程,通过该过程,给定一个 k 凝聚顶点集,以递归方式识别最大 l 凝聚子集,其中 l>k。因此,可以找到一个顶点子集层次结构,整个图 G 位于其根。

此函数实现凝聚块并计算图的完整凝聚块层次结构。

有关详细信息,请参阅以下参考

J. Moody 和 D. R. White。Structural cohesion and embeddedness: A hierarchical concept of social groups。American Sociological Review, 68(1):103--127, Feb 2003。 https://doi.org/10.2307/3088904

参数: 

:

输入图。它必须是无向的和简单的。请参阅 igraph_is_simple()

:

如果不是空指针,则它必须是一个初始化的整数向量列表;凝聚块将存储在此处。每个块都用一个类型为 igraph_vector_int_t 的向量进行编码,该向量包含块的顶点 ID。

凝聚力:

如果不是空指针,则它必须是一个初始化的向量,并且块的凝聚力存储在此处,顺序与 blocks 向量列表中的块相同。

父级:

如果不是空指针,则它必须是一个初始化的向量,并且块层次结构存储在此处。对于每个块,存储其父块的 ID(即 blocks 向量列表中的位置)。对于层次结构中的顶部块,存储 -1

块树:

如果不是空指针,则它必须是指向未初始化的图的指针,并且块层次结构将作为 igraph 图存储在此处。顶点 ID 对应于 blocks 向量中块的顺序。

返回值: 

错误代码。

时间复杂度:待定。

示例 22.6.  文件 examples/simple/cohesive_blocks.c

#include <igraph.h>

int main(void) {
    igraph_t g;
    igraph_vector_int_list_t blocks;
    igraph_vector_int_t cohesion;
    igraph_vector_int_t parent;
    igraph_t block_tree;
    igraph_integer_t i;

    igraph_famous(&g, "zachary");
    igraph_vector_int_list_init(&blocks, 0);
    igraph_vector_int_init(&cohesion, 0);
    igraph_vector_int_init(&parent, 0);

    igraph_cohesive_blocks(&g, &blocks, &cohesion, &parent,
                           &block_tree);

    printf("Blocks:\n");
    for (i = 0; i < igraph_vector_int_list_size(&blocks); i++) {
        igraph_vector_int_t *sg = igraph_vector_int_list_get_ptr(&blocks, i);
        printf("  ");
        igraph_vector_int_print(sg);
    }
    printf("Cohesion:\n  ");
    igraph_vector_int_print(&cohesion);
    printf("Parents:\n  ");
    igraph_vector_int_print(&parent);
    printf("Block graph:\n");
    igraph_write_graph_edgelist(&block_tree, stdout);

    igraph_vector_int_list_destroy(&blocks);
    igraph_vector_int_destroy(&cohesion);
    igraph_vector_int_destroy(&parent);
    igraph_destroy(&block_tree);

    igraph_destroy(&g);

    return 0;
}