Поиск пути в графе с наименьшими потерями по закону квадратов Ланчестера

Я пытаюсь найти наилучший путь в графе, используя алгоритм Беллмана-Форда. Однако вместо того, чтобы использовать сумму всех ребер для вычисления длины пути, мне нужен путь с наименьшими потерями. Мы рассчитываем потери по следующей формуле: remainingSoldiers = sqrt(a^2 - b^2), где A — наша армия, B — вражеская армия, а restSoldiers — количество наших солдат, оставшихся после боя.

Пример: Допустим, мы хотим захватить город D. Мы начинаем в вершине A с армией силой 100. Наша армия движется в вершину B, которую патрулирует вражеская армия численностью 50. Согласно нашей формуле, у нас есть 86 солдаты ушли. Далее наша армия движется в вершину C, поэтому наши 86 солдат сражаются с 40 солдатами, патрулирующими вершину C, и у нас осталось 76 солдат. И, наконец, наши 76 солдат отправляются в вершину D, которую охраняют 70 вражеских солдат. Согласно нашей формуле мы покорили вершину D с 29 солдатами.

Итак, чтобы найти лучший путь, мы должны рассчитать, какой путь выбрать, чтобы иметь наименьшие потери. Подсказка, которую я получил, состоит в том, чтобы установить силу нашей армии и силу вражеской армии как веса ребер и использовать Беллмана-Форда с модифицированным алгоритмом релаксации, чтобы найти лучший путь поиска. Это именно то, что я сделал в своем коде ниже.

Я понял, что для того, чтобы найти лучший путь, я должен найти путь с наименьшим числом потерь, а не с наибольшим количеством оставшихся солдат, поскольку поиск пути с наибольшим числом — это NP-полная задача.

Мой код следующий (я использую пользовательскую библиотеку для графиков, но она должна быть очень простой и понятной):

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());
    }

И ниже мой модифицированный код для релаксации:

    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);
    }

Однако мой код выводит полную ерунду. Не могли бы вы помочь мне решить мою проблему и реализовать формулу внутри релаксации метода для алгоритма Беллмана-Форда?

Это график, с которым я запускаю свой код (не крайний случай, просто случайный график для проверки базовой функциональности): https://i.imgur.com/Y2OhfDj.png . Мы пытаемся захватить город H из города A. Наша армия численностью 120 человек находится в городе A. При запуске моего модифицированного кода bellman-ford выводит следующее: {a=Infinity, d=Infinity, f=Infinity, g=Infinity, b=Infinity, c=Infinity, h=Infinity, e=Infinity}.Я думаю, что мне нужно каким-то образом изменить ребра, представляющие силу моей армии, в соответствии с моим алгоритмом (я могу использовать метод .setWeight() на любом ребре..), но непонятно, как это реализовать. Я испробовал множество вариантов релаксации, но пока ни один из них не приблизился к правильному ответу.

Спасибо!


