rucksackproblem

napsi

Erfahrenes Mitglied
hallo liebe Gemeinde!

Ich habe ein Rätsel zu lösen, das auf dem Fussweg etwas mühsam ist und mit Hilfe eines kleinen Programmes sicherlich eine kurze Aufgabe ist.


Gepäckstücke (Nr. = Bezeichnung)
Nummer Gewicht in kg Wert in Euro
1 2.4 150
2 4.5 140
3 4.8 132
4 4.2 123
5 2.6 122
6 4.65 85
7 2.1 79
8 4.2 51
9 2.2 24
10 3.8 44
11 3.6 28
12 2.3 35
13 2.4 47
14 3.05 36
15 1.55 43
16 1.4 48
17 3.7 77
18 4.5 18
19 1.4 37
20 3.1 22
21 2.5 32


verfügbare Koffer
Nummer Gesamtgewicht kg Eigengewicht kg
A 10 1
B 9 1
C 9 0.4
D 8 1
E 12 2
G 13 1.5
H 14 2

Beschreibung:

In die Koffer A-H sollen nun die Gepäckstücke so aufgeteilt werden, dass einerseits das Gesamtgewicht (=Gepäckstück+Eigengewicht Koffer) nicht überschritten wird UND die Summe des Gepäckstückwertes je Koffer die 200 Euro nicht überschreitet.

Meine Frage: Hat jemand so etwas schon mal programmiert, bzw. kennt wer ein entsprechendes Programm zum Herunterladen?

Ich offe auf Eure Hilfe

lg.

Napsi
 

Anhänge

  • Rücksackproblem.pdf
    95,6 KB · Aufrufe: 28
Ich habe folgenden Code gefunden, allerdings ist der nur für einen Koffer, ich brauche aber x Rucksäcke, kann mir hier jemand helfen?
C++:
# include<stdio.h>
# include<conio.h>

void knapsack(int n, float weight[], float profit[], float capacity)
{
 float x[20], tp= 0;
 int i, j, u;
 u=capacity;

 for (i=0;i<n;i++)
     x[i]=0.0;

 for (i=0;i<n;i++)
 {
 if(weight[i]>u)
      break;
 else
     {
     x[i]=1.0;
     tp= tp+profit[i];
     u=u-weight[i];
     }
 }

 if(i<n)
       x[i]=u/weight[i];

 tp= tp + (x[i]*profit[i]);

 printf("n The result vector is:- ");
 for(i=0;i<n;i++)
        printf("%ft",x[i]);

 printf("m Maximum profit is:- %f", tp);

}

void main()
{
 float weight[20], profit[20], capacity;
 int n, i ,j;
 float ratio[20], temp;
 clrscr();

 printf ("n Enter the no. of objects:- ");
 scanf ("%d", &num);

 printf ("n Enter the wts and profits of each object:- ");
 for (i=0; i<n; i++)
 {
 scanf("%f %f", &weight[i], &profit[i]);
 }

 printf ("n enter the capacityacity of knapsack:- ");
 scanf ("%f", &capacity);

 for (i=0; i<n; i++)
 {
 ratio[i]=profit[i]/weight[i];
 }

 for(i=0; i<n; i++)
 {
    for(j=i+1;j< n; j++)
    {
      if(ratio[i]<ratio[j])
      {
      temp= ratio[j];
      ratio[j]= ratio[i];
      ratio[i]= temp;

     temp= weight[j];
     weight[j]= weight[i];
     weight[i]= temp;

     temp= profit[j];
     profit[j]= profit[i];
     profit[i]= temp;
     }
   }
 }

 knapsack(n, weight, profit, capacity);
 getch();
}

lg.

Napsi
 
Zuletzt bearbeitet von einem Moderator:
Hallo napsi,
der Code-Schnipsel den du gepostet hast, führt mit ziemlicher Sicherheit den Algorithmus von Nemhauser und Ullmann aus.

Hier kannst du dich in das Problem einigermaßen gut einlesen:
http://www-i1.informatik.rwth-aachen.de/~algorithmus/algo15.php

Dein Problem ist jetzt, dass du mehrere Rucksäcke zur Verfügung hast. Wie du dein Programm gestaltest, hängt deshalb von der konkreten Aufgabenstellung ab.
Ist beispielsweise eine Lösung gesucht, die möglichst gut und in kurzer Zeit berechenbar ist? Dann wäre der Algorithmus einfach umzuschreiben:
1) Schnapp dir den Koffer, der das größte Fassungsvermögen besitzt.
2) Führe den Algorithmus von Nemhauser und Ullmann aus, um die optimale Füllung für diesen Koffer zu finden.
3) Packe den Koffer (heißt: Entferne den Koffer und die Elemente in ihm aus der Liste).
4) Falls noch leere Koffer und Gegenstände vorhanden sind, führe den Algorithmus erneut aus.

