WedX - журнал о программировании и компьютерных науках

Простой Java-реализация алгоритма Флойда-Уоршалла не работает?

Я пытался реализовать алгоритм Флойда-Уоршалла на Java, не используя способ «три вложенных цикла for», но я не могу понять, где я ошибся в коде.

Это карта, показывающая, как связаны мои вершины. Белые числа — это вершины, а черные числа — это расстояния между соединенными вершинами.

Карта вершин: https://i.imgur.com/htcaA4y.png

После выполнения итераций я получаю следующие окончательные матрицы расстояний и последовательностей. То, что говорит «что-то не так», — это столбец 8 окончательной матрицы последовательности (тот, что справа). Чтобы попасть в вершину 8 из любой другой вершины, путь должен пройти сначала из вершины 8 в вершину 9, а ЗАТЕМ в вершину 10 (что не так согласно матрице — он идет прямо из вершины 8 в вершину 10).

Выходная матрица: https://i.imgur.com/o6fQweH.png

Вот код. В чем проблема?


import java.util.ArrayList;

public class Main_Simple {

    public static void main(String[] args) {

        // 1. Setup the distance matrix
        //  -inf for vertices that are not connected
        //  -### for edge weights of vertices that are connected
        //  -0 across the diagonal

        int inf = 1000000; // Temporary 'infinity' variable

        // Initial distance matrix must be n x n
        int[][] initialDistanceMatrix = {
                {0, 1,  inf,    1,  inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf},
                {1, 0,  1,  inf,    1,  inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf},
                {inf,   1,  0,  inf,    inf,    1,  inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf},
                {1, inf,    inf,    0,  1,  inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf},
                {inf,   1,  inf,    1,  0,  1,  inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf},
                {inf,   inf,    1,  inf,    1,  0,  2,  inf,    inf,    1,  inf,    inf,    inf,    inf,    inf},
                {inf,   inf,    inf,    inf,    inf,    2,  0,  inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf},
                {inf,   inf,    inf,    inf,    inf,    inf,    inf,    0,  1,  inf,    inf,    inf,    inf,    inf,    inf},
                {inf,   inf,    inf,    inf,    inf,    inf,    inf,    1,  0,  1,  inf,    inf,    inf,    inf,    inf},
                {inf,   inf,    inf,    inf,    inf,    1,  inf,    inf,    1,  0,  2,  1,  inf,    inf,    inf},
                {inf,   inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    2,  0,  inf,    inf,    inf,    2},
                {inf,   inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    1,  inf,    0,  1,  inf,    inf},
                {inf,   inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    1,  0,  1,  inf},
                {inf,   inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    1,  0,  1},
                {inf,   inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    inf,    2,  inf,    inf,    1,  0}
        };

        // 2. Setup the sequence matrix
        //  -All of column-1 are ones
        //  -All of column-2 are twos
        //  -etc
        //  -0 across the diagonal

        // Initial sequence matrix must be the same size as the initial distance matrix
        int[][] initialSequenceMatrix = new int[initialDistanceMatrix.length][initialDistanceMatrix.length];
        for (int row = 0; row < initialSequenceMatrix.length; row++) {
            for (int column = 0; column < initialSequenceMatrix.length; column++) {
                if (row == column) {
                    initialSequenceMatrix[row][column] = 0;
                } else {
                    initialSequenceMatrix[row][column] = column + 1; // +1 to account 0-based array
                }
            }
        }

        // 3. Iterate through the matrices (n-1) times
        //  -On the kth iteration, copy the kth column and kth row down to the next distance and sequence matrix
        //  -On the kth iteration, check matrix (k-1) and take the minimum of the following two:
        //      -d(ij)
        //      -d(ik)+d(kj)
        //      where i = row number, j = column number, and k = iteration number
        //  -After the distance matrix has been calculated, compare the current distance matrix to the previous.
        //  If the numbers are the same, keep the sequence matrix the same.  Otherwise, change the sequence
        //  matrix to the current iteration's number.

        ArrayList<int[][]> distanceMatrices = new ArrayList<int[][]>();
        distanceMatrices.add(initialDistanceMatrix);

        ArrayList<int[][]> sequenceMatrices = new ArrayList<int[][]>();
        sequenceMatrices.add(initialSequenceMatrix);

        // Print the matrices to make sure they are made correctly
        printMatrix(initialDistanceMatrix, "Initial distance matrix");
        printMatrix(initialSequenceMatrix, "Initial sequence matrix");

        // Matrix Iteration Loops
        for (int iteration = 1; iteration < initialDistanceMatrix.length; iteration++) {

            // Initialize new distance matrix
            int[][] currentDistanceMatrix = new int[initialDistanceMatrix.length][initialDistanceMatrix.length];
            for (int row = 0; row < currentDistanceMatrix.length; row++) {
                for (int column = 0; column < currentDistanceMatrix.length; column++) {
                    currentDistanceMatrix[row][column] = 0;
                } // ends 'column' loop
            } // ends 'row' loop

            // Distance Matrix iteration
            for (int row = 0; row < currentDistanceMatrix.length; row++) {
                for (int column = 0; column < currentDistanceMatrix.length; column++) {

                    if (row == column) { // If you are on the diagonal, insert '0'
                        currentDistanceMatrix[row][column] = 0;
                    } else if (row == (iteration - 1) || column == (iteration - 1)) { // Brings down the row and column of the iteration (-1 to account 0-based array)
                        currentDistanceMatrix[row][column] = distanceMatrices.get(iteration - 1)[row][column];
                    } else { // If you are on any other square...
                        int Dij = distanceMatrices.get(iteration - 1)[row][column];
                        int Dik_Dkj = distanceMatrices.get(iteration - 1)[row][iteration - 1] + distanceMatrices.get(iteration - 1)[iteration - 1][column];

                        if (Dij > Dik_Dkj) currentDistanceMatrix[row][column] = Dik_Dkj;
                        else currentDistanceMatrix[row][column] = Dij;
                    }

                } // ends 'column' loop
            } // ends 'row' loop

            // Add the distance matrix to the matrix array
            distanceMatrices.add(currentDistanceMatrix);

            // Initialize new sequence matrix
            int[][] currentSequenceMatrix = new int[initialDistanceMatrix.length][initialDistanceMatrix.length];

            // Sequence Matrix iteration
            for (int row = 0; row < currentSequenceMatrix.length; row++) {
                for (int column = 0; column < currentSequenceMatrix.length; column++) {

                    if (row == column) { // If you are along the diagonal...
                        currentSequenceMatrix[row][column] = 0;
                    } else if (row == (iteration - 1) || column == (iteration - 1)) { // If you are on the same row or column as the iteration...
                        currentSequenceMatrix[row][column] = sequenceMatrices.get(iteration - 1)[row][column];
                    } else { // If you are on any other square...
                        // You need to check the current distance matrix to see if it matches the previous.
                        // If it does match, keep the same number.
                        // If it changed, changed the number in that cell to the current iteration

                        // Compare the most recent distance matrix to the one before it
                        if (distanceMatrices.get(distanceMatrices.size() - 1)[row][column] == distanceMatrices.get(distanceMatrices.size() - 2)[row][column]) {
                            currentSequenceMatrix[row][column] = sequenceMatrices.get(sequenceMatrices.size() - 1)[row][column];
                        } else {
                            currentSequenceMatrix[row][column] = iteration;
                        }
                    }

                } // ends 'column' loop
            } // ends 'row' loop

            // Add the sequence matrix to the matrix array
            sequenceMatrices.add(currentSequenceMatrix);

        } // ends matrix iteration loops

        System.out.println("-------------------------------------------------------");

        printMatrix(distanceMatrices.get(distanceMatrices.size() - 1), "Final Distance Matrix");
        printMatrix(sequenceMatrices.get(sequenceMatrices.size() - 1), "Final Sequence Matrix");

    } // ends main method

