将碰撞的物体合并为一个

Merge collided bodies as one

我正在研究 Javascript 中的一些物理知识,基本上想将碰撞体作为一个整体来处理。

我有一个包含所有发生的碰撞的数组,一个碰撞由两个物体组成,物体 A 和物体 B。

假设发生六次碰撞,物体之间的碰撞:

  1. X 和 Y
  2. Y 和 Z
  3. C 和 D
  4. E 和 F
  5. F 和 H
  6. G 和 H

现在我想将所有以某种方式连接的物体合并成一个物体。我想要列表中的那些合并的机构。例如,在这种情况下,我想要一个如下所示的列表:

  1. X、Y、Z(因为X和Y碰撞,Y和Z碰撞)
  2. C和D(因为C和D相撞)
  3. E、F、G、H(因为E与F相撞,F与H相撞,G与H相撞)

现在我很确定那里有一些我需要的算法,我只是不知道去哪里找,我也没有办法自己解决这个问题。

你在现实生活中会怎么做?

我想我会阅读每条规则。对于每条规则,我会将这两个部分连接起来。我最终得到的是一组 blob。然后我可以遍历每个图表以获取每个图表中的节点列表。每个 "connected component" 将是一个 "blob"。稍微形式化这个算法可能会得到这个:

// make the graph of connected components
nodes = map<symbol, pair<symbol, list<symbol>>>
for each (a, b) in rules do
    if nodes[a] is null then nodes[a] = node(a, [b])
    else nodes[a].connections.append(b)

    if nodes[b] is null then nodes[b] = node(b, [a])
    else nodes[b].connections.append(a)
loop

blobs = map<symbol, list<symbol>>
for each (a, b) in rules do

    firstNode = nodes[a]
    // do a DFS/BFS search starting from firstNode to find
    // all nodes in the connected component. whenever you
    // follow a link from a node, remove it from the node's
    // list of links. this prevents ever searching from that
    // node again since we know what component it's in already
    // add each node to the list of symbols in blobs[a]

loop

在第一个循环中,我们读取每条规则一次,然后做恒定量的工作,所以在规则数量上是O(n)次。它将为每个规则存储两个连接,就规则数而言,O(n) 存储也是如此。

在第二个循环中,我们查看每个规则并为每个规则的 LHS 符号执行 DFS 或 BFS。但是,请注意搜索只会遍历任何边一次,因此这是规则数量的 O(n) 时间。我们最终会得到一组 blob,其列表的并集将是不超过规则数的符号集,因此它也是 O(n) 存储。

所以我们有一个 O(n) 时间,O(n) space 复杂度算法来确定斑点。渐进地说,我们能做得更好吗?显然我们需要查看所有 n 条规则,所以时间复杂度是最优的。另请注意,此问题的任何解决方案都必须说明每个符号最终属于哪个 blob,因此只需将答案写在输出磁带上就需要 O(n) space。所以这也应该是最优的。

如果您有一个包含所有对象的 ADT(在本例中为地图)并且您保留父 ID 以跟踪对象碰撞,您可以在恒定时间内处理每个碰撞+合并。

// setup
var X = {id: 1, name:'X'};
var Y = {id: 2, name:'Y'};
var Z = {id: 3, name:'Z'};
var C = {id: 4, name:'C'};
var D = {id: 5, name:'D'};
var E = {id: 6, name:'E'};
var F = {id: 7, name:'F'};
var G = {id: 8, name:'G'};
var H = {id: 9, name:'H'};
var all = { 1:X, 2:Y, 3:Z, 4:C, 5:D, 6:E, 7:F, 8:G, 9:H };

// method to merge collided objects together
function collision(obj1, obj2) {
    var p1 = obj1.parent;
    var p2 = obj2.parent;
    if(p1 === undefined && p2 === undefined) {
        obj1.parent = obj1.id;
        obj2.parent = obj1.id;
        obj1.name += obj2.name;
        delete all[obj2.id];
    } else if(p1 !== undefined && p2 === undefined) {
        obj2.parent = obj1.parent;
        all[obj1.parent].name += obj2.name;
        delete all[obj2.id];
    } else if(p1 === undefined && p2 !== undefined) {
        obj1.parent = obj2.parent;
        all[obj2.parent].name += obj1.name;
        delete all[obj1.id];
    } else if(p1 !== undefined && p2 !== undefined && obj1.parent !== obj2.parent) {
        if(all[obj1.parent] !== undefined) {
            all[obj1.parent].name += all[obj2.parent].name;
            delete all[obj2.parent];
        } else if(all[obj2.parent] !== undefined) {
            all[obj2.parent].name += all[obj1.parent].name;
            delete all[obj1.parent];
        }
    }
}

// test
console.log(JSON.stringify(all));
collision(X, Y);
collision(Y, Z);
collision(C, D);
collision(E, F);
collision(F, H);
collision(G, H);
console.log(JSON.stringify(all));
collision(X, E);
console.log(JSON.stringify(all));

{"1":{"id":1,"name":"X"},"2":{"id":2,"name":"Y"},"3":{"id":3,"name":"Z"},"4":{"id":4,"name":"C"},"5":{"id":5,"name":"D"},"6":{"id":6,"name":"E"},"7":{"id":7,"name":"F"},"8":{"id":8,"name":"G"},"9":{"id":9,"name":"H"}}

{"1":{"id":1,"name":"XYZ","parent":1},"4":{"id":4,"name":"CD","parent":4},"6":{"id":6,"name":"EFHG","parent":6}}

{"1":{"id":1,"name":"XYZEFHG","parent":1},"4":{"id":4,"name":"CD","parent":4}}