Murtoreppuongelma: Ahne algoritmi esimerkin kanssa

โšก ร„lykรคs yhteenveto

Murtolukuinen reppuongelma kรคyttรครค ahnetta algoritmia, joka lajittelee paketit arvon ja painon suhteen mukaan ja ottaa esineet tรคssรค jรคrjestyksessรค, jolloin esineiden murto-osat tรคyttรคvรคt jรคljellรค olevan kapasiteetin taatusti optimaalisen ratkaisun saavuttamiseksi.

  • ๐Ÿ’ก Ahne strategia: Jokaisessa vaiheessa tehdรครคn paikallisia optimaalisia valintoja siinรค toivossa, ettรค saavutetaan globaali optimi kokonaisongelmalle.
  • ๐Ÿ‡ง๐Ÿ‡ท Arvo/painosuhde: Paketit lajitellaan yksikkรถkustannusten V[i] / W[i] mukaan laskevaan jรคrjestykseen ennen valinnan aloittamista.
  • ๐Ÿ“ฆ Murtolukusรครคntรถ: Seuraavan paketin osittainen siivu tรคyttรครค mahdollisen jรคljelle jรครคvรคn kapasiteetin, mikรค takaa optimaalisen ratkaisun osittaismuunnokselle.
  • โฑ๏ธ Monimutkaisuus: O(n log n) pikalajittelulla tai yhdistรคmislajittelulla, jossa lajitteluvaihe on vallitseva valintasilmukan sijaan.
  • ๐Ÿšซ rajoitus: Sama ahneussรครคntรถ epรคonnistuu 0/1 Knapsackissa, jossa esineitรค ei voida jakaa, joten kรคytetรครคn dynaamista ohjelmointia.
  • ๐Ÿš€ Kรคyttรถ: Lastin lastaus, salkun allokointi, pilvikaistanleveyden jakaminen ja tekoรคlyresurssien aikataulutus perustuvat kaikki Fractional Knapsackiin.

Murtolukuinen reppuongelma Ahne algoritmi

Mikรค on ahne strategia?

Ahneita algoritmeja valitsevat parhaan paikallisen vaihtoehdon jokaisessa vaiheessa toivoen, ettรค ketju paikallisia optimeja tuottaa globaalisti optimaalisen ratkaisun. Kuten dynaaminen ohjelmointi, ne kohdistuvat optimointiongelmiin, mutta eivรคt koskaan katso taaksepรคin harkitakseen uudelleen aiempia pรครคtรถksiรค.

Ahneet algoritmit ovat yleensรค yksinkertaisia โ€‹โ€‹kirjoittaa, nopeita (usein lineaarisessa tai neliรถllisessรค ajassa), helppoja debugata ja muistia vรคhรคn kuluttavia. Kompromissina on, ettรค tulos ei ole aina optimaalinen, joten strategia toimii vain ongelmissa, joilla on todistetusti ahneutta kestรคvรค rakenne.

Ahneet strategiat ratkaisevat kombinatorisen optimoinnin rakentamalla ratkaisun A yhden komponentin Ai kerrallaan. Jokaisella askeleella valitaan Ai optimaalisesti nykyisillรค rajoituksilla ja kutistetaan ongelma pienemmรคksi osaongelmaksi.

Ahneen metodin on oltava oikein, joten kahden ominaisuuden on tรคytyttรคvรค:

  1. Ahneudenhaluinen ominaisuus: Jokaisella askeleella saavutettu paikallinen optimi johtaa globaaliin optimiin. Valinta riippuu menneistรค pรครคtรถksistรค, mutta ei tulevista.
  2. Optimaalinen alusrakenne: Koko ongelman optimaalinen ratkaisu sisรคltรครค sen osaongelmien optimaaliset ratkaisut.

Ahneella algoritmilla on viisi osaa:

  1. Ehdokasjoukko, josta ratkaisut rakennetaan.
  2. Valintafunktio, joka valitsee parhaan seuraavan ehdokkaan.
  3. Toteutettavuusfunktio, joka tarkistaa, voiko ehdokas laajentaa nykyistรค osaratkaisua.
  4. Objektifunktio, joka arvostaa tรคydellistรค tai osittaisratkaisua.
  5. Arviointifunktio, joka ilmoittaa, kun ratkaisu on valmis.

