Алгоритм Флойда-Уоршалла - не работает в определенных случаях

Я пытаюсь создать простую программу с использованием алгоритма Флойда-Уоршалла для расчета кратчайшего маршрута между двумя или более парами узлов.

Я использую класс Village для представления узлов и класс Road для представления дорог между указанными узлами. Далее по коду я также вызываю класс TransportRequest, который представляет два узла, из которых мне нужно сгенерировать кратчайший путь.

public string CalculateShortestRoute(List<Village> villagesList, List<Road> roadsList, Guid mapID)
    {
        //Initialize two dimentional arrays with the amount of villages.
        int[,] dist = new int[villagesList.Count, villagesList.Count];
        string[,] path = new string[villagesList.Count, villagesList.Count];

        //Loop through each village in order to insert distance values into a two dimentional array
        for (int from = 0; from < villagesList.Count; from++)
        {
            for (int to = 0; to < villagesList.Count; to++)
            {
                //If the village from and village to are the same, set the distance to 0.
                if (from == to)
                {
                    dist[from, to] = 0;
                }
                else
                {
                    int result = 1000000;

                    //Get the road between the two currently selected villages.
                    Road rDist = roadsList.SingleOrDefault<Road>(v => v.Village.Name == villagesList[from].Name && v.Village1.Name == villagesList[to].Name);

                    //If the road doesn't exist...
                    if (rDist == null)
                    {
                        //... swap the village from and village to, and check again
                        rDist = roadsList.SingleOrDefault<Road>(v => v.Village.Name == villagesList[to].Name && v.Village1.Name == villagesList[from].Name);
                        if (rDist != null)
                        {
                            //If the road is now found, get the distance.
                            result = Convert.ToInt32(rDist.Distance);
                        }
                    }
                    else
                    {
                        //If the road initially existed, get its distance.
                        result = Convert.ToInt32(rDist.Distance);
                    }

                    //Set the distance at the given positions to the integer retrieved.
                    dist[from, to] = result;
                }
            }
        }


        //Loop through all the villages again and initialize the other two dimentional array with the village name.
        for (int from = 0; from < villagesList.Count; from++)
        {
            for (int to = 0; to < villagesList.Count; to++)
            {
                path[from, to] = villagesList[from].Name;
            }
        }

        //Re-loop through all the villages three times...
        for (int k = 0; k < villagesList.Count; k++)
        {
            for (int i = 0; i < villagesList.Count; i++)
            {
                for (int j = 0; j < villagesList.Count; j++)
                {
                    if (dist[i, j] != 1000000)
                    {
                        //... and if the distances between two selected villages is greater than the other two added together, ...
                        if (dist[i, j] > (dist[i, k] + dist[k, j]))
                        {
                            //... Set the distances of the two selected villages to the shortest distance.
                            dist[i, j] = dist[i, k] + dist[k, j];
                            int distance = dist[i, j];


                            Village vv = villagesList.SingleOrDefault(v => v.Name == villagesList[k].Name);
                            //Also, add the village name to the path.
                            path[i, j] = path[i, j] + " " + vv.Name;

                        }
                    }
                }
            }
        }

        for (int from = 0; from < villagesList.Count; from++)
        {
            for (int to = 0; to < villagesList.Count; to++)
            {
                Village vv = villagesList.SingleOrDefault(v => v.Name == villagesList[to].Name);

                path[from, to] = path[from, to] + " " + vv.Name;
            }
        }

        //Get the transport requests on the current map.
        Map m = new BAMap().GetMapByID(mapID);
        List<TransportRequest> transportRequests = new BATransportRequest().GetTransportRequestsByMapID(m.ID).ToList();


        string p = "";

        int dis = 0;
        //For each transport request, get the index village from and village to from the map vilalges.
        foreach (TransportRequest r in transportRequests)
        {

            int villFrom = villagesList.IndexOf(villagesList.Single<Village>(vill => vill.Name == r.Village.Name));
            int villTo = villagesList.IndexOf(villagesList.Single<Village>(vill => vill.Name == r.Village1.Name));

            //Retrieve the distance between these two villages from the two dimentional array
            dis = dist[villFrom, villTo];

            //Initialize a string 'p' to null.
            //If it is null...
            if (p == "")
            {
                //... add the path of the village from and village to.
                p = path[villFrom, villTo];
            }
            else
            {
                //Else, if it is not null, add the path of the village from and village to to the string.
                p += " " + path[villFrom, villTo];
            }
        }

        //If the string doesn't start with an 'A'...
        if (!p.StartsWith("A"))
        {
            string pp = p;
            //.. Start the path from village 'A', and add the path retreived beforehand to the 'A'
            p = "A " + pp;
        }

        //If the final path doesn't end with an 'A'...
        if (!p.EndsWith("A"))
        {
            //... attach an 'A' to it.
            p += " A";
        }

        //Return the path.
        return p;

    }

