Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Problem plecakowy - algorytm z nawrotami

Ostatnio zmodyfikowano 2015-05-07 20:36
Autor Wiadomość
james4444
Temat założony przez niniejszego użytkownika
Problem plecakowy - algorytm z nawrotami
» 2015-05-06 20:09:13
Witam,

1.Problem plecakowy:
–Mamy zbiór przedmiotów, z których każdy ma wagę i wartość.
–Waga i wartość są wartościami całkowitymi, większymi od zera
–Złodziej planuje przenieść skradzione przedmioty w plecaku, ale plecak rozerwie się, jeżeli całkowita waga przedmiotów przekroczy pewną wartość W
–Zadaniem złodzieja jest znalezienie zbioru przedmiotów o największej wartości całkowitej bez przekroczenia maksymalnej wagi W

Możemy rozwiązać ten problem przy użyciu drzewa przestrzeni stanów:
–Poruszając się w lewo od korzenia dodajemy pierwszy przedmiot do plecaka, zaś w prawo odrzucamy go.
–Podobnie idziemy w lewo od węzła na poziomie 1, gdy dodajemy drugi przedmiot, a w prawo, gdy odrzucamy go, itd.
–Każda ścieżka od korzenia do liścia jest potencjalnym rozwiązaniem.

Problem jest problemem optymalizacyjnym.
–Oznacza to, że nie wiemy, czy węzeł zawiera rozwiązanie, dopóki nie zostanie zakończone przeszukiwanie.
–Jeżeli przedmioty dołączone aż do bieżącego węzła mają większą wartość niż najlepsze znalezione do tej pory rozwiązanie, zmieniamy wartość najlepszego dotychczas znalezionego rozwiązania.
–Jednak nadal możemy znaleźć lepsze rozwiązanie w jednym z węzłów podrzędnych („kradnąc więcej przedmiotów”)
–Z tego powodu w problemach optymalizacyjnych zawsze odwiedzamy węzły podrzędne obiecujących węzłów

2.Ogólny algorytm z powrotami dla problemów optymalizacyjnych

void checknode (node v)
{
  node u;
  if(value(v) is better than best) best = value(v);
  if(promising(v)) for(each child u of v) checknode(u);
}

Zmienna best ma wartość najlepszego znalezionego dotychczas rozwiązania, natomiast wartość value(v) stanowi wartość rozwiązania w węźle.
Po zainicjowaniu zmiennej best wartością mniejszą niż najgorsze potencjalne rozwiązanie na najwyższym poziomie przekazywany jest węzeł korzenia.

Węzeł jest obiecujący tylko wtedy, gdy możemy przejść do jego potomków.

3. Cechy pozwalające stwierdzić, że węzeł jest nieobiecujący

Cecha 1: osiągnięcie wagi niepozwalającej dodanie już żadnego przedmiotu
–Jeżeli weight jest sumą wag wszystkich przedmiotów dołączonych do określonego węzła, to węzeł jest nieobiecujący, gdy: weight >= W
(węzeł jest nieobiecujący nawet wtedy, gdy weight jest równe W, ponieważ w przypadku optymalizacji „obiecujący” oznacza, że powinniśmy przeglądać węzły potomne)

Cecha 2: (korzystamy z wniosków z algorytmu zachłannego)
–Wykorzystamy podejście zachłanne jedynie w celu ograniczenia zakresu przeszukiwania.
–Posortujmy przedmioty w porządku nierosnącym, według wartości pi/wi , gdzie wi oraz pi to odpowiednio waga i wartość i-tego przedmiotu.
–Założenie: Próbujemy określić czy bieżący węzeł jest obiecujący

–Niezależnie od tego, w jaki sposób próbowaliśmy wybrać pozostałe przedmioty, nie możemy uzyskać większego zysku, niż wówczas, jeżeli moglibyśmy skorzystać z ograniczeń ułamkowego problemu plecakowego od tego węzła dalej ( w problemie tym złodziej może ukraść dowolny ułamek z zabranych przedmiotów)
–Dlatego możemy znaleźć górną granicę zysku, jaki może zostać uzyskany poniżej bieżącego węzła.


====ALGORYTM====
–Niech profit będzie sumą zysków z przedmiotów dołączonych do bieżącego węzła
–Niech weight będzie sumą wag tych przedmiotów
–Inicjujemy zmienne bound (granica) oraz totweight wartościami profit oraz weight

–Następnie w sposób zachłanny zbieramy przedmioty, dodając ich wartości do zmiennej bound oraz ich wagi do zmiennej totweight, aż do natrafienia na przedmiot, który po zabraniu spowoduje przekroczenie przez totweight wartości W
–Następnie zabieramy ułamek przedmiotu, na jaki pozwala waga W i dodajemy wartość tego ułamka do ostatniej wagi.
–Jeżeli jesteśmy w stanie zabrać tylko ułamek wagi, węzeł ten nie może prowadzić do zysku równego bound, ale wartosć bound jest nadal górną granicą zysku, jaki możemy osiągnąć poprzez rozwinięcie tego węzła.