Ahneen idea

Greedy One lajittelee paketit pelkรคstรครคn arvon mukaan:

  • Lajittele paketit arvojรคrjestyksessรค, joka ei ole nouseva.
  • Kรคvele lajiteltu lista lรคpi ja lisรครค jokainen paketti reppuun, jos jรคljellรค oleva kapasiteetti riittรครค.

Tรคmรค sรครคntรถ ei aina anna optimaalista vastausta. Vastaesimerkki:

  • Parametrit: n = 3, M = 19.
  • Paketit: {i = 1; W = 14; V = 20}, {i = 2; W = 6; V = 16}, {i = 3; W = 10; V = 8} โ€” korkea arvo, mutta myรถs suuri paino.
  • Ahne valitsee paketin 1, jonka kokonaisarvo on 20, kun taas optimaalinen valinta (paketti 2, paketti 3) saavuttaa arvon 24.

Idea Greedy Twosta

Greedy Two lajittelee paketit pelkรคstรครคn painon mukaan:

  • Lajittele paketit painon mukaan ei-alenevassa jรคrjestyksessรค.
  • Kรคvele lajiteltu lista lรคpi ja lisรครค jokainen paketti reppuun, jos jรคljellรค oleva kapasiteetti riittรครค.

Tรคmรคkรครคn sรครคntรถ ei ole optimaalinen. Vastaesimerkki:

  • Parametrit: n = 3, M = 11.
  • Paketit: {i = 1; W = 5; V = 10}, {i = 2; W = 6; V = 16}, {i = 3; W = 10; V = 28} โ€” kevyet, mutta edulliset.
  • Ahne Kaksi valintaa (paketti 1, paketti 2) yhteisarvolla 26, kun taas optimaalinen valinta (paketti 3) saavuttaa 28.

Ahneen Kolmon idea

Greedy Three korjaa molemmat ongelmat yhdistรคmรคllรค arvon ja painon yhdeksi ranking-avaimeksi. Se on murtolukuisen reppuongelman standardimenetelmรค.

  • Laske jokaisen paketin yksikkรถkustannus V[i] / W[i].
  • Lajittele paketit yksikkรถhinnan mukaan ei-nousevaan jรคrjestykseen.
  • Kรคvele lajiteltu lista lรคpi ja lisรครค jokainen paketti, jos jรคljellรค oleva kapasiteetti riittรครค.

Greedy Three lajittelee yksikkรถhinnan mukaan

Ahne Kolme lajittelua yksikkรถkustannuksen mukaan V[i] / W[i]

Idea: Laske jokaisen paketin arvo-painosuhde V[i] / W[i], lajittele arvot laskevaan jรคrjestykseen ja ota suurin mahdollinen suhde ensin, kunnes reppu on tรคynnรค.

Totuuden puolesta murto- variantissa, kun seuraavaan pakettiin ei mahdu kokonaista pakettia, otetaan murto-osa, joka tรคyttรครค tรคsmรคlleen jรคljellรค olevan kapasiteetin. Tรคmรค lisรคsรครคntรถ tekee Greedy Threestรค todistettavasti optimaalisen Murtolukuisessa Repussa.

Greedy Three -pakettien valinta

Algoritmin vaiheet

0/1 haarautuvassa ja sidotussa variantissa lajiteltu yksikkรถkustannuslista ajaa hakupuuta:

  • Vaihe 1: Juurisolmu edustaa tyhjรครค reppua. Kokonaisarvo = 0. Ylรคraja = M ร— suurin yksikkรถkustannus.
  • Vaihe 2: Haaraa juuri sen mukaan, kuinka monta kopiota suurimman suhteen paketista siihen mahtuu. Laske jokaiselle lapselle uudelleen Kokonaisarvo, jรคljellรค oleva kapasiteetti M ja Ylรคraja.
  • Vaihe 3: Laajenna ensin lasta, jolla on suurin ylรคraja, toivoen lรถytรคvรคsi nopeasti vahvan ratkaisun.
  • Vaihe 4: Karsitaan kaikki solmut, joiden UpperBound ei ole parempi kuin nykyinen paras tรคydellinen ratkaisu.
  • Vaihe 5: Kun jokaista solmua joko laajennetaan tai karsitaan, nykyinen paras tรคydellinen ratkaisu on optimaalinen.

