Gefakte Münze finden

Anforderungen ändern sich. Zudem ist es immer gut zu Wissen wo die Grenzen des Programms liegen. Niemand sieht gerne eine OutofMemoryException.

Nicht der Rechner, muß es wissen, sondern die Waage.
Ich sehe es nur als eine gute Praxis, den User möglichst früh aus einem Programm zu abstrahieren. Gerade als Anfänger, wenn man sehr sehr viel „Debuggt“ und von Automatischem Testen noch nichts gehört hat, ist es doch sehr praktisch etwas zu haben das automatisch funktioniert.

import java.util.Scanner;

public class ManualScale implements Scale {

    private final Scanner scanner = new Scanner(System.in);

    @Override
    public Result weigh(int minA, int maxA, int minB, int maxB, boolean rest) {
        System.out.printf("Which set is heavier? 
1:[%d - %d] 
2:[%d - %d]%s%n",minA,maxA, minB,maxB, (rest)? "
3:they are equal":"");
        int answer = scanner.nextInt();
        switch (answer){
            case 1: return Result.A_HEAVIER;
            case 2: return Result.B_HEAVIER;
            case 3: return Result.EQUAL;
        }
        return Result.ERROR;
    }
}

Interface rausziehen und schon hat man eine Manuelle Waage. Das schafft man in unter 3 Minuten.

Dann solltest du dir mal die Sourcen dazu ansehen.
subList liefert dir keine neue Liste, sondern mehr oder minder nur einen Pointer auf einen Teil der original Liste. Hat wohl Performancegründe.
Wenn du jetzt allerdings an der originalliste etwas änderst, dann passt der Pointer nicht mehr. Das ändern wird durch das remove verursacht.
Und sobald auf die SubList zugegriffen wird, gibt es die Exception. Das solltest du mal nachschlagen.

Jetzt hast du mal einen anderen Objektorientierten Ansatz.

[QUOTE=Majora]Dann solltest du dir mal die Sourcen dazu ansehen.
subList liefert dir keine neue Liste, sondern mehr oder minder nur einen Pointer auf einen Teil der original Liste. Hat wohl Performancegründe.
Wenn du jetzt allerdings an der originalliste etwas änderst, dann passt der Pointer nicht mehr. Das ändern wird durch das remove verursacht.
Und sobald auf die SubList zugegriffen wird, gibt es die Exception.[/QUOTE]Wieder was gelernt.
Danke.

bye
TT

Nur um es mal so in den Raum zu werfen… Wenn einem die ganzen Münzen zu viele „Miniobjekte“ sind, dann bietet sich doch das Flyweight-Pattern an.
Die Nummer der Münze sowie das Gewicht werden dann extern verwaltet → statt Millionen von Klassen nur noch 2 Klassen (Factory und „leere“ Münze).

Edit: naja… ich hatte mir den Ansatz mit den „vielen Münzen“ nicht so genau angesehen. Das sind ja nur ein Haufen Integers die da erzeugt werden. Diese mickrige Statusinformation rechtfertigt wohl kein Flyweight :wink:

Okay, erstmal vielen Dank für die vielen Lösungsansätze, ihr habt mir echt weitergeholfen :slight_smile:
Zu den objektorientierten Ansätzen muss ich euch leider mitteilen, dass ich selber noch am Lernen bin und noch nicht objektorientiert programmiert hab :o Ich werd mir die Ansätze aber nochmal angucken, sobald ich mehr kann :wink:

Ich hatte mich jetzt mal an Spacerats Lösung orientiert und bei mir sieht’s wie folgt aus (entschuldigt bitte die zu ausführlichen Kommentierungen, die haben aber ihren Grund xD):


	
	public static void main(String[] args) {
		
		/* Niedrigste Münze */
		int min = 1;
		
		/* Höchste Münze */
		int max = 1000;
		
		/* Mittelwert, linkes und rechtes Minimum und Maximum */
		int mid, lMin, lMax, rMin, rMax;
		
		/* Abfragevariable */
		int a;
		
		/* Variable für die Ausgabe der Lösung */
		int loesung = 0;
		
		/* Boolescher Wert, ob die falsche Münze gefunden wurde */
		boolean gefunden = false;
		
		
		/* Es werden Ja-Nein-Fragen gestellt, die der Benutzer mit 0 für "Nein" und 1 für "Ja" beantworten kann */
		System.out.println("0 = Nein, 1 = Ja");
		
		
		/* Do-while-Schleife mit Ja-Nein-Fragen, um die falsche Münze zu finden */
		do {

			/* Zuweisung der Minima-/Maxima-/Mittelwert-Variablen */
			mid = (min + max) / 2;
			lMin = min;
			lMax = mid;
			rMin = mid + 1;
			rMax = max;
			
			
			/* Muss eine ungerade Anzahl an Münzen aufgeteilt werden, wird die Münze des rechten Minimums beiseite gelegt.
			 * Es wird gefragt, ob nun beide Münzhaufen gleich schwer sind.
			 */
			if ((max - min + 1) % 2 != 0) {
				rMin++;
				System.out.println("Links liegt "+lMin+" bis "+lMax+", rechts liegt "+rMin+" bis "+rMax+".");
				a = IO.readInt("Sind beide Münzhaufen gleich schwer?: ");
				
				/* Wenn ja, ist die beiseite gelegte Münze die falsche Münze und die Nummer wird der Variablen "loesung" zugewiesen.
				 * Wenn nicht, fällt die Münze weg und es wird normal fortgefahren.
				 */
				if (a == 1) {
					loesung = rMin--;
					gefunden = true;
				}	/* end if */
				
			}	/* end if */

			
			/* Wenn die Münze noch nicht gefunden wurde und eine gerade Anzahl an Münzen aufgeteilt wurde,
			 * wird angezeigt auf welcher Seite der Waagschale welche Münzen (von/bis) liegen.
			 * Es wird gefragt, ob der rechte Münzhaufen schwerer ist.
			 */
			if (!gefunden) {
				System.out.println("Links liegt "+lMin+" bis "+lMax+", rechts liegt "+rMin+" bis "+rMax+".");
				a = IO.readInt("Ist der Münzhaufen rechts schwerer?: ");
				
				/* Wenn ja, dann liegt die falsche Münze auf der linken Seite und das linke Minimum wird zum generellen Minimum,
				 * sowie das linke Maximum zum generellen Maximum.
				 */
				if (a == 1) {
					min = lMin;
					max = lMax;
					
				/* Wenn nein, dann liegt die falsche Münze auf der rechten Seite und das rechte Minimum wird zum generellen Minimum,
				 * sowie das rechte Maximum zum generellen Maximum.
				 */
				} else {
					min = rMin;
					max = rMax;
				}	/* end if-else */
				
			}	/* end if */
			
			
			/* Wenn Maximum vom Minimum subtrahiert 0 ergibt, ist "max" die falsche Münze und wird der Variablen "loesung" zugewiesen. */
			if (min - max == 0) {
				loesung = max;
				gefunden = true;
			}	/* end if */
		
		/* Die do-while-Schleife wird mindesten einmal ausgeführt und wiederholt sich dann, solange die gefälschte Münze nicht gefunden wurde. */
		} while (!gefunden);	/* end do-while */
		
		
		/* Ausgabe der Nummer der falschen Münze */
		System.out.println("Münze Nummer "+loesung+" ist die gefälschte Münze.");
		
	}	/* end main */
	
}	/* end class */```


An folgender Stelle liegt jetzt aber das Problem:

/* Muss eine ungerade Anzahl an Münzen aufgeteilt werden, wird die Münze des rechten Minimums beiseite gelegt.

  • Es wird gefragt, ob nun beide Münzhaufen gleich schwer sind.
    */
    if ((max - min + 1) % 2 != 0) {
    rMin++;
    System.out.println(“Links liegt “+lMin+” bis “+lMax+”, rechts liegt “+rMin+” bis “+rMax+”.”);
    a = IO.readInt("Sind beide Münzhaufen gleich schwer?: ");

    /* Wenn ja, ist die beiseite gelegte Münze die falsche Münze und die Nummer wird der Variablen “loesung” zugewiesen.

    • Wenn nicht, fällt die Münze weg und es wird normal fortgefahren.
      /
      if (a == 1) {
      loesung = rMin–;
      gefunden = true;
      } /
      end if */

} /* end if */```