–Załóżmy, że węzeł znajduje się na poziomie i, a węzeł na poziomie k jest tym, który spowoduje przekroczenie przez sumę wag wartości W, wtedy:
                    totweight = weight + E ( suma od j=i+1 do k-1 ) wj
oraz
                    bound = ( profit + E ( suma od j=i+1 do k-1 ) pj ) + (W-totweight) * (pk/wk)
                         /zysk z pierwszych k-1 zebranych przedmiotow//pojemnosc k-tego przedmiotu//zysk na jednostke wagi/

–Jeżeli maxprofit ma wartość zysku najlepszego znalezionego do tej pory rozwiązania, to węzeł na poziomie i jest nieobiecujący, jeżeli:
bound <= maxprofit

Uwaga! Korzystamy tu z zasad algorytmu zachłannego jedynie w celu uzyskiwania granicy wskazującej nam, czy powinniśmy kontynuować rozwijanie węzła poniżej węzła bieżącego. Nie korzystamy z nich w celu zachłannego zbieranie przedmiotów bez możliwości późniejszej zmiany decyzji (jak jest to realizowane przez algorytm zachłanny)

JEŚLI KTOŚ TO WSZYSTKO PRZECZYTAŁ I ZROZUMIAŁ, TO WYRAZY UZNANIA ODE MNIE, SAM BYM ZAMKNĄŁ SZYBKO KARTĘ I POSZEDŁ ROBIĆ AMBITNIEJSZE RZECZY
C/C++
#include <iostream>
#include <fstream>
using namespace std;
struct rekord {
    int cena, waga, jednostka;
};
struct wezel {
    wezel * lewy, * prawy;
    int n, wmax, profit, weight, maxprofit, poziom, poprzednik;
};
rekord * tablica_rekordow( ifstream & dane, int rozmiar )
{
    rekord * t, z1;
    t = new rekord[ rozmiar + 1 ];
    z1.cena = 0; z1.waga = 0; z1.jednostka = 0; * t = z1;
    for( int i = 1; i < rozmiar + 1; i++ )
    {
        dane >> z1.cena; dane >> z1.waga; dane >> z1.jednostka;
        *( t + i ) = z1;
    }
    return t;
}
int ustal_bound( rekord * tab1, wezel * v1 )
{
    int k, t = v1->weight, b1 = v1->profit, sw = 0, sp = 0;
    for( k = 0; k < v1->n + 1; k++ ) { sw += tab1[ k ].waga; if( sw > v1->wmax ) break; }
    sw = 0; if( k == v1->n ) k++;
   
    if( v1->poziom + 1 <= k - 1 )
    {
        for( int j = v1->poziom + 1; j <= k - 1; j++ ) { sw += tab1[ j ].waga; sp += tab1[ j ].cena; }
        t += sw;
    }
    if( k == v1->n + 1 ) { b1 += sp; } else { b1 += sp +( v1->wmax - t ) * tab1[ k ].jednostka; }
    cout << "k = " << k << endl << "t = " << t;
    return b1;
}
void nawracaj( rekord * tab, wezel * & v )
{
    int b;
    v->poziom++;
    v->profit += tab[ v->poziom ].cena;
    v->weight += tab[ v->poziom ].waga;
    if( v )
    {
        if( v->lewy )
        {
            if( v->weight < v->wmax )
            {
                if( v->profit > v->maxprofit ) v->maxprofit = v->profit;
               
                b = ustal_bound( tab, v );
                if( b > v->maxprofit )
               
               
               
                {
                    cout << endl << "p = " << v->profit << endl << "w = " << v->weight << endl << "mp = " << v->maxprofit;
                    cout << endl << "b = " << b << endl << "=========" << endl;
                    v = v->lewy;
                    nawracaj( tab, v ); //lewy
                }
                else
                {
                    cout << "p = " << v->profit << endl << "w = " << v->weight << endl << "mp = " << v->maxprofit;
                    cout << endl << "b = " << b << endl << "=========" << endl;
                    v = v->prawy;
                    nawracaj( tab, v ); //prawy
                }
            }
        } /*
                if(v->prawy)
                {
                        v->profit-=tab[v->poziom].cena; //przepisuje poprzednik
                        v->weight-=tab[v->poziom].waga;
                        if(v->profit>v->maxprofit)v->maxprofit=v->profit;
                        b=ustal_bound(tab, v);
                        if(b>v->maxprofit)
                        {
                            cout<<endl<<"p = "<<v->profit<<endl<<"w = "<<v->weight<<endl<<"mp = "<<v->maxprofit;
                            cout<<endl<<"b = "<<b<<endl<<"========="<<endl;
                            v=v->lewy;
                            nawracaj(tab, v); //lewy
                        }
                        else
                        {
                            cout<<endl<<"p = "<<v->profit<<endl<<"w = "<<v->weight<<endl<<"mp = "<<v->maxprofit;
                            cout<<endl<<"b = "<<b<<endl<<"========="<<endl;
                            v=v->prawy;
                            nawracaj(tab, v);//prawy
                        }
                }*/
    }
}
int main()
{
    ifstream plik;
    ofstream zapis( "wyjscie.txt" );
    plik.open( "dane.txt" );
    if( !plik ) { cout << "Nie udalo sie otworzyc pliku"; return 0; }
    if( !zapis ) { cout << "Nie udalo sie utworzyc pliku"; return 0; }
    int n, w;
    plik >> n; plik >> w;
    rekord * tablica;
    tablica = tablica_rekordow( plik, n );
    wezel * korzen;
    korzen = new wezel;
    korzen->profit = 0; korzen->weight = 0; korzen->n = n; korzen->wmax = w; korzen->maxprofit = 0; korzen->poziom = 0; korzen->poprzednik = 0;
    int ik, sik = 0, f = 0, sip = 0;
    for( ik = 0; ik < n + 1; ik++ ) { sik += tablica[ ik ].waga; if( sik > w ) { f = 1; break; } sip += tablica[ ik ].cena; }
    if( f == 0 ) ik = n + 1; else { sik -= tablica[ ik ].waga; sip +=( w - sik ) * tablica[ ik ].jednostka; }
    cout << "========" << endl << "0 0" << endl << "p = " << korzen->profit << endl << "w = " << korzen->weight << endl << "mp = " << korzen->maxprofit;
    cout << endl << "k = " << ik << endl << "t = " << sik << endl << "b = " << sip << endl << "=========" << endl;
    nawracaj( tablica, korzen );
    delete[] korzen;
    delete[] tablica;
    plik.close();
    zapis.close();
    return 0;
}
Dane wejściowe:
4 16
40 2 20
30 5 6
50 10 5
10 5 2

