Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Münzproblem


klezmor - Sa 08.12.07 03:29
Titel: Münzproblem
Folgende Aufgabe: Man hat eine Menge von 6 Münzen, diese haben alle irgendwelche unterschiedlichen Werte, wobei eine Münze den Wert 1Cent hat. Nun soll man einen Algorithmus entwickeln, der die minimale Anzahl an Münzen herausfindet, welche man benötigt um einen bestimmten Wert darzustellen.
Bsp. der Wert lautet 34Cents und die Münzen: 1, 11, 12, 13, 15, 20 die minimale Anzahl wäre in diesem Fall 3 da 1*1+1*13+1*20=34( gibt vielleicht noch weiter möglichkeiten mit 3). Ich habe keine Ahnung wie man so etwas implementieren könnte. Möglicherweise rekursiv, aber ich komme nicht ganz drauf. Hat jemand eine ahnung? Es kommt hierbei nicht auf optimierung an sondern man kann alle möglichkeiten durchgehen.
Das Problem steckt also hauptsächlich in der Implementierung.

MFG Klezmor.


Gausi - Sa 08.12.07 10:33

Wenn die Münzwerte beliebig sein können, bleibt dir afaik nichts anderes übrig, als alle Möglichkeiten durchzuprobieren (was immer das hier auch genau heißen mag). Mit unserem Münzsystem (1,2,5,10,20,50) kann man geschickter vorgehen, indem man immer die größte passende Münze nimmt und dann weiter macht. Bei deinen Angaben versagt das Verfahren z.B. für den Wert 30.


delfiphan - Sa 08.12.07 14:28

Alles durchprobieren geht wohl etwa so: Du fängst bei Münze 1 an und probierst alle möglichen Anzahlen durch. Zuerst 0 Stück, dann 1 Stück, 2 Stück, usw. bis der Totalwert überschritten ist. Und für jede Anzahl für Münze 1 (0,1,2,...) probierst du alle Anzahlen für Münze 2 durch. Das geht einfach mit einer Rekursion. Der Aufwand einer solchen Berechnung ist exponential.
Dabei sollte bei deinem Beispiel sowas rauskommen:

Münzanzahlen in blau, Werte schwarz, Bestlösungen orange
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
 0x1,2x11,1x12,0x13,0x15,0x20  Total: 3
 1x1,0x11,0x12,1x13,0x15,1x20  Total: 3
 1x1,3x11,0x12,0x13,0x15,0x20  Total: 4
 2x1,0x11,1x12,0x13,0x15,1x20  Total: 4
 3x1,1x11,0x12,0x13,0x15,1x20  Total: 5
 4x1,0x11,0x12,0x13,2x15,0x20  Total: 6
 6x1,0x11,0x12,1x13,1x15,0x20  Total: 8
 7x1,0x11,1x12,0x13,1x15,0x20  Total: 9
 8x1,0x11,0x12,2x13,0x15,0x20  Total: 10
 8x1,1x11,0x12,0x13,1x15,0x20  Total: 10
 9x1,0x11,1x12,1x13,0x15,0x20  Total: 11
10x1,0x11,2x12,0x13,0x15,0x20  Total: 12
10x1,1x11,0x12,1x13,0x15,0x20  Total: 12
11x1,1x11,1x12,0x13,0x15,0x20  Total: 13
12x1,2x11,0x12,0x13,0x15,0x20  Total: 14
14x1,0x11,0x12,0x13,0x15,1x20  Total: 15
19x1,0x11,0x12,0x13,1x15,0x20  Total: 20
21x1,0x11,0x12,1x13,0x15,0x20  Total: 22
22x1,0x11,1x12,0x13,0x15,0x20  Total: 23
23x1,1x11,0x12,0x13,0x15,0x20  Total: 24
34x1,0x11,0x12,0x13,0x15,0x20  Total: 34


klezmor - Sa 08.12.07 14:50

Also nehmen wir mal an ich hab die Münzen: 1, 3, 5, 7, 11 Der Baum sähe dann für alle möglichkeiten folgendermaßen aus:
1.Schicht: 1-----------3---------5---------7-------------11
2.Schicht (1)--------(1,3)---(1,3,5)--(1,3,5,7)--(1,3,5,7,11)
3.Schicht (1)--------(1)(1,3) .... .... ....
4.Schicht (1) .....
usw.bis der Betrag 0 ist

Aber wie durchlafue ich einen solchen Baum , ich weiß einfach nciht, wie ich das rekursiv implementieren kann.
Ich muss letztendlich auch noch feststellen können wie viele Schichten ich durchlaufen bin um den betrag vollständig auf 0 gesetzt zu haben.
Bitte helft mir.



Gruß Klezmor


F4n4T!C - Sa 08.12.07 15:42

rekursion ist ein böses thema...mein thema muss ich auch rekursiv lösen, aber im endeffekt ist eine rekursion, z.b. eine prozedur die sich immer wieder selber aufruft, bist zu einem "ende"