Pseudokoodi puhtaalle Fractional Knapsackin ahneelle algoritmille:

Fractional Knapsack (Array W, Array V, int M)
1. for i <- 1 to size(V)
2.     cost[i] <- V[i] / W[i]
3. Sort-Descending(cost)
4. total <- 0
5. i <- 1
6. while (i <= size(V) and M > 0)
7.     if W[i] <= M
8.         M <- M - W[i]
9.         total <- total + V[i]
10.        i <- i + 1
11.    else
12.        total <- total + V[i] * (M / W[i])
13.        M <- 0

Algoritmin monimutkaisuus:

  • Yksinkertaisen lajittelun (valinta tai kupla) kรคyttรคminen: O(n2).
  • Pikalajittelun tai yhdistรคmislajittelun kรคyttรถ: O(n log n), lajitteluaskeleen mรครคrรครคmรค.

Java Code ahneelle kolmelle

Mรครคrittele KnapsackPackage luokka painolla, arvolla ja johdetuilla kustannuksilla (lajittelussa kรคytetty V/W-suhde):

public class KnapsackPackage {

    private double weight;
    private double value;
    private Double cost;

    public KnapsackPackage(double weight, double value) {
        super();
        this.weight = weight;
        this.value = value;
        this.cost = Double.valueOf(value / weight);
    }

    public double getWeight() { return weight; }

    public double getValue() { return value; }

    public Double getCost()  { return cost; }
}

Luo sitten funktio, joka toteuttaa Greedy Threen:

public void knapsackGreProc(int W[], int V[], int M, int n) {
    KnapsackPackage[] packs = new KnapsackPackage[n];
    for (int i = 0; i < n; i++) {
        packs[i] = new KnapsackPackage(W[i], V[i]);
    }

    Arrays.sort(packs, new Comparator<KnapsackPackage>() {
        @Override
        public int compare(KnapsackPackage a, KnapsackPackage b) {
            return b.getCost().compareTo(a.getCost());
        }
    });

    double remain = M;
    double result = 0d;

    for (int i = 0; i < n && remain > 0; i++) {
        if (packs[i].getWeight() <= remain) {
            remain -= packs[i].getWeight();
            result += packs[i].getValue();
            System.out.println("Pack " + i + " - Weight " + packs[i].getWeight()
                             + " - Value " + packs[i].getValue());
        } else {
            double fraction = remain / packs[i].getWeight();
            result += packs[i].getValue() * fraction;
            System.out.println("Pack " + i + " - Fraction " + fraction
                             + " - Value " + packs[i].getValue() * fraction);
            remain = 0;
        }
    }

    System.out.println("Max Value:\t" + result);
}

Funktio knapsackGreProc() in Java

Funktio knapsackGreProc() in Java

Koodin selitys:

  1. Kรครคri jokainen syรถte sisรครคn KnapsackPackage joten lajitteluavain (V/W-suhde) on laskettu etukรคteen.
  2. Lajittele kustannusten mukaan laskevaan jรคrjestykseen.
  3. Ota jokainen paketti kokonaisena, jos se sopii.
  4. Ota murto-osa seuraavasta pakkauksesta tรคyttรครคksesi jรคljellรค olevan kapasiteetin.
  5. Pysรคytรค heti, kun jรคljellรค oleva kapasiteetti on nolla.

Korjaa huomautus: Alkuperรคisen Java silmukka edistynyt i vain silloin, kun paketti ei mahtunut, mikรค aiheutti saman paketin ottamisen toistuvasti. Yllรค oleva versio siirtรครค yhden paketin iteraatiota kohden ja lisรครค osittaistรคyttรถvaiheen, joka vastaa todellista osittaisreppusรครคntรถรค.

Java ajuri, joka suorittaa algoritmin toimivassa esimerkissรค:

public void run() {
    int W[] = new int[]{15, 10, 2, 4};
    int V[] = new int[]{30, 25, 2, 6};
    int M = 37;
    int n = V.length;
    knapsackGreProc(W, V, M, n);
}