n = 4, ilosc rzeczy ( ilosc wierszy ), W(wmax) = 16 ( pojemnosc plecaka, maksymalne upakowanie ), potem mamy n kolejnych wierszy w ktorych kolejno: cena przedmiotu, waga przedmiotu, cena na jednostke ( cena / waga )

Tłumaczę, jak ja to widzę: w mainie zmienna tablica -> przechowuje rekordy z danych wejsciowych, struktura o nazwie wezel, zawiera w sobie wszystkie potrzebne do operacji zmienne, w tym struktura ta posiada dwa wskazniki, wskazujace na ta strukture o nazwach: lewy i prawy, chodzi mi o to, ze w zaleznosci od danych warunkow bede przemieszczal sie do wezla ( wierzcholka konkretniej ) lewego lub prawego. Główny problem mam z wywoływaniem rekurencji w zależności właśnie od tego, czy ma iść do węzła lewego bądź prawego, niekiedy muszę się cofać o parę wierzchołków do góry, mam z tym problem. Zmienna poziom ustala mi poziom na jakim jest wykonywana dana operacja ( w prostym znaczeniu jest to indeks ).
Wyślę później jeszcze wyniki dla podanych danych wejściowych, kolejne operacje i zmienne jak się zmieniają, żeby zrozumieć problem.
P-131822
darko202
» 2015-05-07 09:09:30
dużo napisałeś, ale nie rozumiem Twojego problemu
z czym masz problem ?


"problem plecakowy" jest algorytmem z kanonu
jest omawiane w wielu miejscach np. na
http://algorytmy.info​/KodKontroler/kod/360/
P-131842
Fireho
» 2015-05-07 12:59:52
I w czym masz problem? Bo z tematu wynika że już skończyłeś - na gotowe prace jest ten dział.

PS: Zostaw taki kod na kilka tygodni, wróć i spróbuj domyślić się co znaczą nazwy
n
,
t
,
z1
,
k
,
t
,
b1
,
sw
,
sp
,
v
,
b
,
n
,
w
,
v1
,
tab
,
tab1
 oraz co robią te gęsto zbite bloki pętli, zmiennych i
if
ów - powodzenia.
P-131843
Piastlis
» 2015-05-07 20:36:15
O ile zrozumiałem to ma to być taki fikuśny generator kombinacji.Ale dlaczego jest on taki zawiły i nieczytelny?? I po co jako zmienna :cena na jednostke ( cena / waga ). To wynik dzielenia ). I po co wkładać ułamek przedmiotu??!!
P-131866
« 1 »
  Strona 1 z 1