Mit dem Vorgehen würdest du in überschaubarer Zeit eine Lösung erhalten, die zumindest nahe der optimalen Lösung ist.
Jedoch muss es nicht unbedingt die optimale Lösung sein! Es kann nämlich sein, dass der Gesamtwert aller Gegenstände in allen Koffern steigt wenn du einen Koffer nicht optimal füllst, da du dann einen anderen Koffer noch optimaler füllen kannst. Dieser Fall dürfte jedoch recht selten sein.

Falls du also den optimalen Fall für alle Koffer berechnen willst, müssen wir uns einen anderen Algorithmus überlegen.

Hoffe ich konnte etwas helfen.
Gruß Technipion
 
Hallo Technipion!

Prinzipiell war dein Post schon eine Hilfe, bei der Umsetzung scheitere ich allerdings.

Danke trotzdem

lg.

Napsi
 
Hallo napsi,
wo genau scheiterst du denn? Du bist ja schließlich in das Forum eingetreten damit wir dir helfen...

Also schieß los, zusammen schaffen wir das :D

Gruß Technipion
 
Hallo napsi.
Ist das Thema hier noch offen?

Nun, ich verseuche es dennoch mal ^^'
ich habe das ganze mal in 2 Strukturen gepackt für Gepäck und Koffer, habe es auch so gemacht, dass es rekursiv arbeitet.
es versucht das erste stück in den ersten koffer zu legen und ruft sich dann selbst auf. Wenn er der ste koffer mit dem Gepäckstück überfüllt wäre versucht er es im zweiten usw.
wenn bei dieser Sortierung alle koffer gefüllt werden aber noch Gepäckstücke übrig bleiben gibt es eine leere liste zurück.
Wenn alle Gepäckstücke verteilt wurden wird die Liste der verteilten Gepäckstücke sofort zurückgegeben.

Das alles wird aus 2 listen ausgelesen [mit beliebig vielen Koffern und Gepäckstücken ]
das Ergebnis wird in einer "result.html" gespeichert bei erfolg und diese wird direkt geöffnet.

Zu deiner Vorgabe: Alle Gepäckstücke und Koffer zusammen wiegen 93,65Kg alle Koffer zusammen fassen aber lediglich 75Kg... [es kann keine Lösung geben xP]
Wenn du aber den Kofferplatz erhöhst dann wird der folgende Algorithmus auch eine Lösung finden.