Wenn der Abstand zwischen den Münzen sehr klein wird, wird die Ausgabe und damit dann ja auch die Berechnung falsch.
Ich hatte als Beispielzahl 736 ausprobiert und irgendwann landete ich bei folgender Ausgabe:

Links liegt 731 bis 733, rechts liegt 734 bis 736.
Ist der Münzhaufen rechts schwerer?: 0
Links liegt 734 bis 735, rechts liegt 737 bis 736.
Sind beide Münzhaufen gleich schwer?:

Ich könnte es zwar mit if-else lösen, aber das wäre ziemlich unschön. Gibt es da irgendwie eine bessere Möglichkeit?
Das Problem ist, dass ich zwingend “Sind beide Münzhaufen gleich schwer?” fragen muss.

Vielleicht solltest Du noch im Programm schreiben, dass die falsche Münze leichter ist als eine echte. Da Du im Programm immer fragst ob ein Haufen schwerer ist, erliegt man leicht dem Eindruck, dass im schwereren Haufen die falsche Münze liegen muss…

Dann ist Deine Aufteilung bei ungerader Anzahl von Münzen ungünstig. Hast Du Dir mal angeschaut wie viel Münzen links und rechts liegen. Du berechnest zwar die mittlere Münze (mit mid = (min + max) / 2;) korrekt und setzt folgerichtig rMin = mid + 1; Allerdings inkrementierst Du bei ungerader Anzahl rMin, anstatt lMax zu reduzieren. Dadurch hast Du (bei ungerader Anzahl) immer unterschiedlich viele Münzen auf beiden Haufen und die Frage ob beide gleich schwer sind erübrigt sich. Denn das wird so nie der Fall sein :wink:

@black_droid : Ich weiß nicht, ob du mit einer IDE arbeitest, aber wenn du deine Kommentare mit einem Doppelstern versiehst, also so:

	/** Niedrigste Münze */
	int min = 1;

zeigt dir die IDE den Kommentar beim überfahren von min mit der Maus an. Zumindest bei Klassenvariablen, ich seh’ gerade, dass deine Variablen lokal in der Main stecken, da geht das nicht.

Eine solche Aufgabe schreit doch geradezu nach einer rekursiven Lösung, das vereinfacht den Code erheblich

	public static int findeMuenze(int min, int max){
		final int result;
		if(min==max) return min;
		int mitte = (max+min)/2;
		if(1==(max - min +1)%2){
			if(ask("Sind die Haufen [%d,%d] und [%d,%d] gleich schwer?",min,mitte-1,mitte,max-1)){
				result = max;
			}else{
				result = findeMuenze(min,max-1);
			}
		}else{
			if(ask("Ist der Haufen [%d,%d] leichter als [%d,%d]?",min,mitte,mitte+1,max)){
				result = findeMuenze(min,mitte);
			}else{
				result = findeMuenze(mitte+1,max);
			}
		}
		return result;
	}

So, folgende Lösung wurde akzeptiert:


	
	public static void main(String[] args) {

		/* Niedrigste Münze */
		int min = 1;

		/* Höchste Münze */
		int max = 1000;

		/* Mittelwert, linkes und rechtes Minimum und Maximum */
		int mid, lMin, lMax, rMin, rMax;

		/* Abfragevariable */
		int a;

		/* Variable für die Ausgabe der Lösung */
		int loesung = 0;

		/* Boolescher Wert, ob die falsche Münze gefunden wurde */
		boolean gefunden = false;

		/*
		 * Es werden Ja-Nein-Fragen gestellt, die der Benutzer mit 0 für "Nein"
		 * und 1 für "Ja" beantworten kann
		 */
		System.out.println("0 = Nein, 1 = Ja");

		/* Do-while-Schleife mit Ja-Nein-Fragen, um die falsche Münze zu finden */
		do {

			/* Zuweisung der Minima-/Maxima-/Mittelwert-Variablen */
			mid = (min + max) / 2;
			lMin = min;
			lMax = mid;
			rMin = mid + 1;
			rMax = max;

			/*
			 * Muss eine ungerade Anzahl an Münzen aufgeteilt werden, wird die
			 * Münze des rechten Minimums beiseite gelegt. Es wird gefragt, ob
			 * nun beide Münzhaufen gleich schwer sind.
			 */
			if ((max - min + 1) % 2 != 0) {
				rMax--;
				System.out.println("Links liegt " + lMin + " bis " + lMax
						+ ", rechts liegt " + rMin + " bis " + rMax + ".");
				a = IO.readInt("Sind beide Münzhaufen gleich schwer?: ");

				/*
				 * Wenn ja, ist die beiseite gelegte Münze die falsche Münze und
				 * die Nummer wird der Variablen "loesung" zugewiesen. Wenn
				 * nicht, fällt die Münze weg und es wird normal fortgefahren.
				 */
				if (a == 1) {
					loesung = rMax + 1;
					gefunden = true;
				} /* end if */

			} /* end if */

			/*
			 * Wenn die Münze noch nicht gefunden wurde und eine gerade Anzahl
			 * an Münzen aufgeteilt wurde, wird angezeigt auf welcher Seite der
			 * Waagschale welche Münzen (von/bis) liegen. Es wird gefragt, ob
			 * der rechte Münzhaufen schwerer ist.
			 */
			if (!gefunden) {
				System.out.println("Links liegt " + lMin + " bis " + lMax
						+ ", rechts liegt " + rMin + " bis " + rMax + ".");
				a = IO.readInt("Ist der Münzhaufen rechts schwerer?: ");

				/*
				 * Wenn ja, dann liegt die falsche Münze auf der linken Seite
				 * und das linke Minimum wird zum generellen Minimum, sowie das
				 * linke Maximum zum generellen Maximum.
				 */
				if (a == 1) {
					min = lMin;
					max = lMax;

				/*
				 * Wenn nein, dann liegt die falsche Münze auf der rechten
				 * Seite und das rechte Minimum wird zum generellen Minimum,
				 * sowie das rechte Maximum zum generellen Maximum.
				 */
				} else {
					min = rMin;
					max = rMax;
				} /* end if-else */

			} /* end if */

			/*
			 * Wenn Maximum vom Minimum subtrahiert 0 ergibt, ist "max" die
			 * falsche Münze und wird der Variablen "loesung" zugewiesen.
			 */
			if (min - max == 0) {
				loesung = max;
				gefunden = true;
			} /* end if */

		/*
		 * Die do-while-Schleife wird mindesten einmal ausgeführt und
		 * wiederholt sich dann, solange die gefälschte Münze nicht gefunden
		 * wurde.
		 */
		} while (!gefunden); /* end do-while */

		/* Ausgabe der Nummer der falschen Münze */
		System.out.println("Münze Nummer " + loesung
				+ " ist die gefälschte Münze.");

	} /* end main */

} /* end class */```

Danke, _Michael :)

Ist zwar zu spät, aber ich hätte noch eine ohne Ifs in der Spiellogik

:
[spoiler]```import java.util.Scanner;

