在给定图形中的连接的情况下查找所有连接的组件(Java)

时间:2011-09-25 12:40:57

标签: java networking graph components simulation

我正在用Java构建一个模拟移动节点网络的程序。当节点发生碰撞时,他们有可能建立连接。我试图以一种使连接组件显而易见的方式存储图中的所有节点,因此我将其存储为一组列表,其中每个列表是一个组件,集合包含所有组件。 (每个组件的大小必须大于1。)

完整的代码在github上:https://github.com/IceCreamYou/ViralSpread

但这是重要的部分:

    // Store the components in the "temp" variable while we calculate them.
    // Each list is a component; the set contains all components.
    HashSet<LinkedList<Person>> temp = new HashSet<LinkedList<Person>>();
    // hardConnections is a set of all connections between the nodes.
    // Each connection appears only once (e.g. if there is a connection from A to B
    // there will not be a connection from B to A -- however each connection is
    // considered bidirectional).
    for (Person[] connection : hardConnections) {
        // Each element of hardConnections is an array
        // containing two connected nodes.
        // "Person" is the node class.
        Person a = connection[0], b = connection[1];
        boolean bFound = false, aFound = false;
        // If we've already added one of the nodes to a component,
        // add the other one to it.
        for (LinkedList<Person> component : temp) {
            boolean bInComponent = false, aInComponent = false;
            for (Person p : component) {
                if (p.samePosition(b)) {
                    bInComponent = true;
                }
                if (p.samePosition(a)) {
                    aInComponent = true;
                }
            }
            if (bInComponent && !aInComponent) {
                component.add(a);
                bFound = true;
                break;
            }
            else if (!bInComponent && aInComponent) {
                component.add(b);
                aFound = true;
                break;
            }
            else if (bInComponent && aInComponent) {
                aFound = true;
                bFound = true;
                break;
            }
        }
        // If neither node is in a component, create a new component containing both nodes.
        if (!bFound && !aFound) {
            LinkedList<Person> newComponent = new LinkedList<Person>();
            newComponent.add(b);
            newComponent.add(a);
            temp.add(newComponent);
        }
    }
    components = temp;

p.samePosition()检查节点是否具有相同的X和Y位置。我正在使用该方法而不是equals(),以防问题与等式检查失败有关,即使我没有覆盖Person类中的任何内容(包括equals(),hashCode()等)。

这似乎在大部分时间都有效。不幸的是,有时在将新连接添加到现有组件的组件之后,此代码会将新的单个组件计算为两个组件大小不正确。

我知道问题也可能是我正在计算连接错误。但是,连接在屏幕上正确显示,并且它们的数量正确(在集合中 - 不仅仅是在屏幕上计数),因此错误可能不存在。以防万一,这是计算连接的代码。它比上面的代码更不通用,所以可能更难阅读。

    // "infected" and "infector" are the nodes.
    // sameColor is true if the nodes have the same type.
    // Nodes with the same type should retain their connections
    // (which are also to nodes of the same type) while if the nodes have different types
    // then the "infected" node will lose its connections because
    // it will no longer be the same type as those connections.
    // Note that nodes of different types should never have connections to each other.
    boolean sameColor = (infected.getVirus().equalsVirus(infector.getVirus()));
    boolean sameColorConnectionExists = false;
    // As above, hardConnections is a set of connections between nodes (the Person class)
    Iterator<Person[]> it = hardConnections.iterator();
    while (it.hasNext()) {
        Person[] connection = it.next();
        if (sameColor) {
            if ((infected.equals(connection[0]) && infector.equals(connection[1])) ||
                    (infected.equals(connection[1]) && infector.equals(connection[0]))) {
                sameColorConnectionExists = true;
            }
        }
        // Remove connections to the connected person.
        else if (infected.equals(connection[0]) || infected.equals(connection[1])) {
            it.remove();
        }
    }

    // Create a new connection if the nodes were of different types or
    // if they are the same type but no connection exists between them.
    if (!sameColor || !sameColorConnectionExists) {
        hardConnections.add(new Person[] {infected, infector});
    }
    // After this function returns, the infected node is changed to the type of the infector.

基本上发生的事情是我们遍历连接集并删除与受感染节点的任何连接,这些连接不是来自相同类型的节点。然后,如果感染节点和感染节点之间的连接不存在,我们添加一个。