um es mal ganz einfach zu sagen


Delphi-Quelltext
1:
2:
3:
4:
5:
procedure muenze(a,b,c,d,e,f:integer)//hier sind die variablen die zurückgegeben werden
begin
  dann kommt hier dein ganzer quellcode;
  muenze(a,b,c,d,e,f);//damit ruft sich die prozedur selbst auf...et voila du hast eine rekursion
end;


um das problem zu lösen, müsste ich mich jetzt paar tage damit beschäftigen...bis wann brauchst du das programm?


blaueled - Sa 08.12.07 18:19

Hallo,

du müsstest dein Münzen der Größe nach sortieren, und dann von oben nach unten probieren:

dein bsp:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
34 cent
münzen: 20,15,13,12,11,1

1. Vergleich
(34 - 20 (14)) > 0 --> 1. Münze ist 20, rest ist 14

2. Vergleich
(14 - 20 (-6)) > 0 --> nächste Münze
(14 - 15 (-1)) > 0 --> nächste Münze
(14 - 13 (1))  > 0 --> 2. Münze ist 13, rest ist 1

3. Vergleich
...
(1 - 1 (0))    = 0 --> 3. Münze ist 1, rest ist 0 --> Beenden


Das sollte die schnellste und am einfachsten Umzusetzende Methode sein

Blauled


Gausi - Sa 08.12.07 18:27

@blaueled: Und genau dieses "Greedy"-Verfahren funktioniert nicht immer. Nimm 30 Cent:

20 Cent - 10 Cent bleiben übrig.

15, 13, 12, 11 Cent passen nicht, also werden 10 1-Cent-Münzen genommen. Die gesuchte Lösung wäre aber 2x15 Cent gewesen. Man muss schon komplizierter ran - delfiphan hat da glaube ich das richtige Verfahren - das muss man nur in Code umsetzen ;-)


klezmor - Sa 08.12.07 18:40

Ja genau der greedy Algo, ist nur optimal für unsere Währungssysteme.
Aber so wie ich Delphifan verstanden habe, probiert er 6^x(x=rekursionstiefe) möglichkeiten durch, ich möchte die komplexität ein wenig senken in dem ich unterscheide zwischen: nimm erst eine 2er münze und dann eine 10 ner, wie in meinem baum beschrieben. Wobei sich natürlich für große x die Komplexitäten nicht groß unterscheiden, bzw. in landau notation gleich sind(da beide exponentiell).


delfiphan - Sa 08.12.07 18:49

Ob ein effizientes Lösungsverfahren existiert (für den allgemeinen Fall), welche garantiert die richtigen Lösungen findet, ist fragwürdig. Wenn nämlich alle Münzen Primzahlen sind, entspricht die Aufgabe einer Primfaktorenzerlegung. Wenn du hier einen effizienten Algorithmus findest, bist du Millionär ;). (Edit: Die Fragestellung bei der Primfaktorenzerlegung ist anders, da kennt man normalerweise die Faktoren nicht schon am Anfang und die Zerlegung ist eindeutig)

user profile iconklezmor hat folgendes geschrieben:
nimm erst eine 2er münze und dann eine 10 ner
Wenn du alle Möglichkeiten durchprobierst, ist die Reihenfolge der Münzen egal.

user profile iconklezmor hat folgendes geschrieben:
Aber so wie ich Delphifan verstanden habe, probiert er 6^x(x=rekursionstiefe) möglichkeiten durch
Nein, die Rekursionstiefe ist 6 und es werden 829 Möglichkeiten durchprobiert, nämlich alle Möglichkeiten mit der Summe kleinergleich dem Totalwert.
Wenn es eine Münze gibt, die eindeutig mit minimaler Anzahl durch andere Münzen zusammengesetzt werden kann, kann man die wegoptimieren (das ist bei allen Währungen hoffentlich bei allen Münzen der Fall ;) )

user profile iconGausi hat folgendes geschrieben:
Man muss schon komplizierter ran - delfiphan hat da glaube ich das richtige Verfahren - das muss man nur in Code umsetzen ;-)
Die Rekursionsfunktion ist relativ kurz, 10 Zeilen ;)


>M@steR< - Sa 08.12.07 22:59

Gelöscht


>M@steR< - Sa 08.12.07 23:36

Gelöscht


delfiphan - So 09.12.07 00:21

user profile icon>M@steR< hat folgendes geschrieben:
ich habe den algo jetzt als quellcode und er funzt perfekt!

Und was ist mit 23x1 + 1x11? Gibt auch 34 ;) Es gibt übrigens zwei minimale Lösungen, dein Algorithmus findet nur die eine.

Übrigens weiss ich nicht, ob es sinnvoll ist, wenn du deinen Quelltext hier reinpostest...


>M@steR< - So 09.12.07 00:24

Gelöscht


delfiphan - So 09.12.07 00:31

