Registrierung Kalender Mitgliederliste Teammitglieder Suche Häufig gestellte Fragen Zur Startseite

Informatiker Board » Themengebiete » Praktische Informatik » Algorithmen » Weintrinker Problem » Hallo Gast [Anmelden|Registrieren]
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | An Freund senden | Thema zu Favoriten hinzufügen
Neues Thema erstellen Antwort erstellen
Zum Ende der Seite springen Weintrinker Problem
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
SilverG6 SilverG6 ist männlich
Grünschnabel


Dabei seit: 10.09.2014
Beiträge: 3
Herkunft: Jena

Weintrinker Problem Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Meine Frage:
Die Aufgabe :
Es treffen sich 2 Wanderer. Der Erste besitzt einen Krug mit 8 Liter Wein Inhalt, dass Gesamtvolumen dieses Krugs ist ebenfalls 8 Liter. Der Zweite besitzt 2 Krüge mit jeweils 5 und 3 Liter Füllvolumen, jedoch besitzt er noch keinen Wein. Der erstere möchte mit dem 2. so teilen das sie beide gleich viel Wein besitzen.

Die beiden besitzen leider keine Hilfsmittel zum abmessen. Das bedeutet, dass sie nur die Krüge komplett umkippen können bis einer leer oder der zu befüllende Voll ist.

Ich soll dazu nun ein Programm schreiben welches auch für allgemein gültige Fälle die schnellste Lösung angibt.

Meine Ideen:
Ich brauche keine Hilfe beim programmieren, an was es mir fehlt ist ein richtiger Lösungsansatz. Irgendwie scheint es mir was mit Differenzen zusammenzuhängen. Ich hab schon ziemlich lange daran gesessen und weiß das dies nur ein dürftiger eigener Ansatz ist.
Meine Idee ist nun, dass man immer die Differenz zwischen den beiden Wanderern betrachtet und immer so umfüllt man beim umfüllen die Differenz entweder verringert oder halt die kleinst mögliche Erhöhung entsteht.
Bsp. Person A besitzt 2 Krüge mit den Volumen 10(a) und 8(b) Liter.
Krug a ist mit 10 Liter Wein gefüllt.
Krug b ist mit 4 Liter Wein gefüllt.
Person B besitzt auch 2 Krüge mit den Volumen 11(c) und 7(d) Liter.
Krug c ist mit 4 Liter Wein gefüllt.
Krug d ist mit 6 Liter Wein gefüllt.

Die Differenz beträgt 4 Liter
Person A besitzt 14 Liter und Person B 10 also muss Person A abgeben
Möglichkeiten des umfüllens :
a --> c = 7 Liter Austausch
a --> d = 1 Liter Austausch
b --> c = 4 Liter Austausch
b --> d = 1 Liter Austausch

Man muss aber auch nur 2 Liter ausgleichen, da man sonst ja die Differenz einfach halten würde also muss immer die hälfte der Differenz ausgeglichen werden.

also würde ich jetzt einfach a --> d füllen
das würde ich dann einfach in eine Schleife mit Abbruchbedingung stecken und fertig \(^.^)/ /(._.)\

Ich danke schonmal für Hilfe !
mit freundlichen Grüßen Franz
10.09.2014 08:46 SilverG6 ist offline E-Mail an SilverG6 senden Beiträge von SilverG6 suchen Nehmen Sie SilverG6 in Ihre Freundesliste auf
SilverG6 SilverG6 ist männlich
Grünschnabel


Dabei seit: 10.09.2014
Beiträge: 3
Herkunft: Jena

RE: Weintrinker Problem Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Was mir und einem Freund noch aufgefallen ist. Es muss bei jeder Differenzveränderung zwischen den Weinmengen der Wanderer ein internes umschütten erfolgen bevor sie wieder gegenseitig umgießen.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von SilverG6: 10.09.2014 16:05.

