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

Informatiker Board » Themengebiete » Praktische Informatik » Algorithmen » Weintrinker Problem » Antwort erstellen » Hallo Gast [Anmelden|Registrieren]

Antwort erstellen
Benutzername: (du bist nicht eingeloggt!)
Thema:
Nachricht:

HTML ist nicht erlaubt
BBCode ist erlaubt
Smilies sind erlaubt
Bilder sind erlaubt

Smilies: 21 von 33
smileWinkDaumen hoch
verwirrtAugenzwinkerngeschockt
Mit ZungeGottunglücklich
Forum Kloppebösegroßes Grinsen
TanzentraurigProst
TeufelSpamWillkommen
LehrerLOL HammerZunge raus
Hilfe 
aktuellen Tag schließen
alle Tags schließen
fettgedruckter Textkursiver Textunterstrichener Text zentrierter Text Hyperlink einfügenE-Mail-Adresse einfügenBild einfügen Zitat einfügenListe erstellen CODE einfügenPHP CODE farbig hervorheben
Spamschutz:
Text aus Bild eingeben
Spamschutz

Die letzten 10 Beiträge
eulerscheZahl

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.
Karlito

Nice work! Daumen hoch

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

Gruß,

Karlito
eulerscheZahl

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));
		}
	}
}
eulerscheZahl

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.
Karlito

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
Karlito

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
eulerscheZahl

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.
SilverG6

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
Karlito

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
SilverG6 RE: Weintrinker Problem

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.
Es sind weitere Beiträge zu diesem Thema vorhanden. Klicken Sie hier, um sich alle Beiträge anzusehen.