    public static void printMatrix(int[][] matrix, String message) {
        System.out.println("\n" + message);
        for (int row = 0; row < matrix.length; row++) {
            for (int column = 0; column < matrix.length; column++) {
                System.out.print(matrix[row][column] + "\t");
            } // ends 'column' loop
            System.out.println();
        } // ends 'row' loop
        System.out.println();
    }

} // ends class Main_Simple

  • Пытались ли вы отладить свою реализацию с гораздо меньшей картой? 12.11.2015
  • @Rhymoid Да, похоже, он работал на матрицах 5x5, 6x6 и 7x7. странно, как это просто не работает на этой карте. Может быть, я упускаю что-то фундаментальное в алгоритме Floyd-W? 14.11.2015

Ответы:


1

Вы неправильно выполняете итерацию по всем вершинам в первом цикле.

for (int iteration = 1; iteration < initialDistanceMatrix.length; iteration++)

Должно быть:

for (int iteration = 1; iteration < initialDistanceMatrix.length + 1; iteration++)

Или, что еще лучше, вместо использования iteration - 1 для всех ваших индексов массива используйте iteration, а затем вы можете использовать:

for (int iteration = 0; iteration < initialDistanceMatrix.length; iteration++)

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

12.11.2015
  • @ AndyN Хммм, похоже, это изменение не решило проблему для меня. Не могли бы вы помочь мне понять, как ваше изменение влияет на алгоритм? Насколько я понимаю, добавление еще одной итерации в цикл не меняет способ расчета чисел на квадрат. Даже тогда это не изменит решения этого конкретного столбца с 10 до 8, потому что ничего не изменилось в фактическом вычислении, просто добавив еще одну итерацию. В своем решении вы говорите, что я не выполняю итерацию должным образом по всем вершинам в первом цикле, но добавление итерации в конец не меняет начало. 15.11.2015
  • @ngserna Не перебирая все вершины в первом цикле, вы не проверяете все возможные промежуточные вершины. Первый цикл ищет возможные промежуточные вершины, которые короче от i до j. В этом случае вы не рассматриваете последнюю вершину. Так, например, вы не учитываете, что Dik_Dkj может пройти через вершину 15 (k == 15) . Говоря это, у вас все еще может быть ошибка в том, как вы создаете свою матрицу последовательности, которую я не вижу. Вы можете написать меньший модульный тест с ожидаемым результатом и пошагово построить матрицу последовательности. 16.11.2015
  • Вы также можете просмотреть раздел реконструкции пути [этого] (en. wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm). В частности, посмотрите, как вычисляется следующая вершина при каждом обновлении расстояния, а не в отдельном наборе циклов for. 16.11.2015
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

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

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]