10.09.2014 16:03 SilverG6 ist offline E-Mail an SilverG6 senden Beiträge von SilverG6 suchen Nehmen Sie SilverG6 in Ihre Freundesliste auf
Karlito Karlito ist männlich
Kaiser


Dabei seit: 11.04.2011
Beiträge: 1.461

Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Hallo,

ich würde die Differenzen außer Acht lassen und einfach nur eine Breitensuche implementieren mit der Abbruchbedingung, dass es keine weiteren Möglichkeiten mehr gibt oder die Differenz null ist. Das Problem ist nämlich, dass die Differenz zwischenzeitlich wieder höher werden kann.

Für die Breitensuche würde ich einfach eine Warteschlange nehmen und von jeder Konfiguration jede Folgekonfiguration in die Warteschlange einfügen (jede Umfüllmöglichkeit). Dabei muss man sich natürlich zu jedem Knoten im Suchbaum den Pfad merken. Weiterhin braucht es eine Liste, in welcher alle schon bekannten Konfigurationen gespeichert sind um doppelte Expansion des Knotens mit einer bekannten Konfiguration zu vermeiden und zu garantieren, dass das Programm terminiert, falls es keine Lösung gibt.

Gruß,

Karlito
11.09.2014 16:21 Karlito ist offline E-Mail an Karlito senden Beiträge von Karlito suchen Nehmen Sie Karlito in Ihre Freundesliste auf
SilverG6 SilverG6 ist männlich
Grünschnabel


Dabei seit: 10.09.2014
Beiträge: 3
Herkunft: Jena

Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Also meinst du ich sollte es mit Backtracking und probieren versuchen ?
Die Abbruchbedingung wäre dann also wenn eine gleiche Verteilung wie irgendwann vorher schon auftritt ?

Ja das ist uns auch schon aufgefallen das war etwas verwirrend ^^

Aber soll ich das ganze dann einfach per Zufall machen ?
Ich hätte eigentlich gerne einen Algorithmus ._. großes Grinsen So wie bei einem Labyrinth das ich irgendwie definieren kann das man gegen keine Wände läuft ^^

Aber ich glaube ich weiß ungefähr was du meinst smile
ich versuche das mal umzusetzen !
DANKE <3

Btw bleibt das Forum hier so falls ich nachträglich dann noch fragen hätte ?

mfg franz
11.09.2014 18:24 SilverG6 ist offline E-Mail an SilverG6 senden Beiträge von SilverG6 suchen Nehmen Sie SilverG6 in Ihre Freundesliste auf
eulerscheZahl eulerscheZahl ist männlich
Foren Gott


Dabei seit: 04.01.2013
Beiträge: 2.859

Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Du fängst mit einem Umfüllvorgang an, speicherst das Ergebnis, kehrst zur Lage von vor dem Umfüllen zurück und versuchst einen anderen Zug, bis du für eine Stellung alle möglichen Züge getestet hast.
Diese Stellungen haben ermöglichen wiederum Züge, die du abarbeiten musst.
Eine Möglichkeit ist Rekursion: du entscheidest dich am Anfang für einen Pfad und klapperst den komplett ab. Dann machst du das selbe für einen anderen Anfangszug.
Alternativ (das dürfte effizienter sein): Du speicherst alle mit einem Zug erreichbaren Ergebnisse. Für jedes von diesen machst du einen weiteren Zug und speicherst das dann wieder (wenn du diese Stellung nicht früher schon erreicht hattest). Von den Ergebnissen aus Runde 2 kannst du die für den 3. Zug ermitteln. Zusätzlich solltest du für jeden Zug noch speichern, wo du hergekommen bist, damit du nicht nur sagen kannst, wie viele Züge nötig sind, sondern auch welche.

Und hier wird kein Thread geschlossen, solange es kein Spam ist. Kannst dich gerne auch nach ein paar Wochen wieder melden.

