Algorytm Johnsona - najkrótsze ścieżki w grafie

0

Witam. Czy mógłbym prosić Was o ocenę programu? W porównaniu z podanymi odpowiedziami w poleceniu, jest jedna różnica w moich odpowiedziach. W 3 linijce Out.txt, wypisuje 6(1), zamiast 6(5). Ale nie wiem, gdzie szukać błędu.
screenshot-20211215223643.png

#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
 
using namespace std;
#define MAXINT 1000000;
 
struct wierzcholki
{
    vector < int >polaczenia;
    vector < int >wagi;
} *Lista;
 
 
class Forda_Bellmana
{
public:
    int *dist;
public:
    void Algorytm (int ile_wierzcholki)
    {
        dist = new int[ile_wierzcholki + 1];
        for (int i = 0; i < ile_wierzcholki + 1; i++)
        {
            dist[i] = MAXINT;
        }
        dist[0] = 0;
        for (int k = 0; k < ile_wierzcholki - 1; k++)
        {
 
            for (int i = 0; i < ile_wierzcholki + 1; i++)
            {
                for (int j = 0; j < Lista[i].polaczenia.size (); j++)
                {
 
                    if (dist[Lista[i].polaczenia[j]] > dist[i] + Lista[i].wagi[j])
                    {
 
                        dist[Lista[i].polaczenia[j]] = dist[i] + Lista[i].wagi[j];
                    }
                }
            }
 
        }
        for (int j = 0; j < ile_wierzcholki + 1; j++)
        {
 
        }
 
 
 
    }
 
};
 
class Dijkstry
{
public:
    int *dist;
    int *pred;
 
    int min (int *_dist, bool * _tabB, int ileW)
    {
        int min = MAXINT;
        int gdzie_min = 0;
 
        for (int i = 0; i < ileW; i++)
        {
            if (!_tabB[i] && _dist[i] <= min)
            {
                min = _dist[i];
                gdzie_min = i;
            }
        }
        return gdzie_min;
    }
    void Dijkstra (int ileW, int **tablica, int Wdocelowy)
    {
        dist = new int[ileW];
        pred = new int[ileW];
 
        bool *tabB = new bool[ileW];
 
        for (int i = 0; i < ileW; i++)
        {
 
            dist[i] = MAXINT;
            pred[i] = -1;
            tabB[i] = false;
        }
 
 
        dist[Wdocelowy - 1] = 0;
        pred[Wdocelowy - 1] = -1;
 
 
        for (int j = 0; j < ileW; j++)
        {
 
            int u = min (dist, tabB, ileW);
 
 
 
            tabB[u] = true;
 
            for (int v = 0; v < ileW; v++)
            {
                if (!tabB[v])
                {
                    if (tablica[u][v] != 214748000 && tablica[u][v] != -1)
                    {
                        if (dist[u] != 214748000)
                        {
                            if (dist[u] + tablica[u][v] < dist[v])
                            {
                                dist[v] = dist[u] + tablica[u][v];
                                pred[v] = u + 1;
                            }
                        }
                    }
                }
 
            }
 
        }
    }
};
 
