寻找 DAG 中 2 个顶点之间的最短路径(未加权)

发布于 2024-09-07 06:21:58 字数 3452 浏览 1 评论 0原文

在 Floyd-Warshall/Dijkstra 回复洪水之前,请让我解释一下情况,因为我确信可以针对这种情况调整任一算法,而且必须如此,因为这不是一个玩具示例程序(请注意,在 java 中)所以必须保持它在内存方面的可管理性)

我拥有的是从节点 0 到节点 n 生成的网络图,节点 3 无法链接到节点 5,因为当节点 3 选择它的 out links< 时,节点 5 不存在/em>.每个“节点”都表示为 in_neighbours[nodeID] 和 out_neighbours[nodeID] ,比如 nodeId=3,所以我们讨论的是节点 3。还要注意 in_/out_ 都是排序的,(in_ 自然排序,因为 5 会选择它的 out 链接全部同时存在,只有 6 才会选择 out_links,因此 3 的 in_ 永远不能包含 {6, 5, 7}) 和 ofc 都可以包含重复项。 (in/out 是大小为 n 的 ArrayList 数组,其中 out_ 的大小始终为 d 或 m,它与 n 一起由用户在启动时指定)

无权重。我必须做的是找到averageDistance()

public double getAvgDistance() {
    int sum = 0;        

    for (int i=1; i<n; i++) {
        for (int j=0; j < i; j++) {
            sum += dist(i, j);             // there are duplicates, make sure i skip 
        }
    }

    return (double)sum / (double)(  ((n*(n-1)) / 2)  );
}

到目前为止我所拥有的是最好的情况。注意我只想找到 j 和 j 之间的距离。 i,不是同时所有距离(没有足够的内存,它将在 m=20 d=1 000 000 处进行测试)

private int dist(int i, int j) {
    int dist = 0;

    for (int link : in_neighbours[j]) {
        System.out.print("\nIs "+j+" linked to by "+i);
        if (out_neighbours[i].contains(link)) {
            System.out.print(" - yes!");
            dist = 1;
        }
    }

    return dist;
}

所以我问“新鲜”(ofc 此时图已完成)节点 i 是否正在链接如果是的话,直接到它的任何一个老伙伴,距离是 1 跳。

是我一个人的问题,还是如果向后遍历节点,“最短”路径将始终是第一个找到的路径?

我如何检查它是否不是 1,即基本情况后面的“else”?我的数学很弱,请温柔一点:) 有任何提示如何利用链接已排序的事实吗?

这不是家庭作业或我试图欺骗的东西,这与代码本身无关,这必须是一个有用的工具,“学习”是一路走来的。

这是图表的样子:nodeID、out links、in links for m=7 n=13,(注意 0 个周期正是图表的初始化方式):

0 | 0 0 0 0 0 0 0  | 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 2 2 2 2 3 4 5 6 6 7 8 9 
1 | 0 0 0 0 0 0 0  | 2 2 3 4 5 5 8 12 
2 | 0 0 0 0 0 1 1  | 3 3 3 3 3 4 4 4 6 7 8 10 
3 | 0 1 2 2 2 2 2  | 4 4 5 5 6 6 7 11 
4 | 0 1 2 2 2 3 3  | 5 5 6 8 9 10 
5 | 0 1 1 3 3 4 4  | 6 7 8 9 9 11 12 
6 | 0 0 2 3 3 4 5  | 7 7 7 8 9 9 12 
7 | 0 2 3 5 6 6 6  | 8 9 10 11 11 12 
8 | 0 1 2 4 5 6 7  | 10 10 10 11 12 
9 | 0 4 5 5 6 6 7  | 10 11 11 
10 | 2 4 7 8 8 8 9  | 12 12 
11 | 3 5 7 7 8 9 9  | 
12 | 1 5 6 7 8 10 10  | 

抱歉,阅读时间过长。 编辑:方法中的代码错误,这是我现在认为正确的。

修改 dist nr2,尝试查找是否有路径:

private int dist(int i, int j) {
    int dist = 0, c = 0, count = 0;
    boolean linkExists = false;

    for (int link : in_neighbours[j]) {

        //System.out.print("\nIs "+j+" linked to by "+i);
        if (out_neighbours[i].contains(link)) {

            //System.out.print(" - yes!");
            dist = 1; // there is a direct link

        } else {

            while ( c < j ) {
                // if there's a path from 0 up to j, check if 'i' links to a node which eventually links to 'j'
                if (out_neighbours[i].contains(c) && 
                        (out_neighbours[c].contains(link) || in_neighbours[c].contains(link) )) {

                    count++; // yes. and this is one node we had to step through to get closer
                    linkExists = true;
                } else {
                    linkExists = false; // unreachable, the path was interrupted somewhere on the way
                    break;
                }
                c++; 
            }

            if (linkExists) {
                dist = count-1; // as 2 nodes are linked with 1 edge
            } else {
                dist = 0; // no path was found
            }
        }


    }

    return dist;
}

Before the Floyd–Warshall/Dijkstra replies flood comes in please let me explain the situation as i'm sure either algorithm can be tuned for this case, and it has to be as this is not a toy example program (mind you, in java so have to keep it manageable memory-wise)

What i have is a web graph generated from node 0 to node n, node 3 cannot link to node 5, because node 5 didnt exist when node 3 was choosing it's out links. Every "node" is represented as in_neighbours[nodeID] and out_neighbours[nodeID] say nodeId=3, so we're talking about node 3. Note also that in_/out_ are both sorted, (in_ is naturally sorted as 5 will have chosen its out links all at once, only then 6 will choose out_links so 3's in_'s can never contain {6, 5, 7}) and ofc both can contain duplicates. (in/out are ArrayList arrays of size n, where out_ is always of size d or m, which along with n is specified at startup by the user)

No weights. What i must do is find the averageDistance()

public double getAvgDistance() {
    int sum = 0;        

    for (int i=1; i<n; i++) {
        for (int j=0; j < i; j++) {
            sum += dist(i, j);             // there are duplicates, make sure i skip 
        }
    }

    return (double)sum / (double)(  ((n*(n-1)) / 2)  );
}

What I have so far is the best case. Note i want only to find the distance between j & i, not all distances at the same time (not enough memory, it will be tested at m=20 d=1 000 000)

private int dist(int i, int j) {
    int dist = 0;

    for (int link : in_neighbours[j]) {
        System.out.print("\nIs "+j+" linked to by "+i);
        if (out_neighbours[i].contains(link)) {
            System.out.print(" - yes!");
            dist = 1;
        }
    }

    return dist;
}

So im asking if the "fresher" (ofc at this point the graph is completed) node i is linking to any of its older buddies directly if so, distance is 1 hop.

Is it just me or the 'shortest' path will always be the first found path if nodes are traversed backwards?

How do i check if its not 1, the "else" after the base case? My math is fairly weak please be gentle :)
Any hints how to make use of the fact that the links are sorted?

It's not homework or something that im trying to cheat around from, it's not about the code itself, this has to be a useful tool, the "learning" comes by itself along the way.

here's how a graph looks nodeID, out links, in links for m=7 n=13, (note the 0 cycles is just how the graph is initialized):

0 | 0 0 0 0 0 0 0  | 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 2 2 2 2 3 4 5 6 6 7 8 9 
1 | 0 0 0 0 0 0 0  | 2 2 3 4 5 5 8 12 
2 | 0 0 0 0 0 1 1  | 3 3 3 3 3 4 4 4 6 7 8 10 
3 | 0 1 2 2 2 2 2  | 4 4 5 5 6 6 7 11 
4 | 0 1 2 2 2 3 3  | 5 5 6 8 9 10 
5 | 0 1 1 3 3 4 4  | 6 7 8 9 9 11 12 
6 | 0 0 2 3 3 4 5  | 7 7 7 8 9 9 12 
7 | 0 2 3 5 6 6 6  | 8 9 10 11 11 12 
8 | 0 1 2 4 5 6 7  | 10 10 10 11 12 
9 | 0 4 5 5 6 6 7  | 10 11 11 
10 | 2 4 7 8 8 8 9  | 12 12 
11 | 3 5 7 7 8 9 9  | 
12 | 1 5 6 7 8 10 10  | 