__________________
Syntax Highlighting fürs Board (Link)
11.09.2014 21:51 eulerscheZahl ist offline Beiträge von eulerscheZahl suchen Nehmen Sie eulerscheZahl in Ihre Freundesliste auf
Karlito Karlito ist männlich
Kaiser


Dabei seit: 11.04.2011
Beiträge: 1.461

Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Hallo,

Zitat:
Original von SilverG6
Also meinst du ich sollte es mit Backtracking und probieren versuchen ?

Ja.

Zitat:
Original von SilverG6
Die Abbruchbedingung wäre dann also wenn eine gleiche Verteilung wie irgendwann vorher schon auftritt ?


Nein. Du hast 2 Gruppen an Krügen in der allgemeinen Variante, oder? Ziel ist einfach, dass beide Gruppen die gleiche Menge Wein haben. D.h. die Differenz der Menge Wein zwischen beiden Gruppen ist 0.

Zitat:
Original von SilverG6
Aber soll ich das ganze dann einfach per Zufall machen ?
Ich hätte eigentlich gerne einen Algorithmus ._. großes Grinsen So wie bei einem Labyrinth das ich irgendwie definieren kann das man gegen keine Wände läuft ^^


Natürlich nicht per Zufall. Du kannst doch Systematisch vorgehen. Du hast einen Knoten im Suchbaum. Dann wählst du einen Krug aus und schüttest der Reihe nach in alle anderen Krüge um. Das wiederholst du für jeden nicht leeren Krug.
Was den Algorithmus angeht: da wüsste ich gerade keinen Weg. Schau dir mal A* an. Die Schwierigkeit dabei ist sicher eine gute Heuristik zu finden.

Zitat:
Original von SilverG6
Btw bleibt das Forum hier so falls ich nachträglich dann noch fragen hätte ?


Der Link bleibt gleich, die Rangfolge auf der "Portalseite" ändert sich sobald ein neuer Beitrag verfasst wird. Im Forum selbst kannst Du den Beitrag jedoch immer wieder finden und weitere Fragen anhängen.

Gruß,

Karlito
11.09.2014 22:15 Karlito ist offline E-Mail an Karlito senden Beiträge von Karlito suchen Nehmen Sie Karlito in Ihre Freundesliste auf
Karlito Karlito ist männlich
Kaiser


Dabei seit: 11.04.2011
Beiträge: 1.461

Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Hallo euler,

Zitat:
Original von eulerscheZahl
Eine Möglichkeit ist Rekursion: du entscheidest dich am Anfang für einen Pfad und klapperst den komplett ab. Dann machst du das selbe für einen anderen Anfangszug.


Eine Rekursion ist hier ungeeignet ist, da man mit Rekursion üblicherweise eine Tiefensuche realisiert. Laut Aufgabenstellung ist jedoch die schnellste Lösung gefragt, auf welche man nur stößt, wenn man alle Lösungen der Tiefensuche vergleicht oder Breitensuche verwendet und die erste Lösung wiedergibt. Würde bedeuten, dass man bei der Rekursion den gesamten Suchbaum ablaufen muss um dann alle Lösungen zu vergleichen (unoptimiert). Oder übersehe ich etwas?

Gruß,

Karlito
11.09.2014 22:20 Karlito ist offline E-Mail an Karlito senden Beiträge von Karlito suchen Nehmen Sie Karlito in Ihre Freundesliste auf
eulerscheZahl eulerscheZahl ist männlich
Foren Gott


Dabei seit: 04.01.2013
Beiträge: 2.859

Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Deshalb
Zitat:
Alternativ (das dürfte effizienter sein): ...

Man könnte das Ganze etwas optimieren, wenn man zu jeden Schritt die Anzahl der benötigten Züge und die Folgezustände speichert (um die Zahl der nötigen Züge dann für den gesamten Baum zu verringern). Aber wie gesagt: Breitensuche ist effizienter.
Vielleicht habe ich morgen etwas langeweile und schreibe ein paar Zeilen, mal sehen.