Du hast keine Garantie, dass du die beste Lösung findest.


>M@steR< - So 09.12.07 00:32

Gelöscht


delfiphan - So 09.12.07 00:32

Erst bestes Beispiel schlägt schon fehl: 1*13 + 3*15 + 1*20 = 78 (5 Münzen)
Deine Minimallösung: 6*13 = 78 (6 Münzen)


>M@steR< - So 09.12.07 00:37

Gelöscht


klezmor - So 09.12.07 15:26

user profile icondelfiphan hat folgendes geschrieben:
Erst bestes Beispiel schlägt schon fehl: 1*13 + 3*15 + 1*20 = 78 (5 Münzen)
Deine Minimallösung: 6*13 = 78 (6 Münzen)


Delphifan hat Recht, die Lösung bringt wirklich nicht immer die minimale Anzhahl der Münzen heraus. Aber um was es mir eigentlich ging, ist dass, der baum welchen ich oben hingezeichnet habe weniger kombinationen enthält wie die lösung von delphifan und somit, wenn auch nciht viel, aber dennoch ein wenig schneller zu berechnen wäre.

Oder täusche ich mich da, ganz sicher bin ich mir auch net?


BenBE - So 09.12.07 16:22

Ist wieder mal ein typisches Rucksack-Problem:

Sieht in PHAssemPler (hingeschmiert) so aus:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
<?php

$coins = array(1,11,12,13,15,20);

$value = 78;

$minsolution = array();

$stack = array(array());

while(count($stack)) {
    $tmp = array_shift($stack);

    $tmpsum = array_sum($tmp);

    if ($tmpsum == $value) {
        $minsolution = $tmp;
        break;
    }

    if ($tmpsum > $value) {
        continue;
    }

    foreach($coins as $newcoin) {
        $newtmp = $tmp;
        $newtmp[] = $newcoin;
        $stack[] = $newtmp;
    }
}

var_dump($minsolution);
echo array_sum($minsolution);

?>


Ist ne typische Breitensuche ... Geht aber noch stark zu optimieren ...


delfiphan - So 09.12.07 18:08

@typisches Rucksackproblem: Beim Rucksackproblem ist die Anzahl Gegenstände egal. Es geht dort nicht um die Minimierung der Anzahl sondern Maximierung des Nutzens. Ausserdem soll dort das Gewicht kleinergleich einem Maximalwert sein, hier muss der Totalbetrag genau stimmen. Und man kann einen Gegenstand genau null oder einmal in den Rucksack einpacken, ich darf/muss aber mehrere Stücke einer Münzart nehmen.


BenBE - So 09.12.07 19:02

Allen NP-vollständigen Problemen ist gemeinsam, dass sie NP-vollständig sind; daher heißen sie ja so ...
Die Unterschiede sind marginal, da in polynomialer Zeit aufzählbar ;-)

Scherz beiseite.

@delfiphan: Bitte unterscheide zwischen allgemeinem und speziellen Rucksack-Problem; beim Allgemeinen durfte man nämlich Gegenstände auch mehrfach nehmen.


delfiphan - So 09.12.07 20:27

Ich habe den selben Algorithmus, jedoch als Tiefensuche implementiert.

Der Nachteil dabei ist, dass ich bei einer Lösung nicht sofort sagen kann, dass sie minimal ist. Auf der anderen Seite verbraucht der Algorithmus von BenBE mit der Laufzeit exponentiell viel Speicher; bei mir bleibt er konstant. Dafür kann BenBE die Berechnung abbrechen, sobald er eine Lösung hat - da die erste automatisch minimal ist. Es ist aber nicht so, dass mein Algorithmus alle Lösungen wirklich durchprobieren muss, sondern man kann da einiges weglassen. Bei der Zahl 78 wird insgesamt 463x die Summe auf den Totalwert geprüft, bei BenBE sind es 3463. Bestimmt lassen sich aber beide Varianten noch optimieren ;)


BenBE - Mo 10.12.07 00:22

Bei mir kann allein mit der Reihenfolge der Münzen in der Eingabe schon der Suchvorgang stark optimiert werden, da damit die Summe schneller anwächst. Eine Optimierung bei mir wäre z.B. die Summe direkt im Schleifendurcklauf mitzuberechnen für den nächsten Schritt; dann wäre das Berechnen der Summe nämlich in O(1) statt O(n) möglich.


DrRzf - Di 11.12.07 00:31

Wie wärs mit einer Suchtiefenbegrenzung wenn amn alle durchtestet.
Zuerst sucht der algo bis er die erste lösung gefunden hat, die zb aus 8 münzen besteht.
alle weiteren suchen führt er nur bis zu einer suchtiefe von 7 münzen durch.
findet er eine weitere mit 5 münzen, wird alles über einer suchtiefe von 4 ignoriert.
alternativ könnte man auch bis zur suchtiefe der besten gefundnenen konstellation gehn um
mehrere treffer mit gleicher anzahl auszugeben.