Sorry for the agonising long read.
EDIT: Wrong code in the methods, this is what i think is correct now.

Revision of dist nr2, just try and find if theres a path at all:

private int dist(int i, int j) {
    int dist = 0, c = 0, count = 0;
    boolean linkExists = false;

    for (int link : in_neighbours[j]) {

        //System.out.print("\nIs "+j+" linked to by "+i);
        if (out_neighbours[i].contains(link)) {

            //System.out.print(" - yes!");
            dist = 1; // there is a direct link

        } else {

            while ( c < j ) {
                // if there's a path from 0 up to j, check if 'i' links to a node which eventually links to 'j'
                if (out_neighbours[i].contains(c) && 
                        (out_neighbours[c].contains(link) || in_neighbours[c].contains(link) )) {

                    count++; // yes. and this is one node we had to step through to get closer
                    linkExists = true;
                } else {
                    linkExists = false; // unreachable, the path was interrupted somewhere on the way
                    break;
                }
                c++; 
            }

            if (linkExists) {
                dist = count-1; // as 2 nodes are linked with 1 edge
            } else {
                dist = 0; // no path was found
            }
        }


    }

    return dist;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

俯瞰星空 2024-09-14 06:21:58

由于模型中所有边的权重相同,因此您可以使用 BFS 搜索来找到从 S 到 T 的最短路径。

这是一个迭代过程,从集合 #0 开始,仅包含源节点 ({S})。
在每一步 i 中,您通过一步查找集合 (i-1) 中可实现的所有节点来创建集合 #i。

迭代在两种情况下终止:

1) 当您检测到集合 #k 包含 T 时。在这种情况下,您返回 k-1。

2)当集合为空时,意味着两个节点不可达。

内存消耗大约是节点数量的两倍,因为在每个步骤 i 中,您都使用两个集合(i-1 和 i),且受节点总数限制。

--编辑--

这是一个可能的实现(我对其进行了一些测试):

private Integer getDist(int i, int j) {
    Set<Integer> currentSet = new HashSet<Integer>();
    currentSet.add(i);
    int dist = 0;
    while (true) {
        Set<Integer> nextSet = new HashSet<Integer>();
        for (Integer currNode : currentSet)
            nextSet.addAll(out[currNode]);

        if (nextSet.isEmpty())
            return null; //i.e. infinite
        if (nextSet.contains(j))
            return dist;
        dist++;
        currentSet = nextSet; 
    }
}

该实现假设 inout 被定义为 List[],节点由从0开始的数字标识。最小距离计算为路径中中间节点的数量,而不是边的数量。

列表中的重复项不会破坏任何内容,但它们与算法无关。

Since all edges have the same weight in your model, you can use a BFS search to find the shortest path from S to T.

This is an iterative process, starting with set #0, containing only the source node ({S}).
At each step i, you create set #i by finding all nodes achievable from set (i-1) in one step.

The iteration terminates in two cases:

1) When you detect that set #k contains T. In this case you return k-1.

2) When the set is empty, meaning that the two nodes are unreachable.

The memory consumption is about twice the number of nodes, since at each step i you are working with two sets (i-1 and i), bounded by the total number of nodes.

--EDIT--

Here is a possible implementation (I made some tests on it):

private Integer getDist(int i, int j) {
    Set<Integer> currentSet = new HashSet<Integer>();
    currentSet.add(i);
    int dist = 0;
    while (true) {
        Set<Integer> nextSet = new HashSet<Integer>();
        for (Integer currNode : currentSet)
            nextSet.addAll(out[currNode]);

        if (nextSet.isEmpty())
            return null; //i.e. infinite
        if (nextSet.contains(j))
            return dist;
        dist++;
        currentSet = nextSet; 
    }
}

The implementation assumes that in and out are defined as List<Integer>[], and nodes are identified by numbers starting from 0. The minimal distance is counted as the number of intermediate nodes in the path, and not as the number of edges.

The duplicates you have in the lists do not break anything here, but they are irrelevant for the algorithm.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文