int
main ()
{
    fstream plik;
    int n;
 
 
    plik.open ("In.txt", ios::in);
    if (plik.good () == false)
    {
        cout << "nie otwiera sie plik ";
        return 1;
    }
 
    plik >> n;
    Lista = new wierzcholki[n + 1];
    string lin;
    char pom;
    getline (plik, lin);
    int indeks = 1, liczba;
    while (getline (plik, lin))
    {
        istringstream zmienna (lin);
        while (zmienna >> liczba)
        {
            Lista[indeks].polaczenia.push_back (liczba);
            zmienna >> pom;
            zmienna >> liczba;
            zmienna >> pom;
            Lista[indeks].wagi.push_back (liczba);
        }
        indeks++;
    }
    plik.close ();
 
    for (int i = 0; i < n; i++)
    {
        Lista[0].polaczenia.push_back (i + 1);
        Lista[0].wagi.push_back (0);
    }
 
 
 
    Forda_Bellmana ford;
    ford.Algorytm (n);
 
 
    int **tabb = new int *[n];
    for (int i = 0; i < n; i++)
    {
        tabb[i] = new int[n];
    }
 
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            tabb[i][j] = MAXINT;
        }
    }
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < Lista[i + 1].polaczenia.size (); j++)
        {
            tabb[i][Lista[i + 1].polaczenia[j] - 1] =
                    Lista[i + 1].wagi[j] + ford.dist[i + 1] -
                    ford.dist[Lista[i + 1].polaczenia[j]];
        }
    }
 
    for (int j = 0; j < n; j++)
    {
        for (int i = 0; i < n; i++)
        {
 
        }
 
    }
    Dijkstry tab[n];
 
    for (int i = 0; i < n; i++)
    {
        tab[i].Dijkstra (n, tabb, i + 1);
    }
    int tabD[n][n];
 
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            tabD[i][j] = tab[i].dist[j] + ford.dist[j + 1] - ford.dist[i + 1];
        }
    }
 
 
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
 
        }
 
    }
 
 
    plik.open ("Out.txt", ios::out);
    if (plik.good () == false)
    {
        cout << "nie otwiera sie plik ";
        return 1;
    }
    for (int i = 0; i < n + 1; i++)
    {
        plik << ford.dist[i] << " ";
    }
    plik << endl;
    for (int j = 0; j < n; j++)
    {
        for (int i = 0; i < Lista[j].polaczenia.size (); i++)
        {
            plik << Lista[j].polaczenia[i] << "(" << Lista[j].wagi[i] +
                                                     ford.dist[j] - ford.dist[Lista[j].polaczenia[i]] << ") ";
        }
        plik << endl;
    }
    for (int i = 0; i < n; i++)
    {
        plik << "Delta^[" << i + 1 << "][";
        for (int j = 0; j < n; j++)
        {
            plik << tab[i].dist[j] << " ";
        }
        plik << "],D[" << i + 1 << "][";
        for (int j = 0; j < n; j++)
        {
            plik << tabD[i][j] << " ";
        }
        plik << "]" << endl;
    }
 
    plik.close ();
    return 0;
}
0

Co mówi debugger i po co Masz w kilku miejscach puste pętle?

4
Rafał Masny napisał(a):

Witam. Czy mógłbym prosić Was o ocenę programu?

Dużo problemów w stylu programowania:

  • za dużo dzieje się w main - podziel to na mniejsze funkcje
  • współdzielenie zmiennych ro różnych celów (szczególnie ten plik który raz odczytuje raz zapisuje)
  • liczby magiczne, np 214748000
  • za duża ilość wcięć jest sygnałem, że kod powinien być podzielony na mniejsze funkcje.
  • https://dsp.krzaq.cc/post/176/ucze-sie-cxx-kiedy-uzywac-new-i-delete
  • w kodzie zostały jakieś śmieci, np te puste pętle for.

Te wszystkie problemy utrudniają mi:

  • czytanie twojego kodu
  • przepuszczenie kodu przez narzędzie online np: https://godbolt.org/z/qhW5v5fda by automat wyszukał klasyczne błędy pamięci lub niezdefiniowane zachowania (np chciałbym poprawić kod tak, by dany były wczytywane z std::cin i zapisywane do std::cout bo na godbolt nie ma obsługi plików, a na innych kompilatorach online nie da się włączyć address sanitizer ).

Jak przepiszesz przykładowe dane wejściowe, to może puszczę narzędzie u siebie lokalnie i wyślę ci wynik i analizę (wtedy bez linka oczywiście).

0

Najwyraźniej nie ma błędów pamieci lub UB:

johnson4programmers:$ g++ -std=c++17 -O2 -o johnson -Wall -Wextra -fsanitize=address,undefined johnson.cpp 
johnson.cpp:34:35: warning: comparison of integers of different signs: 'int' and 'std::vector<int>::size_type' (aka 'unsigned long') [-Wsign-compare]
                for (int j = 0; j < Lista[i].polaczenia.size(); j++)
                                ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
