【问题标题】:Comparing two graphs比较两个图表
【发布时间】:2013-03-27 00:08:55
【问题描述】:

我需要比较许多图表(最多几百万个图表比较),我想知道最快的方法是什么。

图的顶点最多可以有 8 个邻居/边,并且顶点的值可以是 0 或 1。旋转图仍然是同一个图,每个图都有相同数量的顶点。

图表可能如下所示:

现在我通过从第一个图中获取一个顶点并将其与第二个图中的每个顶点进行比较来比较图。如果找到相同的顶点,则检查两个顶点的邻居是否相同,然后重复此操作,直到知道图形是否相同为止。

这种方法太慢了。在不丢弃肯定不同的图的情况下,比较几千个图和大约一百个顶点需要 40 多秒。

我正在考虑计算每个图表的唯一值,然后只比较值。我试图这样做,但我只设法提出如果相等则图形可能相等的值,如果值不同则图形也不同。
如果我的程序比较这些值,那么它会在大约 2.5 秒内计算所有内容(这仍然太慢)。

向该图添加顶点并更新边的最佳/最快方法是什么?现在我将此图存储在 std::map< COORD, Vertex > 中,因为我认为这样搜索顶点更容易/更快。
COORD 是游戏板上的顶点位置(顶点位置与比较图无关),顶点是:

struct Vertex
{
    Player player; // Player is enum, FIRST = 0, SECOND = 1
    Vertex* neighbours[8];
};

此图表示 Gomoku 的当前棋盘状态,在棋盘边缘环绕,棋盘大小为 n*n,其中 n 最大为 2^16。

我希望我在写这篇文章时没有犯太多错误。我希望有人可以帮助我。

【问题讨论】:

  • 我无法证明这一点,但我认为可能有一种方法可以创建图形的标准化表示。然后,您需要一个将图形映射到其规范化形式的转换函数。证明所有等效图具有相同的归一化形式,然后您可以将问题简化为两次转换和一次比较。
  • 要判断两个图是否相似,可以检查以下因素:顶点数,边数,入度和出度,欧拉环,欧拉路径..等
  • 你的问题没有很好的定义。什么时候两张图“相同”?是不是它们同构为未标记的图?然后你试图解决图同构问题,它是 NP,但不知道是 P 或 NP-hard。有一个名为“nauty”的库为其实现标准算法。
  • 另一条评论:为什么要将五子棋棋盘存储为如此复杂的结构而不是二维数组?可以使用模运算来实现板边缘的环绕。
  • 只是一个建议。如果您愿意改用 boost 图形库,这对您来说可能会很有趣。 boost.org/doc/libs/1_39_0/libs/graph/doc/isomorphism.html

标签: c++ algorithm data-structures graph


【解决方案1】:

抱歉,我还没有能力发表评论。这并不完全是一个答案,而是一个可能的优化建议。

我建议尝试记忆(存储所有发现不同的顶点对),以便下次比较这两个顶点时,您只需进行简单的查找和回复。这可能会提高性能(或恶化性能),具体取决于您拥有的图表类型。

【讨论】:

    【解决方案2】:

    您要解决的问题称为graph isomorphism

    问题出在 NP 中(尽管不知道它是否是 NP-Complete)并且没有找到它的多项式时间算法。

    您描述的算法似乎需要指数级时间。

    【讨论】:

    • 不知道它是 NP 完全的,而不是“不是 NP 完全的”。
    • P 中有一些算法适用于平面图等特殊情况。他的图表很特别,几乎是平面的,也许有机会。
    • 图同构没有考虑节点可能有值(这里是 0 和 1)。
    • @abeln:您的编辑仍然不正确,不知道问题是 NP 完全的,在 P 中,或者两者都不是(或两者都有:))。唯一已知的是它在 NP 中:给定一个同构,可以很快证明它是正确的。所以它可能仍然是 P!=NP 但图同构在 P 中。也可能是 P!=NP 并且图同构是 NP 完全的。
    • @Thomas:我从 Wikipedia 误读:“众所周知,图同构问题位于 NP 类的低层次,这意味着它不是 NP 完全的,除非多项式时间层次崩溃为它的第二层”
    【解决方案3】:

    首先,您需要将每个图形转换为一致的表示,这样做的自然方法是创建图形的有序表示。

    第一级排序是通过根据邻居的数量进行分组来实现的。

    每个具有相同数量邻居的节点组通过将其邻居值(0 和 1)映射到一个二进制数上进行排序,然后用于在组节点之间强制执行顺序。

    然后您可以使用散列函数以有序形式迭代每个组的每个节点。然后可以使用散列值来提供加速查找

    【讨论】:

    【解决方案4】:

    您发现自己可以通过检查一个边框的所有 n*n 移位乘以另一个边框旋转 8 次来检查同构,因此具有O(n^3) 复杂性。

    这可以简化为O(n^2)。让我们只向一个方向移动,比如移动x 轴。然后我们只需要找到正确的y-offset。为此,我们将两个图的元素连接如下:

    . . 1 .            0 . . 3
    0 1 . .     =>     0 1 2 .     =>     0 3 0 1 2 0 2
    . 0 . .            0 . 2 .
    _______            
    1 2 3 4            ^---- a 0 indicates start of a row
    

    我们得到两个大小为n 的数组,我们必须检查一个是否是另一个的循环置换。为此,我们将数组 a 与其自身连接并搜索另一个。

    例如,如果两个数组是 a=0301202b=0203012,我们使用 KMP 或类似方法在 03012020301202 中搜索 0203012,它在 O(n + 2n)=O(n) 时间运行(我们可以摆脱整个预处理,因为第一个数组总是相同的)。

    将此O(n) x-check 与n y-shifts 和8 旋转相结合,通过使用O(n) 额外空间为O(n^2) 提供了整体复杂性。

    【讨论】:

    • 只有 4 圈。 O(n^2) 复杂度不是太大了吗?特别是如果 n 可以大到 2^16 并且我将不得不检查数千块板。与地图大小相比,船上的棋子数量可能非常少,这就是为什么我试图考虑一些复杂性仅取决于棋子数量的算法。
    • 我认为确定性算法不会比O(n^2) 更快。
    • 但是比较复杂性不能独立于电路板尺寸吗?你能举出更好的例子来说明你的方法吗?如何比较例如:. 1 .0 . .. 1 .1 . .. . 01 . .
    • 到棋盘大小:如果0 0 0 0 0 ... 0 (z 乘以0) 被-z 替换,则此算法与棋盘大小无关。我的意思是n 是节点数。
    • 举个例子:首先生成x-direction 中的所有循环排列,然后计算y-direction 中的偏移量。在您的示例中,y-offset 等于 0
    猜你喜欢
    • 1970-01-01
    • 2018-02-16
    • 2011-10-04
    • 2018-02-25
    • 2012-03-01
    • 2012-06-21
    • 2011-06-19
    • 2015-06-14
    • 1970-01-01
    相关资源
    最近更新 更多