__________________
Syntax Highlighting fürs Board (Link)
12.09.2014 05:41 eulerscheZahl ist offline Beiträge von eulerscheZahl suchen Nehmen Sie eulerscheZahl in Ihre Freundesliste auf
eulerscheZahl eulerscheZahl ist männlich
Foren Gott


Dabei seit: 04.01.2013
Beiträge: 2.859

Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Entweder habe ich Unsinn getrieben, oder dein zweites Beispiel ist nicht lösbar.
Für das erste erhalte ich: jeweils Füllstand(maximal)
code:
1:
2:
3:
4:
5:
6:
7:
8:
8(8) 0(5) 0(3) 
3(8) 5(5) 0(3) 
3(8) 2(5) 3(3) 
6(8) 2(5) 0(3) 
6(8) 0(5) 2(3) 
1(8) 5(5) 2(3) 
1(8) 4(5) 3(3) 
4(8) 4(5) 0(3)


hier mein Code (C#, getestet mit MonoDevelop):
code:
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:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
using System;
using System.Collections.Generic;

namespace Weintrinker
{
	class MainClass
	{
		private static int encode (int[] curr, int[] max)
		{
			int result = 0;
			for (int i = 0; i < curr.Length - 1; i++) { //no need to add last item, as sum = const
				result *= max [i] + 1;
				result += curr [i];
			}
			return result;
		}

		private static int[] decode (int curr, int[] max, int sum)
		{
			int[] result = new int[max.Length];
			int sum_tmp = 0;
			for (int i = max.Length - 2; i >= 0; i--) {
				result [i] = curr % (max [i] + 1);
				sum_tmp += result [i];
				curr /= (max [i] + 1);
			}
			result [result.Length - 1] = sum - sum_tmp;
			return result;
		}

		private static List<int> getFollowing (int state, int[] max, int sum, List<int[]> index, out bool success)
		{
			List<int> result = new List<int> ();
			success = false;
			int[] curr = decode (state, max, sum);
			for (int source = 0; source < curr.Length; source++) {
				if (curr [source] == 0)
					continue;
				for (int dest = 0; dest < curr.Length; dest++) {
					if (curr [dest] == max [dest] || dest == source)
						continue;
					int fill = Math.Min (curr [source], max [dest] - curr [dest]);
					int[] nextState = (int[])curr.Clone ();
					nextState [source] -= fill;
					nextState [dest] += fill;
					result.Add (encode (nextState, max));
					//test if solution found
					int sumFirst = 0;
					foreach (int i in index[0])
						sumFirst += nextState [i];
					bool allSumsEqual = true;
					for (int i = 1; i < index.Count && allSumsEqual; i++) {
						int sumCurrent = 0;
						foreach (int j in index[i])
							sumCurrent += nextState [j];
						if (sumCurrent != sumFirst)
							allSumsEqual = false;
					}
					if (allSumsEqual) {
						success = true;
						return result; //solution is the last item in the list
					}
				}
			}
			return result;
		}

		private static string Solve (List<int[]> listMax, List<int[]> listCurrent)
		{
			int n = 0;
			int sum = 0;
			foreach (int[] personMax in listMax)
				n += personMax.Length;
			int[] max = new int[n];
			int[] curr = new int[n];
			List<int[]> index = new List<int[]> ();
			int currIndex = 0;
			for (int i = 0; i < listMax.Count; i++) {
				index.Add (new int[listMax [i].Length]);
				for (int j = 0; j < listMax[i].Length; j++) {
					max [currIndex] = listMax [i] [j];
					curr [currIndex] = listCurrent [i] [j];
					sum += curr [currIndex];
					index [i] [j] = currIndex++;
				}
			}
			//states[steps to reach state] < currentState, previousState>
			List<Dictionary<int, int>> states = new List<Dictionary<int, int>> ();
			states.Add (new Dictionary<int, int> ());
			states [0].Add (encode (curr, max), 0);
			bool success = false;
			int[] path;
			for (int moves = 1; states[moves-1].Count > 0; moves++) {
				states.Add (new Dictionary<int, int> ());
				foreach (KeyValuePair<int, int> entry in states[moves - 1]) {
					List<int> followingStates = getFollowing (entry.Key, max, sum, index, out success);
					foreach (int next in followingStates) {
						bool add = true;
						for (int j = 0; j <= moves; j++)
							if (states [j].ContainsKey (next)) {
								add = false;
								continue;
							}
						if (add) 
							states [moves].Add (next, entry.Key);
					}
					if (success) {
						path = new int[moves + 1];
						path [path.Length - 1] = followingStates [followingStates.Count - 1];
						goto solutionFound;
					}
				}
			}
			return "no solution found\n";
			solutionFound:
			for (int i = path.Length - 2; i >= 0; i--)
				path [i] = states [i + 1] [path [i + 1]];
			string solution = "";
			for (int i = 0; i < path.Length; i++) {
				int[] vals = decode (path [i], max, sum);
				for (int j= 0; j < vals.Length; j++)
					solution += vals [j] + "(" + max [j] + ") ";
				solution += "\n";
			}
			return solution;
		}

		public static void Main (string[] args)
		{
			List<int[]> max = new List<int[]> ();
			List<int[]> curr = new List<int[]> ();
			max.Add (new int[] { 8 });
			max.Add (new int[] { 5, 3 });
			curr.Add (new int[] { 8 });
			curr.Add (new int[] { 0, 0 });
			Console.Write (Solve (max, curr));
			max.Clear (); curr.Clear ();
			max.Add (new int[] { 10, 8 });
			max.Add (new int[] { 11, 7 });
			curr.Add (new int[] { 10, 4 });
			curr.Add (new int[] { 4, 6 });
			Console.Write (Solve (max, curr));
		}
	}
}


__________________
Syntax Highlighting fürs Board (Link)
13.09.2014 08:07 eulerscheZahl ist offline Beiträge von eulerscheZahl suchen Nehmen Sie eulerscheZahl in Ihre Freundesliste auf
Karlito Karlito ist männlich
Kaiser


Dabei seit: 11.04.2011
Beiträge: 1.461

Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Nice work! Daumen hoch

So. Und jetzt noch mal in C, Haskell und Prolog großes Grinsen

Gruß,

Karlito
13.09.2014 14:47 Karlito ist offline E-Mail an Karlito senden Beiträge von Karlito suchen Nehmen Sie Karlito in Ihre Freundesliste auf
eulerscheZahl eulerscheZahl ist männlich
Foren Gott


Dabei seit: 04.01.2013
Beiträge: 2.859

Auf diesen Beitrag antworten Zitatantwort auf diesen Beitrag erstellen Diesen Beitrag editieren/löschen Diesen Beitrag einem Moderator melden       Zum Anfang der Seite springen

Aber nur, solange [latex]\prod\limits_{i=1}^{Anzahl_{Eimer}-1}{(1+v_{max,i)}} \leq int_{max}[/latex]. Dann kann ich das int mit der Funktion decode nicht zurück in int[] umrechnen, würde wohl ohnehin Performaceprobleme geben. Immerhin wird mein Code auch mit mehr als zwei Eimern pro Person und mit mehr als zwei Personen fertig.

__________________
Syntax Highlighting fürs Board (Link)
13.09.2014 15:04 eulerscheZahl ist offline Beiträge von eulerscheZahl suchen Nehmen Sie eulerscheZahl in Ihre Freundesliste auf
Baumstruktur | Brettstruktur
Gehe zu:
Neues Thema erstellen Antwort erstellen
Informatiker Board » Themengebiete » Praktische Informatik » Algorithmen » Weintrinker Problem