import org.junit.Test;

public class FindeLeichtereTest {
private static final int START_ANZAHL = 47;
int ergebnis, linksMin = 0, rechtsMax = START_ANZAHL,
aktuelleAnzahl = START_ANZAHL;
Scanner scanner = new Scanner(System.in);

public static void main(String[] args) {
	new FindeLeichtereTest().test();
}

@Test
public void test() {
	while (1 < aktuelleAnzahl) {
		aktuelleAnzahl /= 2;
		printSchaleninhalt("Links", linksMin);
		printSchaleninhalt("Rechts", rechtsMax - aktuelleAnzahl);
		char userInput = extrahiereErstesDerEingabeZeichenAlsKleinbuchstabe();
		switch (userInput) {
		case 'l':
			ergebnis = linksMin;
			rechtsMax = linksMin + aktuelleAnzahl;
			break;
		case 'r':
			ergebnis = rechtsMax;
			linksMin = rechtsMax - aktuelleAnzahl;
			break;
		default:
			ergebnis = linksMin + aktuelleAnzahl;
			aktuelleAnzahl = 1;
		}
	}
	System.out.println("die gesuchte Kugel ist Nr.: " + (1 + ergebnis));
}

private char extrahiereErstesDerEingabeZeichenAlsKleinbuchstabe() {
	String nextLine = scanner.nextLine();
	boolean isEingabeValide = false;
	char userInput = 'k';
	while (!isEingabeValide) {
		System.out
				.println("welche Schale ist leichter [(l)inks,(r)echts,(k)eine]? ");
		try {
			userInput = nextLine.trim().toLowerCase().charAt(0);
			isEingabeValide = 'l' == userInput || 'r' == userInput
					|| 'k' == userInput;
		} catch (StringIndexOutOfBoundsException e) {
			return 'k';
		}
	}
	return userInput;
}

private void printSchaleninhalt(String message, int startNummer) {
	System.out.println(startNummer + "  " + aktuelleAnzahl);
	StringBuilder kugelNummernListe = new StringBuilder(message + " lieg"
			+ (1 == aktuelleAnzahl ? "t" : "en") + " die Nummner"
			+ (1 == aktuelleAnzahl ? "" : "n") + ":");
	String separator = " ";
	for (int i = startNummer; i < startNummer + (aktuelleAnzahl); i++) {
		kugelNummernListe.append(separator);
		kugelNummernListe.append(i + 1);
		separator = ", ";
	}
	System.out.println(kugelNummernListe.toString());
}

}```[/spoiler]

bye
TT

Aber das halbierungsverfahren ist nicht immer eines der besten.
Angenommen man hat neun Münzen, dann würde ich erstmal drei drei wiegen. Falls die gleich sind, von den übrigen drei zwei wiegen. Falls diese gleich sind ist die letzte Kugel die leichtere. Aomit muss man inmer zweimal wiegen, aber im Durchachnitt ist diese Methode schneller bzw effizienter als das Halbieren

hatten wir schon diskutiert, halbieren ist in der Aufgabenstellung gefordert.

bye
TT

Hab mir nicht alles durchgelesen… Aber ok wenn es verlangt wird