Finding the path in a graph with the least casualties according to the lanchester square law

Martin N. :

I'm trying to find the best possible path in graph using Bellman-Ford algorithm. However, instead of using the sum of all edges to calculate the path length, I want the path with the least casualties. We calculate casualties using this formula: remainingSoldiers = sqrt(a^2 - b^2), where A is our army, B is the enemy army and remainingSoldiers is the number of our soldiers left after the fight.

An example: Let's say we want to conquer city D. We are starting in vertex A with army of strength 100. Our army travels to vertex B, which is patrolled by an enemy army of strength 50. According to our formula, we have 86 soldiers left. Next our army travels to vertex C, so our 86 soldiers fight with 40 soldiers patrolling vertex C, and we have 76 soldiers left. And finally, our 76 soldiers travel to vertex D which is being guarded by 70 enemy soldiers. According to our formula, we conquered the vertex D with 29 soldiers.

So in order to find the best path, we have to calculate which path to take in order to have the least casualties. Hint I got is to set the strength of our army and the strength of the enemy army as weights of edges and use the Bellman-Ford with a modified relaxation algorithm to find the best find path. That's exactly what I did in my code below.

I realized that in order to find the best path, I must find the path with the lowest number of casualties, not the highest number of soldiers left, since finding the path with the highest number is a NP-complete problem.

My code is following (I'm using a custom library for graphs, but it should be really straight-forward and easy to understand):

public Map<Vertex, Double> bellmanFord(Vertex s) {
            Map<Vertex, Double> d = g.createVertexMap(Double.POSITIVE_INFINITY);
            d.put(s, 0d);
            for (int i = 0; i < g.getVertices().size(); i++)
                    for (Edge e : g.getEdges())
                            relax(e, d);
            return d;
    }

    public void relax(Edge e, Map<Vertex, Double> d) {
            Vertex u = e.getSource();
            Vertex v = e.getTarget();
            if (d.get(u) + e.getWeight() < d.get(v))
                    d.put(v, d.get(u) + e.getWeight());
    }

And below is my modified code for the relaxation:

    public void relax(Edge e, Map<Vertex, Double> d) {
            Vertex u = e.getSource();
            Vertex v = e.getTarget();
            if (d.get(u) - formula(g.getEdge(u, v).getWeight(), g.getEdge(v, u).getWeight()) > d.get(v))
                d.put(v, d.get(u) - formula(g.getEdge(u, v).getWeight(), g.getEdge(v, u).getWeight()));
    }

    public double formula(double ourCity, double enemyCity) {
            double a = Math.pow(ourCity, 2);
            double b = Math.pow(enemyCity, 2);
            double result = a - b;
            return Math.sqrt(result);
    }

However, my code is outputting a total nonsense. Could you help me fix my problem and implement the formula inside the method relaxation for the Bellman-Ford algorithm?

This is graph I'm launching my code with (not an edge case, just a random graph to test basic functionality): https://i.imgur.com/Y2OhfDj.png . We are trying to conquer the city H from the city A. Our army of strength 120 is located in the city A . When running my modified code, the bellman-ford outputs the following: {a=Infinity, d=Infinity, f=Infinity, g=Infinity, b=Infinity, c=Infinity, h=Infinity, e=Infinity}.I think I have to somehow modify the edges representing the strength of my army as my algorithm goes (I can use method .setWeight() on any edge..), but unsure how to implement it. I tried many variations of the relaxation, but none got close to the correct answer so far.

Thanks!

MDK :

I see three problems with your code:

  1. Storing your starting force in the graph creates ambiguity since you can't tell whether this edge has your forces or enemy's forces as weight. This will be a problem if two cities are connected with each other because you will get double edges. Instead use a variable to store it somewhere in you graph class, in your example double startingForce = 120;
  2. Your call in relax switches between remaining forces and casualties the wrong way around. For calling formula you need your remaining forces, but the output needs to be transformed to casualties again.

    double casualtiesWithCurrentPath = startingForce - formula(startingForce - d.get(u), e.getWeight());
    if (casualtiesWithCurrentPath < d.get(v))
        d.put(v, casualtiesWithCurrentPath);
    
  3. If a node is unreachable it has infinity casualties, leading to -infinity remaining forces in this node. However by squaring them in formula the sign gets lost, leading to infinite strength, so you need to calculate with

    double a = Math.signum(ourCity) * Math.pow(ourCity, 2);
    

    instead

With these changes I get {a=13.229217479686895, b=17.043698590130006, c=11.372195087997852, d=12.761947052363922, e=4.241630972097752, f=0.41739256898601695, g=1.678404338007681, h=0.0} in the example

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=141487&siteId=1