我无法弄清楚这里的bug在哪里......任何帮助(甚至是项目本身的其他提示/评论)都会受到赞赏。如果您正在查看github中的代码,则有问题的代码位于Statistics.java函数updateConnections()和updateComponents()中。 updateConnections()在transferVirus()中从Person.java调用。

感谢。


更新

我一直在输出调试信息,试图弄清楚发生了什么。我已经隔离了一次导致错误碎片的碰撞。在下面的数据中,第一部分显示图的边缘,第二部分显示连接的组件以及它们中的节点数。正如您所看到的,在第二次碰撞之后,有一个额外的“深灰色”组件不应该存在。只有一个“深灰色”组件,它有4个节点。 (截图:http://screencast.com/t/f9PIzjuk

----
blue:(489, 82)          blue:(471, 449)
blue:(416, 412)         blue:(471, 449)
pink:(207, 172)         pink:(204, 132)
dark gray:(51, 285)     dark gray:(635, 278)
dark gray:(635, 278)    dark gray:(746, 369)
dark gray:(51, 285)     dark gray:(737, 313)
dark gray:(737, 313)    dark gray:(635, 278)
cyan:(2, 523)           cyan:(473, 273)

3 blue
2 pink
4 dark gray
2 cyan
----
blue:(514, 79)          blue:(535, 435)
dark gray:(725, 326)    dark gray:(717, 365)
blue:(404, 404)         blue:(535, 435)
pink:(197, 186)         pink:(236, 127)
dark gray:(35, 283)     dark gray:(619, 271)
dark gray:(619, 271)    dark gray:(717, 365)
dark gray:(35, 283)     dark gray:(725, 326)
dark gray:(725, 326)    dark gray:(619, 271)
cyan:(1, 505)           cyan:(490, 288)

3 blue
2 pink
4 dark gray
2 dark gray
2 cyan
----

在更多地考虑我需要哪些信息以便在不产生大量信息的情况下进行调试时,我在模拟过程中产生了快照的结果:

red:(227, 344)      red:(257, 318)
red:(643, 244)      red:(437, 140)
red:(437, 140)      red:(257, 318)
orange:(573, 485)       orange:(255, 143)
orange:(255, 143)       orange:(20, 86)
red:(227, 344)      red:(437, 140)
orange:(727, 494)       orange:(573, 485)

3 red
    red:(257, 318)
    red:(227, 344)
    red:(437, 140)
4 orange
    orange:(255, 143)
    orange:(573, 485)
    orange:(20, 86)
    orange:(727, 494)
2 red
    red:(437, 140)
    red:(643, 244)

如果你遵循这里的逻辑:

  • 查看第一个连接。目前还没有任何组件,因此请使用red:(227, 344), red:(257, 318)创建一个新组件。
  • 看看第二个连接。只有一个组件,组件中的任何节点都不匹配连接中的任何节点,因此创建第二个组件red:(227, 344), red:(257, 318)这是错误的,因为列表后面有其他节点将当前连接中的节点连接到原始组件。
  • 看看第三个连接。第二个节点与第一个组件中的节点匹配,因此将第一个节点添加到第一个组件。
  • ......等等。

所以现在我知道我找到连接组件的算法实际上是错误的。我想我需要对此进行一些研究。我的第一个想法是采取以下方法:

  • 创建连接列表的副本。
  • 当我们查看第一对时,我们还没有任何组件,因此将第一个连接中的节点添加到新的(第一个)组件,并从副本中删除该连接。
  • 查看每个连续的连接。如果连接中的一个节点位于第一个组件中:
    • 将其节点添加到组件
    • 从副本中删除该连接
    • 返回到我们添加到组件的节点的最后一个连接,并检查该连接与当前连接之间的每个连接,以查看它们是否现在连接到组件。如果是,请将它们添加到组件中并从副本中删除它们。递归地做这件事。
  • 当我们结束时,重复从(现在减少的)副本开始而不是所有连接列表的过程。

对此方法的任何想法都会受到赞赏。

1 个答案:

答案 0 :(得分:0)

对上述OP的评论 -

对于任何感兴趣的人,我完全重新设计了算法(它基本上将一组独特的双向边缘映射到一组连接的组件[size> 2])。您可以在https://github.com/IceCreamYou/ViralSpread/blob/master/Statistics.java

的github仓库中找到它