Python3 Code ahneelle kolmelle

Mรครคrittele ensin KnapsackPackage luokassa. __lt__ menetelmรค tekee siitรค suoraan lajiteltavan hinnan mukaan:

class KnapsackPackage(object):
    """Knapsack Package Data Class"""

    def __init__(self, weight, value):
        self.weight = weight
        self.value  = value
        self.cost   = value / weight

    def __lt__(self, other):
        return self.cost < other.cost

Toteuta sitten murtolukuinen reppurutiini:

class FractionalKnapsack(object):

    def knapsackGreProc(self, W, V, M, n):
        packs = [KnapsackPackage(W[i], V[i]) for i in range(n)]
        packs.sort(reverse=True)

        remain = M
        result = 0

        for i in range(n):
            if remain == 0:
                break
            if packs[i].weight <= remain:
                remain -= packs[i].weight
                result += packs[i].value
                print("Pack", i, "- Weight", packs[i].weight, "- Value", packs[i].value)
            else:
                fraction = remain / packs[i].weight
                result += packs[i].value * fraction
                print("Pack", i, "- Fraction", fraction,
                      "- Value", packs[i].value * fraction)
                remain = 0

        print("Max Value:", result)

Funktio knapsackGreProc() in Python

Funktio knapsackGreProc() in Python

Korjaa huomautus: Alkuperรคisen Python luokka mรครคritteli tyhjรคn __init__ ilman kehoa, joka nostaa IndentationErrorYllรค oleva versio poistaa tyhjรคn konstruktorin, koska sitรค ei tarvita.

Ensimmรคisessรค esimerkissรค algoritmia suorittava ajuri:

if __name__ == "__main__":
    W = [15, 10, 2, 4]
    V = [30, 25, 2, 6]
    M = 37
    n = 4

    proc = FractionalKnapsack()
    proc.knapsackGreProc(W, V, M, n)

C# Code ahneelle kolmelle

Mรครคrittele KnapsackPackage luokka:

using System;

namespace KnapsackProblem
{
    public class KnapsackPackage
    {
        private double weight;
        private double value;
        private double cost;

        public KnapsackPackage(double weight, double value)
        {
            this.weight = weight;
            this.value  = value;
            this.cost   = value / weight;
        }

        public double Weight { get { return weight; } }
        public double Value  { get { return value; } }
        public double Cost   { get { return cost; } }
    }
}

Toteuta Greedy Three murtolukutรคyttรถaskelella:

public void KnapsackGreProc(int[] W, int[] V, int M, int n)
{
    KnapsackPackage[] packs = new KnapsackPackage[n];
    for (int k = 0; k < n; k++)
        packs[k] = new KnapsackPackage(W[k], V[k]);

    Array.Sort<KnapsackPackage>(packs,
        (a, b) => b.Cost.CompareTo(a.Cost));

    double remain = M;
    double result = 0d;

    for (int i = 0; i < n && remain > 0; i++)
    {
        if (packs[i].Weight <= remain)
        {
            remain -= packs[i].Weight;
            result += packs[i].Value;
            Console.WriteLine("Pack " + i + " - Weight " + packs[i].Weight
                            + " - Value " + packs[i].Value);
        }
        else
        {
            double fraction = remain / packs[i].Weight;
            result += packs[i].Value * fraction;
            Console.WriteLine("Pack " + i + " - Fraction " + fraction
                            + " - Value " + packs[i].Value * fraction);
            remain = 0;
        }
    }

    Console.WriteLine("Max Value:\t" + result);
}

Funktio KnapsackGreProc() C#:ssa

Funktio KnapsackGreProc() C#:ssa

Vastaesimerkki: Ahne kolmonen 0/1-repulla

Ahne Kolmonen on optimaalinen Murtolukumuunnelmaan, mutta 0/1 Repussa (jossa esineitรค ei voi jakaa) se voidaan voittaa. Vastaesimerkki:

  • Parametrit: n = 3, M = 10.
  • Paketit: {i = 1; W = 7; V = 9; hinta = 9/7}, {i = 2; W = 6; V = 6; hinta = 1}, {i = 3; W = 4; V = 4; hinta = 1}.
  • Ahne Kolme valitsee paketin 1 kokonaisarvolla 9, kun taas optimaalinen 0/1-valinta (paketti 2, paketti 3) saavuttaa 10.