Вышеприведенный код, кажется, отлично работает на большинстве случайно сгенерированных узлов и расстояний, однако в некоторых случаях происходит сбой, например, в приведенном ниже;

Круги представляют собой узлы/деревни. Линии представляют собой дороги между этими деревнями, а расстояние между каждой деревней – это число на  дорога.

Если я попытаюсь вычислить кратчайший маршрут от узла A к узлу E, ответ всегда будет A > E (всего 100), а не A > B > C > D > E (всего 37).

Дороги между каждым узлом являются двунаправленными, что означает, что в данном случае из A в B и из B в A по 10.

Матрицы dist[] правильно заполняются правильными расстояниями между каждой парой узлов. Там, где нет дороги, соединяющей пару узлов, я устанавливаю расстояние по умолчанию 1000000 единиц.

Я просмотрел несколько вопросов на разных форумах (включая этот), но, похоже, никто не решает эту проблему. Я считаю, что алгоритм реализован правильно, но я не вижу, где и почему в определенных случаях он не работает должным образом.

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

Спасибо заранее.


person ClaireG    schedule 04.06.2014    source источник
comment
Дайте мне расположение и названия деревень, как если бы я написал этот вопрос о переполнении стека как модульный тест с шаблоном AAA. ИЛИ загрузите небольшую демо-версию, чтобы мы могли загрузить и отладить ее. Я хочу попробовать решить ее.   -  person Jeremy Thompson    schedule 04.06.2014
comment
@JeremyThompson Спасибо за помощь. Я создал аналогичное решение. Пожалуйста, найдите его в моем DropBox здесь: dropbox.com/sh/063z34og2cn9ejv/AAAa4SBRiHQsVncWz3k6rPvGa   -  person ClaireG    schedule 04.06.2014


Ответы (1)


Это потому, что вы никогда не позволяете уменьшить расстояние между двумя узлами, не соединенными дорогой. Посмотрите, например, на узлы C и E. dist[C,E] останется равным 1000000, потому что вы никогда не рассматриваете возможность перехода из C в E через D.

Кстати, быстрый сеанс отладки, вероятно, был бы способом увидеть, что алгоритм делает шаг за шагом.

person muzzlator    schedule 04.06.2014
comment
Спасибо за ответ. Я вижу, что вы говорите правду, но что вы предлагаете мне делать? - person ClaireG; 04.06.2014
comment
Избавьтесь от условия if. Должно быть достаточно. - person muzzlator; 05.06.2014
comment
Если вы ссылались на if (dist[i, j] != 1000000), это не сработало. - person ClaireG; 05.06.2014
comment
О чем теперь говорит расстояние между А и Е? Все еще 100? Как насчет между C и E? - person muzzlator; 05.06.2014
comment
Теперь от A до E 37, а от C до E 22. Кажется, это сработало, спасибо. Однако путь отображается как A C E A. Он должен быть A B C D E A - person ClaireG; 05.06.2014
comment
Я нашел проблему, это была небольшая ошибка :) Большое спасибо. Ваш ответ помог мне решить эту проблему. - person ClaireG; 05.06.2014