person Community    schedule 16.08.2019    source источник
comment
Пожалуйста, покажите любой ввод/вывод, чтобы мы могли лучше понять, что происходит не так.   -  person Zain Arshad    schedule 16.08.2019
comment
@ZainArshad Привет, я отредактировал свой пост, добавив изображение моего примерного графика, объяснение графика и результат.   -  person    schedule 16.08.2019
comment
return Math.sqrt(vysledok) - что такое vysledok? Этот метод «формулы» не использует «результат»?   -  person Brian Agnew    schedule 16.08.2019
comment
@BrianAgnew ой, извините. Выследок = результат.. Не хватало перевода - сейчас исправил.   -  person    schedule 16.08.2019
comment
@BrianAgnew это словацкое слово для результата   -  person Zain Arshad    schedule 16.08.2019
comment
Разве все узлы не связаны друг с другом? на изображении это как бы только два пути ... плюс, значение на краю означает количество солдат в этом городе, например городе c = 20, b = 10 ?   -  person Zain Arshad    schedule 16.08.2019
comment
@ZainArshad По сути, входными данными может быть любой график, я сделал просто случайный график для проверки базовой функциональности. И да, вместо того, чтобы сохранять силу армий в каждом городе на чем-то вроде Карты, я устанавливаю ее как вес преимущества. Например, ребро B-A имеет вес 10, а это значит, что в вершине B находится армия из 10 солдат. Я думаю, что сохранение его таким образом вместо Map может упростить переписывание bellman-ford.   -  person    schedule 16.08.2019
comment
@МартинН. Я когда-то написал беллман-форд для задания, дайте мне изменить его и посмотреть, работает ли он ... подождите ....   -  person Zain Arshad    schedule 16.08.2019
comment
@МартинН. вы не можете просто найти кратчайший путь с наименьшим количеством вражеских солдат (без сокращения ваших солдат), и после этого, имея путь, вы просто примените формулу sqrt (a ^ 2-b ^ 2) при отображении результата   -  person Zain Arshad    schedule 16.08.2019
comment
@ZainArshad Я тоже об этом думал, но думаю, что будут и контрпримеры, поскольку иногда путь с большим количеством вражеских солдат может быть лучше. Например, наша армия может победить огромную вражескую армию, только если мы справимся с ней с самого начала (когда количество наших юнитов все еще велико). Если какой-то путь имеет меньшее общее количество вражеских солдат, но огромное количество солдат ближе к концу пути, мы не сможем их победить. Ты понимаешь?   -  person    schedule 16.08.2019
comment
Пусть A будет начальным количеством солдат. Осталось солдат в квадрате после первого столкновения: R1^2 = A^2 - B1^2. После второй встречи: R2^2 = R1^2 - B2^2 = A^2 - B1^2 - B2^2. После k встреч: Rk^2 = A^2 - B1^2 - ... - Bk^2. Итак, это, по сути, проблема с кратчайшей частью... Но вы должны возвести в квадрат свои веса ребер.   -  person    schedule 16.08.2019


Ответы (1)


Я вижу три проблемы с вашим кодом:

  1. Хранение вашей стартовой силы в графе создает двусмысленность, поскольку вы не можете сказать, имеет ли это ребро вес ваших сил или сил противника. Это будет проблемой, если два города соединены друг с другом, потому что вы получите двойные края. Вместо этого используйте переменную, чтобы сохранить ее где-нибудь в вашем классе графа, в вашем примере double startingForce = 120;
  2. Ваш призыв к расслаблению переключается между оставшимися силами и потерями в обратном направлении. Для вызова формулы вам нужны ваши оставшиеся силы, но выход нужно снова преобразовать в потери.

    double casualtiesWithCurrentPath = startingForce - formula(startingForce - d.get(u), e.getWeight());
    if (casualtiesWithCurrentPath < d.get(v))
        d.put(v, casualtiesWithCurrentPath);
    
  3. Если узел недоступен, он имеет infinity потерь, что приводит к -infinity оставшимся силам в этом узле. Однако, возводя их в квадрат в formula, знак теряется, что приводит к бесконечной силе, поэтому вам нужно рассчитать с помощью

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

    вместо

С этими изменениями я получаю {a=13.229217479686895, b=17.043698590130006, c=11.372195087997852, d=12.761947052363922, e=4.241630972097752, f=0.41739256898601695, g=1.678404338007681, h=0.0} в примере

person MDK    schedule 19.08.2019
comment
Спасибо! Для этого графика: i.imgur.com/HPSRDny.png (в кружках — армии, вверху кружки — это результаты, которые должен выводить квадратичный закон, A — начальная вершина), он выводит следующие результаты: E = 59,207843891257724, D = 3,025776620794744, B = 0,9848496441074985, I = 67,76647707742761, F = 7,782864932811975, L = 87,03851860318429}, что не соответствует правильным ответам. Однако с вашими изменениями я чувствую, что теперь я так близок! Не могли бы вы помочь? Спасибо - person ; 20.08.2019
comment
Цифры выше кажутся правильными. Это потери, поэтому, чтобы получить оставшуюся силу, вам нужно вычесть их из вашей начальной силы перед выводом. Но поскольку алгоритм минимизирует потери, важно, чтобы узлы взвешивались с потерями, а не с оставшейся силой. - person MDK; 20.08.2019
comment
О, извините, я чувствую себя тупым сейчас.. Так что теперь это работает, большое спасибо. Я застрял на этой проблеме на прошлой неделе .. - person ; 20.08.2019