johnson.cpp:175:27: warning: comparison of integers of different signs: 'int' and 'std::vector<int>::size_type' (aka 'unsigned long') [-Wsign-compare]
        for (int j = 0; j < Lista[i + 1].polaczenia.size(); j++)
                        ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
johnson.cpp:224:27: warning: comparison of integers of different signs: 'int' and 'std::vector<int>::size_type' (aka 'unsigned long') [-Wsign-compare]
        for (int i = 0; i < Lista[j].polaczenia.size(); i++)
                        ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
3 warnings generated.
johnson4programmers:$ cat In.txt 
6
2  6    3 -2    6  3
3 -1    4  3    5 -4
4  1    6  4
5  3
1 -1


johnson4programmers:$ ./johnson 
johnson4programmers:$ cat Out.txt 
0 0 0 -4 0 0 0 
1(0) 2(0) 3(4) 4(0) 5(0) 6(0) 
2(3) 2(3) 
3(5) 3(0) 
4(2) 
5(5) 
1(1) 
Delta^[1][0 3 3 5 10 1000000 ],D[1][0 3 -1 5 10 1000000 ]
Delta^[2][8 0 0 2 7 1000000 ],D[2][8 0 -4 2 7 1000000 ]
Delta^[3][8 11 0 2 7 1000000 ],D[3][12 15 0 6 11 1000004 ]
Delta^[4][6 9 9 0 5 1000000 ],D[4][6 9 5 0 5 1000000 ]
Delta^[5][1 4 4 6 0 1000000 ],D[5][1 4 0 6 0 1000000 ]
Delta^[6][1000000 1000000 1000000 1000000 1000000 0 ],D[6][1000000 1000000 999996 1000000 1000000 0 ]

Błędów w wyniku jest dużo więcej niż opisałeś.

0

[off-top]

MarekR22 napisał(a):
  • [...] (np chciałbym poprawić kod tak, by dany były wczytywane z std::cin i zapisywane do std::cout [...].

Czyli zgodnie z filozofią Unix-a, punkt 2. Cytat z wiki:

  1. Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new "features".
  2. Expect the output of every program to become the input to another, as yet unknown, program. Don't clutter output with extraneous information. Avoid stringently columnar or binary input formats. Don't insist on interactive input.
  3. Design and build software, even operating systems, to be tried early, ideally within weeks. Don't hesitate to throw away the clumsy parts and rebuild them.
  4. Use tools in preference to unskilled help to lighten a programming task, even if you have to detour to build the tools and expect to throw some of them out after you've finished using them.

Oraz jego podsumowanie:

It was later summarized by Peter H. Salus in A Quarter-Century of Unix (1994):

  • Write programs that do one thing and do it well.
  • Write programs to work together.
  • Write programs to handle text streams, because that is a universal interface.

Należałoby uświadamiać początkujące osoby piszące programy konsolowe, żeby starały się nie otwierać plików o ile to nie jest konieczne (np. losowy dostęp do danych), bo wtedy tracimy możliwość wpinania ich w przetwarzanie potokowe.

Nie ma nic piękniejszego niż programy, które potrafią ze sobą współpracować.[/off-top]

0

@jvoytech:
bardziej chodzi mi o coś w tym stylu:

using SomeData = .... ;

SomeData  loadSomeData(std::istream& in)
{
     ....;
}

SomeData  loadSomeData(std::filesystem::path p = "In.txt")
{
     std::istream in{p};
     if (in) return loadSomeData(in);
     std::perror(p.string().c_str());
     return {};
}

To nadal czyta z pliku, ale poprawa takiego kodu, by działało z std::cin jest proste i przyjemne.
Ergo można kod uruchomić na kompilatorze online.
Albo można napisać test z wykorzystaniem std::stringsteam.
Kod staje się bardziej elastyczny.

1 użytkowników online, w tym zalogowanych: 0, gości: 1