Hier der Code [hoffentlich verständlich ^^' ich gab mein bestes beim kommentieren, aber die HTML funktionen überlass ich einfach mal dem "wirst du schon verstehen/akzeptieren dass es geht"]

C++:
//Author: Neko
//Ziel: Tutorials.de

//Vorgabe sind eine liste von Gepäckstücken mit folgenden werten:
//ID | Gewicht | Wert in €
//und eine liste von koffern mit folgenden werten:
//ID [als char] | Gesamtgewicht | Eigengewicht

//Ziel ist es alle Gepäckstücke in den Koffern zu verstauen so dass weder das Gesamtgewicht des Koffers überschritten wird noch der Gesamtwert des Koffers nicht über 200€ liegt

//Die Konstante mit den 200€ habe ich hier im Beispiel früh definiert und sie ist änderbar im Code bei bedarf
//Die liste mit den Gepäckstücken und Koffern wird bei jeden Aufruf neu eingelesen

//Für die ausgabe des ergebnisses auf der Konsole
#include <iostream>

//Für das einlesen der gepäck und kofferlisten
#include <fstream>

//Für die Listen
#include <list>

//zum dateien zeilenweise auslesen
#include <string>

//Maximaer Wert in einem Koffer
#define MAXVALUE 200.00f

using namespace std;

//Gepäckstück
struct Package
{
    //Nummer
    int id;

    //gewicht
    float weight;

    //Wert in €
    //Nutze eine Float-variable um auch cent beträge zu zu lassen
    float value;
};

//Koffer
struct Box
{
    //Koffer"name"
    char id;

    //maximalgewicht
    float max;

    //eigengewicht
    float weight;

    //Beinhaltende Gepäckstücke
    list<Package> packages;

    //Teste ob der Koffer so überfüllt wäre mit diesem Gepäckstück
    bool test(Package add)
    {
        //Testwerte, wenn überschritten, direkter abbruch mit negativ, sonst positiv
        float t_value = 0.0f;
        float t_weight = weight;
        //Durchlaufe jedes Gepäckstück im Koffer
        for (list<Package>::iterator it = packages.begin(); it != packages.end(); ++it)
        {
            //Füge die werte den gesamtwerten hinzu
            t_value += (*it).value;
            t_weight += (*it).weight;
            //teste ob der wert überschritten wurde
            if (t_value > MAXVALUE || t_weight > max)
            {//zu schwer/zu teuer
                return false;
            }
        }
        t_value += add.value;
        t_weight += add.weight;
        //teste ob der wert überschritten wurde
        if (t_value > MAXVALUE || t_weight > max)
        {//zu schwer/zu teuer
            return false;
        }

        //Keine überschreitung gefunden, also gut soweit
        return true;
    }

    //Funktionen für das gesamtgewicht und den gesamtwert
    float allweight()
    {
        float t_weight = weight;
        //Durchlaufe jedes Gepäckstück im Koffer
        for (list<Package>::iterator it = packages.begin(); it != packages.end(); ++it)
        {
            //Füge die werte den gesamtwerten hinzu
            t_weight += (*it).weight;
        }
        return t_weight;
    }
    float allvalue()
    {
        float t_value = 0.00f;
        //Durchlaufe jedes Gepäckstück im Koffer
        for (list<Package>::iterator it = packages.begin(); it != packages.end(); ++it)
        {
            //Füge die werte den gesamtwerten hinzu
            t_value += (*it).value;
        }
        return t_value;
    }
};

//Liest die Gepäckliste ein
list<Package> readPackages()
{
    cout << "Gepäck einlesen:" << endl;
    //Rückgabewert
    list<Package> ret;
 
    //Dateihandler und direkt öffnen
    fstream f;
    f.open("Packages.list.txt", ios::in);
    //Fehler?
    if (!f)
    {
        cout << "Packages.list.txt konnte nicht geöffnet werden" << endl;
        getchar();
        exit(NULL);
    }
    //und einmal bis zum ende durchlesen
    while (!f.eof())
    {
        //Temporär, um eine zeile zu bearbeiten
        string line;
        Package pkg;
        //eine zeile aus der datei lesen
        getline(f,line);

        //id einlesen
        cout << "id:" << (pkg.id = atoi(line.c_str()));


        //anfang der line sicher abschneiden
        if (line.find(' ') == string::npos)
        {//ups, da fehlt ein Leerzeichen, vergiss diese line
            cout << endl;
            continue;//und mach mit der nächsten weiter
        }
        line = line.substr(line.find_first_of(' '));
        //auf weitere leerzeichen prüfen
        while (line.front() == ' ')
        {
            line = line.substr(1);
        }

        //Gewicht einlesen
        cout << " weight:" << (pkg.weight = atof(line.c_str()));

        //anfang der line sicher abschneiden
        if (line.find(' ') == string::npos)
        {//ups, da fehlt ein Leerzeichen, vergiss diese line
            cout << endl;
            continue;//und mach mit der nächsten weiter
        }
        line = line.substr(line.find_first_of(' '));
        //auf weitere leerzeichen prüfen
        while (line.front() == ' ')
        {
            line = line.substr(1);
        }

        //Wert einlesen
        cout << " value:" << (pkg.value = atof(line.c_str()));
        cout << " bestätigt" << endl;

        //alle werte eingelesen: in liste einfügen
        ret.push_back(pkg);

    }


    //Schön wieder schließen
    f.close();

    return ret;
}

//liest die Kofferliste ein
list<Box> readBoxes()
{
    cout << "Koffer einlesen:" << endl;
    //Rückgabewert
    list<Box> ret;
    //Dateihandler und direkt öffnen
    fstream f;
    f.open("Boxes.list.txt", ios::in);
    //Fehler?
    if (!f)
    {
        cout << "Boxes.list.txt konnte nicht geöffnet werden" << endl;
        getchar();
        exit(NULL);
    }
    //und einmal bis zum ende durchlesen
    while (!f.eof())
    {
        //Temporär, um eine zeile zu bearbeiten
        string line;
        Box bx;
        //eine zeile aus der datei lesen
        getline(f, line);

        //id einlesen
        cout << "id:" << (bx.id = line.front());


        //anfang der line sicher abschneiden
        if (line.find(' ') == string::npos)
        {//ups, da fehlt ein Leerzeichen, vergiss diese line
            cout << endl;
            continue;//und mach mit der nächsten weiter
        }
        line = line.substr(line.find_first_of(' '));
        //auf weitere leerzeichen prüfen
        while (line.front() == ' ')
        {
            line = line.substr(1);
        }

        //Maximalgewicht einlesen
        cout << " maxweight:" << (bx.max = atof(line.c_str()));

        //anfang der line sicher abschneiden
        if (line.find(' ') == string::npos)
        {//ups, da fehlt ein Leerzeichen, vergiss diese line
            cout << endl;
            continue;//und mach mit der nächsten weiter
        }
        line = line.substr(line.find_first_of(' '));
        //auf weitere leerzeichen prüfen
        while (line.front() == ' ')
        {
            line = line.substr(1);
        }

        //eigengewicht einlesen
        cout << " weight:" << (bx.weight = atof(line.c_str()));
        cout << " bestätigt" << endl;

        //alle werte eingelesen: in liste einfügen
        ret.push_back(bx);

    }


    //Schön wieder schließen
    f.close();
    return ret;
}

//Rekursive Funktion die alle möglichen Koffer/Gepäck kombinationen ausprobiert
//rückgabe ist eine gefüllte liste, ansonsten eine leere
list<Box> trytofill(list<Box> t_Boxes, list<Package> t_Packages)
{
    //schon alles eingefüllt?
    if (t_Packages.empty())
    {//dann einfach die liste so zurückgeben
        return t_Boxes;
    }

    //Rückgabewert [leer, fals nichts gefunden]
    list<Box> ret;

    //Durchlaufe alle Koffer
    //und versuche dabei den ersten koffer aus der liste einzufügen
    //mit dieser konstellation wird dann weiter getestet
    //bis die liste leer ist
    for (list<Box>::iterator it = t_Boxes.begin(); it != t_Boxes.end(); ++it)
    {
        //Würde das gepäckstück passen?
        if ((*it).test(t_Packages.front()))
        {
            //wenn ja, dann teste weiter
            list<Package> Packages = t_Packages;
            (*it).packages.push_back(Packages.front());
            Packages.pop_front();//aus der für diese box potenziellen liste löschen
            list<Box> possibleBoxes = trytofill(t_Boxes, Packages);
            if (!possibleBoxes.empty())
            {//liste ist nicht leer, also wurde eine lösung gefunden
                return possibleBoxes;
            }
        }//wenn das so nicht passt dann einfach weiter testen
    }

    return ret;
}
//ausgabe in HTML date
void print(list<Box> FinaleListe)
{
    cout << "fertig, speichere in result.html" << endl;
    fstream f;
    f.open("result.html", ios::out);
    if (!f)
    {
        cout << "ERROR OPENING [result.html]" << endl;
    }

    f << "<DOCTYPE HTML>" << endl;
    f << "<html>" << endl;
    f << "<head>" << endl;
    f << "</head>" << endl;
    f << "<body>" << endl;
    f << "<table border=\"2\">" << endl;
    //bit ist die abkürzung für BoxITerator
    for (list<Box>::iterator bit = FinaleListe.begin(); bit != FinaleListe.end(); ++bit)
    {//Jeden koffer durchgehen und die werte ausgeben:
        f << "<tr bgcolor=\"FFa800\">" << endl;
        f << "<td>Koffer:" << (*bit).id << "</td>" << endl << "<td>Maximalgewicht:" << (*bit).max << "</td>" << endl << "<td>Eigengewicht:" << (*bit).weight << "</td>" << endl << "<td>Gesamtgewicht:" << (*bit).allweight() << "</td>" << endl << "<td>Gesamtwert:" << (*bit).allvalue() << "</td>" << endl << "<td>Gepäckstücke:" << (*bit).packages.size() << "</td>" << endl;
        f << "</tr>" << endl;
        //und darunter jedes enthaltene gepäckstück auflisten
        f << "<tr>" << endl;
        f << "<td>Gepäck ID</td>" << endl << "<td colspan=\"3\">Gewicht</td>" << endl << "<td>Wert</td>" << endl;
        f << "</tr>" << endl;
        for (list<Package>::iterator pit = (*bit).packages.begin(); pit != (*bit).packages.end(); ++pit)
        {
            f << "<tr>" << endl;
            f << "<td>" << (*pit).id << "</td>" << endl << "<td colspan=\"3\">" << (*pit).weight << "</td>" << endl << "<td>" << (*pit).value << "</td>" << endl;
            f << "</tr>" << endl;
        }
    }
    f << "</table>" << endl;
    f << "</body>" << endl;
    f << "</html>" << endl;
    f.close();
}

//Haputprogramm
int main(int argc, char* argv[])
{
    //Gepäckliste
    list<Package> Packages = readPackages();
    //Kofferliste
    list<Box> Boxes = readBoxes();
    cout << "------------------------TESTE:" << endl;
    list<Box> FinaleListe = trytofill(Boxes,Packages);
    if (FinaleListe.empty())
    {//leider wurde keine lösung gefunden
        cout << "es gibt keine lösung" << endl;
    }
    else{//ausgeben:
        print(FinaleListe);
    }
    //getchar();
    system("result.html");
    //uuuuund fertig
    return NULL;
}

Irgendwie habe ich gefallen gefunden durchs Forum zu schlendern und hier&da mal n Code liegen zu lassen ^^

Achja, und die die Listendateien sind einfache listen mit einem eintrag pro zeile in folgendem format:
Nummer[LEERZEICHEN]Gewicht[LEERZEICHEN]Wert
bzw
Nummer[LEERZEICHEN]Gesamtgewicht[LEERZEICHEN]Eigengewicht


Grüße Neko :3
 
Hi Neko

Ich hoffe es stört dich nicht, wenn ich kurz ein Feedback zu deinem Code gebe. Generell gehe ich davon aus, dass neuer Code mittlerweile nach dem "neuen" Standard erstellt und erlernt wird.

Ein kleiner Vorschlag:
Statt mehrmals:
C++:
line = line.substr(line.find_first_of(' '));
while (line.front() == ' ') {
    line = line.substr(1);
 }

Könntest du auch:
C++:
line.erase(line.begin(), std::find_if(line.begin(), line.end(), [](wchar_t c) { return !std::isspace(c, locale); }));

Nicht so schön ist:
C++:
while(!f.eof())

Besser:
C++:
while(std::getline(f, line))

Verzichte auf #define wo immer möglich. Sowas ist besser:
C++:
const float MAXVALUE = 200.00f;

Die C-Funktion atoi in atoi(line.c_str()) kannst du ersetzen durch die neue C++11 Version std::stoi(line)

Verzichte auf using namespace std;

In deinem Fall (wenn die std:: wirklich zu viel sind):
C++:
using std::cout;
using std::getline;
using std::list;
using std::endl;
using std::fstream;

Generell empfehle ich aber einfach ganz darauf zu verzichten und überall den Namespace std anzugeben. Ist schon für den Leser angenehmer weil sofort sieht, welcher Code aus der - gut getesteten - STL kommt und welchen Code er ggf. hinterfragen muss.

Grüsse
Cromon
 
Vielen Dank Cromon

Du hast meine Hoffnung in die Menschheit wieder hergestellt *^*
Es gibt doch noch gute und konstruktive Kritik da draußen ^^


Die Funktion "line.erase" wie du sie nutzt... scheint mir ein wenig kompliziert >.<
Mag aber auch daran liegen dass ich zu lange nicht geschlafen habe und erst seit ner Stunde wieder in Schland bin xP
[grüße aus Wien aus dem Zug zurück ^^]

zu "getline": Ich wusste nicht dass es positiv [true]zurückgibt wenn gelesen werden konnte und negativ [false]wenn nicht ^^'
Danke für den Hinweis

wegen des "#define"s... ist das wirklich so schlimm?
Und: Gibt es während der Laufzeit einen Unterschied bei der Geschwindigkeit? [Define ersetzt ja vor dem Kompilieren alles als Klartext... und const wird doch während der Laufzeit als variable mit konstantem wert behandelt... wäre das nicht im "Extremfall" langsamer mit const zu arbeiten?]

das mit den Namespaces... Ich weiß dass ich zu faul bin xD
Aber nochmals danke ^^'

Ich werde meinen Beitrag aktualisieren mit dem geänderten Code sobald ich zuhause bin ^^'
[sollte gegen 11/12 sein]

Ich bewundere gerade dein Wissen ^^'
und würde dich spontan in mein kleines Projekt einladen:
https://www.tutorials.de/threads/su...m-gewissen-etwas-grafikprogrammierung.399773/

Ich verspreche auch, dass du kein "using" sehen wirst xP

Grüße, Neko :3

EDIT:
Ich habe mir bisher alles was ich zum Coden weiß aus dem imInternet direkt ins Gehirn fließen lassen...
Also tut es mir leid wenn ich einige alten Methoden verwende ^^'
Ich bin sozusagen auf aktuelleKritik angewiesen und schätze diese sehr :3

Ich hoffe es stört niemanden wenn ich ab und zu durchs Forum schlendre und einfach mal aufgaben löse und den kompletten Code poste ^^'
1. zur Lösung des Problems
und
2. damit ich durch Kritik lerne ^^

und damit ich dadurch nicht jemandes Hausaufgaben mache versuche ich darauf zu achten dass die posts 1/2 Wochen alt sind ^^'

Grüße die zweite, Neko out xP
 
Zuletzt bearbeitet:
Zurück