Opetus: kรคytรค Greedy Three -kรคskyรค vain, kun murtoluvut ovat sallittuja. 0/1-muunnelmassa kรคytรค Dynaaminen ohjelmointi sen sijaan.

Murtolukuisen repun sovellukset

  • Lastin lastaus, jossa nestemรคisiรค, jauhemaisia โ€‹โ€‹tai irtotavaroita voidaan jakaa painon mukaan.
  • Salkun allokaatio sijoitusvaihtoehtojen vรคlillรค, jotka hyvรคksyvรคt osittaisen rahoituksen.
  • Pilvipalvelun kaistanleveyden jakaminen, jossa virrat voivat kuluttaa vain murto-osan linkistรค.
  • CPU-ajoitus jaetun aikaviipaleen mallissa jaettavilla tyรถkuormilla.
  • Tekoรคlyresurssien allokointi, jossa harjoitustyรถ voi kรคyttรครค vain murto-osan GPU:sta.

UKK

Murtolukuisessa reppuongelmassa sinun on tรคytettรคvรค M:n vetoinen reppu esineillรค, jotka voidaan jakaa osiin. Jokaisella esineellรค on paino ja arvo; tavoitteena on maksimoida kokonaisarvo kapasiteettia kunnioittaen.

Lajittelu arvo-painosuhteen mukaan ja suurimman suhteen valitseminen ensin on todistetusti optimaalista, koska mikรค tahansa vaihto pienemmรคn suhteen omaavaan kohtaan pienentรครค kokonaisarvoa kapasiteettiyksikkรถรค kohti. Murtoluvut antavat viimeisen kohteen tรคyttรครค jรคljelle jรครคvรคn tilan tรคsmรคlleen.

Murtolukuisessa repussa voit ottaa palan mistรค tahansa esineestรค, ja se ratkaistaan โ€‹โ€‹ahneella arvo/paino -lajittelulla. 0/1 Reppu vaatii kokonaisia โ€‹โ€‹kohteita ja tarvitsee dynaamista ohjelmointia optimaalisen vastauksen saavuttamiseksi.

Arvo-painosuhteen mukainen lajittelu hallitsee suoritusaikaa. Nopeassa lajittelussa tai yhdistรคmislajittelussa algoritmi suoritetaan ajassa O(n log n). Valinta- tai kuplalajittelu nostaa sen arvoon O(n neliรถitynรค). Ahne valintasilmukka itsessรครคn on O(n).

Ilman murtolukuja ahne valinta voi jรคttรครค kรคyttรคmรคtรถntรค kapasiteettia, jonka fiksumpi vaihto tรคyttรคisi. Klassisessa tapauksessa (W = 7, 6, 4; V = 9, 6, 4; M = 10) valitaan arvo 9, kun taas optimaalinen 0/1-vastaus saavuttaa arvon 10.

Irtotavaroiden lastaus, portfolion allokointi, pilvikaistanleveyden jakaminen, suorittimen aikaviipaleiden ajoitus ja tekoรคlyresurssien allokointi jaettavien tyรถkuormien kesken. Mikรค tahansa tilanne, jossa nimikkeet voidaan viipaloida painon mukaan, on mahdollinen.

Vahvistusoppivat agentit pakkaavat pilvitehtรคviรค GPU- tai muistirajoitusten alle, ja koneoppimismallit ennustavat hyviรค haarautumis- ja sidontajรคrjestyksiรค. Murtolukuisessa variantissa ahneus pysyy optimaalisena, joten tekoรคly kohdistuu pรครคasiassa 0/1-tapaukseen.

Kyllรค. GitHub Copilot tukee arvo/paino-lajittelua, ahnetta silmukkaa ja murtolukutรคyttรถรค. Java, Python, tai C#:lla, ja luo yksikkรถtestejรค, jotka varmistavat, ettรค algoritmi osuu tunnettuun optimaaliseen arvoon klassisilla syรถtejoukoilla.

Tiivistรค tรคmรค viesti seuraavasti: