# Rundungsfehler



## Patneu (19. Jun 2011)

Hallo,

ich habe ein sehr seltsames Problem und kann mir einfach nicht erklären woran es liegt. Vermutlich wieder eine von diesen Sachen, wo man sich später gegen den Kopf schlägt, dass man nicht früher drauf gekommen ist...

Ich habe ein Programm, in dem der Benutzer in verschiedene Textfelder Zahlen eingibt, diese werden ausgelesen, wenn der Benutzer einen Button drückt und dann an den Konstruktor einer Schiff-Klasse übergeben:

[JAVA=1044]class Schiff
{
	String bezeichnung;

	int partikelAngriff;
	int energieAngriff;
	int schilde;
	int panzer;
	int antrieb;
	int hitpoints;
	Integer position1;
	Integer position2;
	Integer position3;
	Integer position4;
	Integer position5;
	Integer position6;
	Integer position7;

	double absolutePosition;

	boolean zerstört;
	boolean gereiht;

	public Schiff(String bezeichnung, int partikelAngriff, int energieAngriff, int schilde, int panzer, int antrieb, int hitpoints)
	{
		this.bezeichnung = bezeichnung;
		this.partikelAngriff = partikelAngriff;
		this.energieAngriff = energieAngriff;
		this.schilde = schilde;
		this.panzer = panzer;
		this.antrieb = antrieb;
		this.hitpoints = hitpoints;

		absolutePosition = ((double)(this.partikelAngriff + this.energieAngriff)) - (((double)this.antrieb) / ((double)this.hitpoints));
		if(absolutePosition > 0)
		{
			absolutePosition = ((int)((absolutePosition * 10000) + 0.5)) / 10000.0;
		}
		if(absolutePosition < 0)
		{
			absolutePosition = ((int)((absolutePosition * 10000) - 0.5)) / 10000.0;
		}
		position1 = (int)(absolutePosition / 100);
		position2 = ((int)(absolutePosition / 10)) - (((int)(absolutePosition / 100)) * 10);
		position3 = ((int)absolutePosition) - (((int)(absolutePosition / 10)) * 10);
		position4 = ((int)(absolutePosition * 10)) % 10;
		position5 = ((int)(absolutePosition * 100)) % 10;
		position6 = ((int)(absolutePosition * 1000)) % 10;
		position7 = ((int)(absolutePosition * 10000)) % 10;
	}[/code]

Nun wollte ich an ein paar Beispiel-Werten testen, ob die Werte für die Variablen absolutePosition und position1 bis position7 richtig berechnet werden und keine Rundungsfehler auftreten.

In den meisten Fällen funktioniert das auch und ich bekomme die richtigen Werte. Bei einigen Fällen fiel mir aber auf, dass plötzlich an Stellen, die eine 0 aufweisen sollten, eine 9 stand und wenige Male auch bei Stellen mit einer 5 eine 4.

Ich habe auch einige Werte durchgetestet, um zu sehen, ob die Werte irgendwelche markanten Gemeinsamkeiten haben, die auf den Fehler hinweisen könnten, konnte bis jetzt aber nicht fündig werden. Hier meine Beispielwerte (die erste Zahl ist der Wert absolutePosition, die zweiten die Werte von position1 - position7 hintereinander):

  2,3 0023900
  4,1 0041900
  4,6 0046900
  5,1 0059100
  8,2 0082900
  8,7 0087900
  9,7 0097900
 10,2 0102900
 16,4 0164900
 16,9 0169900
 19,9 0199900
 20,4 0204900
 32,3 0323990
 32,8 0328900
 33,3 0333900
 40,3 0403900
 40,8 0408900
 64,1 0641990
 64,6 0646990
 65,1 0651990
 65,6 0656900
 66,1 0661900
 81,1 0811900
 81,6 0816900
128,2 1282990
128,7 1287990
130,2 1302990
130,7 1307990
131,2 1312900
131,7 1317900
163,2 1632900
163,7 1637900
256,4 2564990
256,9 2569990
261,4 2614990
261,9 2619990
262,4 2624900
262,9 2629900
326,9 3269900
327,4 3274900
512,3 5123990
512,8 5128990
523,3 5233990
523,8 5238990
524,3 5243900
524,8 5248900
654,8 6548900
655,3 6553900

Die einzige Gemeinsamkeit, die ich bisher feststellen kann, ist, dass bisher keine negativen Zahlen (die durchaus vorkommen können) den Fehler aufweisen und die meisten Zahlen nur eine Nachkommastelle haben (allerdings nicht alle, ich weiß von mindestens 2 Zahlen, die auch 2 oder 3 Nachkommastellen haben, sie sind nur in der liste nicht enthalten).

Auch wurde bei allen Nachrechnungen, die ich bis jetzt gemacht habe, die Variable absolutePosition, aus der sich die anderen Variablen herleiten immer korrekt berechnet und gerundet, weshalb mir unbegreiflich ist, wo das Programm diese Werte hernimmt.

Ich hoffe jemand von euch hat einen schlauen Einfall, weil ich mit meinem Latein wirklich am Ende bin. Ich bedanke mich schon im Voraus für alle hilfreichen Hinweise. 



PS: Bevor es jemand erwähnt, ich weiß, dass da viel mehr Kommas drin sind, als eigentlich notwendig, aber ich wollte lieber auf Nummer sicher gehen. :bae:


----------



## Plastagen (19. Jun 2011)

Wäre vielleicht hilfreich, wenn du sagst, welche Werte "falsch" sind und welche Werte du eigentlich erwarten würdest.


----------



## Patneu (19. Jun 2011)

Ok, ich bin irgendwie davon ausgegangen, das ginge aus dem Code hervor, aber ich erkläre es auch gern:

Die Werte der Variablen "position1" bis "position 7" sollen die einzelnen Ziffern des Wertes der Variable "absolutePosition" von der 3. Stelle vor dem Komma bis zur 4. Stelle nach dem Komma sein.

Dafür muss der Wert "absolutePosition" natürlich auf 4 Nachkommastellen gerundet werden, was das Programm auch vernünftig tut, das habe ich geprüft, indem ich mir das Ergebnis habe ausgeben lassen.

Wenn also beispielsweise "absolutePosition", um ein Beispiel aus obiger Liste zu wählen, den Wert "32.3" hat, erwarte ich das die Variablen "position1" bis "position7" die Werte "0" "3" "2" "3" "0" "0" "0" haben. Wie aus der Liste ersichtlich, haben sie aber aus irgendeinem Grund die Werte "0" "3" "2" "3" "9" "9" "0" und ich habe nicht den Hauch einer Ahnung wieso. :bahnhof:

"position4" und "position5" werden hier ohne mir ersichtlichen Grund als 9en angegeben. Bei den meisten Werten, die "absolutePosition" haben kann, passiert das nicht und es muss auch nicht immer unbedingt "position4" und "position5" betreffen und die falschen Werte müssen auch nicht unbedingt 9en sein (ich hatte auch schon 4en anstelle von 5en).

Aber es sind eben aus irgendeinem Grund die falschen Werte und ich wüsste gerne warum das passiert bzw. was ich falsch gemacht haben könnte?


----------



## Plastagen (19. Jun 2011)

Auf dem ersten Blick würde ich sagen, dass es an der Zeile 


```
absolutePosition = ((double)(this.partikelAngriff + this.energieAngriff)) - (((double)this.antrieb) / ((double)this.hitpoints));
```

Und an den Umwandlungsungenauigkeiten im PC zurückzuführen ist.

Und an dem späteren zurückcasten wieder zu Integer.
Laut dem Thread hier:
http://www.java-forum.org/java-basics-anfaenger-themen/77948-int-double-umwandeln-geht.html
Geht beim expliziten casten immer an Genauigkeit verloren, was hier wohl der Fall sein wird.
Explizites casten, heißt cast die angegeben werden müssen, spricht cast von Datentypen größerer Genauigkeit zu Datentypen geringerer Genauigkeit, hier also double -> int

Warum castet du eigentlich imemr hin und her? 
Wäre es nicht besser gleich alles in double anzugeben?


----------



## Patneu (19. Jun 2011)

Naja, stimmt schon, zumindest einige der Werte, aus denen sich "absolutePosition" ermittelt, werde ich später noch als double brauchen und bei den anderen gibt es zumindest keinen zwingenden Grund, warum sie int sein müssten. Werde ich auf jeden Fall machen, dann sieht's auch nicht mehr ganz so wirr aus. 

Also ich weiß ja, dass beim Umwandeln Genauigkeit verloren gehen kann, aber müsste ich das, wenn es bei dieser Zeile

```
absolutePosition = ((double)(this.partikelAngriff + this.energieAngriff)) - (((double)this.antrieb) / ((double)this.hitpoints));
```
passiert, nicht auch schon am Wert der Variable "absolutePosition" selbst bemerken und nicht erst bei den daraus resultierenden Variablen? Denn die "absolutePosition"-Variable hatte bisher immer genau den Wert, den ich erwartet habe, da waren keine Ungenauigkeiten festzustellen.

Außerdem erschließt sich mir auch nicht ganz, wie solche Abweichungen durch schlichte Ungenauigkeiten zustande kommen sollen? Es geht ja größtenteils um Zahlen, die im Prinzip nur eine Nachkommastelle haben, wie kann bei den eigentlich nicht-existierenden 2. und 3. Nachkommastellen dann plötzlich eine 9 auftauchen? Es gibt ja im Prinzip eigentlich gar nichts zu runden, wo man was falsch machen könnte...

Auch wäre mir bei Ungenauigkeiten dann unklar, wieso sie nur in sehr speziellen Fällen auftreten, also wenn bspw. die 1. Stelle nach dem Komma nur um 1 ehrhöht oder reduziert wird, tritt der Fehler schon nicht mehr auf. ???:L Auch habe ich die Werte, bei denen der Fehler auftritt teilweise auch viele Male hintereinander völlig neu berechnet, aber die Fehler sind konstant geblieben.


----------



## Plastagen (19. Jun 2011)

Das liegt ganz einfach daran, dass der Rechner intern nicht alle Zahlen korrekt darstellen kann, da die Zahlen ja nur durch 0 und 1 intern abgespeichert werden. 
Da kann es durchaus auch mal vorkommen, dass bei Zahlen mit nur wenigen Kommastellen bei der Umwandlung in andere Datentypen schon die besagten Fehler auftreten können.
Das hier könnte dir vielleicht weiter helfen:
Gleitkommazahl ? Wikipedia


----------



## Patneu (19. Jun 2011)

Ja, schon klar, dass das vorkommen kann, es kommt mir nur in diesem Fall sehr seltsam vor.

Ich würde doch annehmen, dass wenn es solche Ungenauigkeiten gibt, dass diese nicht immer (bei jeder neuen Berechnung) und dann auch nicht immer bei der selben Zahl auftreten würden, oder?
Wieso bspw. sollte es immer die Zahl 32,3 treffen, aber nie die Zahl 33,3? Die sollten sich doch vom "Fehler-Risiko" her nicht wirklich nennenswert unterscheiden?

Das ist halt das, was ich nicht begreife, dass es eben immer bestimmte Zahlen sind, bei denen der Fehler dann auch jedes Mal auftritt. Das würde ich einfach nicht erwarten, wenn es sich um Ungenauigkeiten bei der Umwandlung handelt, dass sie mit einer solchen Präzision und Kontinuität passieren.



Unabhängig davon hängt aber in meinem Programm vieles davon ab, dass diese Werte korrekt sind. Wenn man diese Ungenauigkeiten wirklich nicht vermeiden können sollte, hoffe ich, dass es da noch andere Möglichkeiten gibt, wie man das besser lösen kann. Hat jemand einen Vorschlag?


----------



## Marco13 (20. Jun 2011)

Das hat mit "Risiko" nicht viel zu tun - das kann man alles vorher ausrechnen. Aber es kann duchaus sein, dass bei einer Rechung z.B. 
32.2 rauskommen sollte, und das intern 32.19999999132 ist, und bei einer anderen
32.3 rauskommen sollte, und das intern 32.30000000000 ist. Und wie so eine Zahl, wenn man sie mit System.out.println ausgibt, aussieht, hat erstmal auch nicht sooo viel damit zu tun, welche Zahl es tatsächlich IST. (Bei der Umwandung von floats in Strings werden die Strings "schön" gemacht...)

Aber eigentlich ist das alles ziemlich egal: Erklär' mal genau was du da mit diesen Nachkommastellen und diesen Faktoren 10, 100, 1000 usw. rumpfuschst. Das sieht nach ziemlichem Gefraddel aus... ???:L


----------



## Dekker (20. Jun 2011)

Patneu hat gesagt.:


> Ich würde doch annehmen, dass wenn es solche Ungenauigkeiten gibt, dass diese nicht immer (bei jeder neuen Berechnung) und dann auch nicht immer bei der selben Zahl auftreten würden, oder?
> Wieso bspw. sollte es immer die Zahl 32,3 treffen, aber nie die Zahl 33,3? Die sollten sich doch vom "Fehler-Risiko" her nicht wirklich nennenswert unterscheiden?
> 
> Das ist halt das, was ich nicht begreife, dass es eben immer bestimmte Zahlen sind, bei denen der Fehler dann auch jedes Mal auftritt. Das würde ich einfach nicht erwarten, wenn es sich um Ungenauigkeiten bei der Umwandlung handelt, dass sie mit einer solchen Präzision und Kontinuität passieren.



Eigentlich wäre es schlimmer wenn der Fehler nicht immer bei der selben Zahl auftretten würde. Wenn wir mal vom Fehler-Risiko reden, ich denke mal du hast den Wikiartikel gelesen?
Der Grund ist einfach, dass die Abstände der darstellbaren Zahlen nicht äquidistant sind.

Java nutzt zur Darstellung von fließkommazahlen den IEEE747 Standard. Les dich mal darin ein und Stelle die Zahl 32,3 dar. Bei Operationen kann je nach wert der beiden kommazahlen der Darstellugnsfehler größer werden.


----------



## Patneu (21. Jun 2011)

Marco13 hat gesagt.:
			
		

> Aber eigentlich ist das alles ziemlich egal: Erklär' mal genau was du da mit diesen Nachkommastellen und diesen Faktoren 10, 100, 1000 usw. rumpfuschst. Das sieht nach ziemlichem Gefraddel aus...



Naja, also im Prinzip möchte ich folgendes tun:

Aus den Variablen "partikelAngriff", "energieAngriff", "antrieb" und "hitpoints" soll der Wert der Variable "absolutePosition" errechnet werden. Dieser Wert soll maximal 3 Stellen vor und 4 Stellen hinter dem Komma haben.

Und die einzelnen Ziffern von "absolutePosition" möchte ich als Integer (oder int) abspeichern, um sie als keys in ineinander verschachtelten HashMaps zu verwenden, die am Ende das jeweilige Schiff-Objekt halten sollen. Dazu dienen die Variablen "position1" bis "position7"

Natürlich hätte ich als key für die HashMap auch direkt "absolutePosition" selbst verwenden können. Die möglichen keys könnten dann aber von -999.9999 bis 999.9999 reichen. Da aber viele der möglichen keys (aber immer unterschiedliche) nicht verwendet werden, würde das bedeuten, dass, wenn ich die in der HashMap gespeicherten Schiff-Objekte später der Reihe nach wieder auslesen will, ich viele nicht existierende keys prüfen müsste, um die tatsächlich existierenden Schiff-Objekte wiederzufinden.

Wenn ich dagegen die einzelnen Ziffern prüfe und es keine Schiff-Objekte gibt, bei denen "absolutePosition" größer als 899,9999 ist, dann kann ich durch Prüfung der ersten Ziffer schon alle Werte für "absolutePosition" >= 900,0000 ausschließen und muss sie nicht prüfen, usw.



Das ist sicher nicht die eleganteste Lösung, aber die einzige die mir einfallen wollte und dafür ist es leider nötig, dass die Variablen "position1" bis "position7" die richtigen Werte haben.

Wenn jemand eine Idee hat, wie ich die richtigen Werte bekommen oder wie ich sonst das massenhafte Prüfen der nicht-existierenden Werte verhindern kann, wäre das super.


----------



## Marco13 (21. Jun 2011)

Patneu hat gesagt.:


> Das ist sicher nicht die eleganteste Lösung, ...



Ja, sicher nicht... lass mich mal kurz überlegen ... :reflect: Ja, es ist nicht die elgeganteste, aber vielleicht die UN-eleganteste 

Du hast jetzt eher beschrieben, WIE du das machst, was du machst (Ziffern speichern, verschachtelte HashMaps...), aber nicht so direkt, WAS du vorhast. 

Sehe ich das richtig, dass diese Eigenschaften ("partikelAngriff", "energieAngriff", "antrieb" und "hitpoints") quasi zur "Identifizierung" eines einzlenen Schiffes verwendet werden sollen? (Gibt es nicht auch mal zwei Schiffe mit genau den gleichen Eigenschaften?)


----------



## Patneu (21. Jun 2011)

Die Eigenschaften "partikelAngriff", "energieAngriff", "schilde", "panzer", "hitpoints" und "antrieb" sind die Eigenschaften des Schiffs, ja.

"Identifizieren", also wiederfinden, muss ich die Schiffe aber anhand ihres Positions-Werts "absolutePosition", weil das Programm einen Kampf zwischen den Schiffen simulieren soll. Dabei legt der Positions-Wert "absolutePosition" die Reihenfolge fest, in der die Schiffe kämpfen (die mit dem höchsten Wert kämpfen zuerst).

Dabei kann es auch vorkommen, dass es verschiedene "Versionen" von Schiffen mit dem gleichen Positions-Wert "absolutePosition" gibt (z.B. mit unterschiedlichen "schilde" oder "panzer"-Werten, die für "absolutePosition" nicht relevant sind) und natürlich können auch mehrere Schiffe mit genau den selben Eigenschaften existieren. Für die Versionen und Anzahl der Schiffe, sollten die HashMaps dann eine 8. und 9. Dimension haben.

Ich hoffe, es ist verständlich geworden, was ich erreichen möchte.


----------



## Samuel72 (21. Jun 2011)

Hallo,

also deine Variablen position1... schreien ja geradezu nach einem Array.
Ich würde in etwa folgendermaßen vorgehen (ohne das jetzt ausprobiert zu haben):


```
int po = absolutePosition*10000;
for (int i=0; i<7; i++) {
  position[i] = po % 10;
  po /= 10;
}
```
Allerdings habe ich jetzt nicht genau im Kopf, wie der %-Operator mit negativen Zahlen umgeht.

Genaugenommen würde ich so aber vermutlich nicht vorgehen. Wie schon erwähnt, scheint diese Weise so ziemlich die maximal uneffektive zu sein.


----------



## Marco13 (21. Jun 2011)

So halb... aber falls es so ist, wie ich es jetzt verstanden habe, ist es gröbster Unfug  (und bei einer 9-dimensionalen HashMap kann man sich dessen ziemlich sicher sein). Vermutlich wirst du dich schon selbst gefragt haben, ob du da nicht irgendwas grundsätzliches falsch machst...

Aber um den Rest auch noch zu verstehen: Die "absolutePosition" beschreibt also NICHT eine _räumliche_ Position in irgendeiner Form, sondern nur eine Position in einer Art "Rangliste"? 
Und es KANN vorkommen, dass zwei oder mehr Schiffe alle die gleichen Eigenschaften (und damit insbesondere auch die gleiche absolutePosition) haben, aber es eben trotzdem unterschiedliche Schiffe sein sollen? Kann man also sagen, dass dieses Rumgerechne NUR dem Zweck dient, die Schiffe entsprechend ihrer "absolutenPosition" zu ordnen? Und dass die absolutePosition keine "direkte" Eigeschaft des Schiffes ist, und eigentlich gar nicht gebraucht würde, wenn man das Sortieren auch "irgendwie anders" hinkriegen würde? 

Und: Was wird bei dieser "HashMap" eigentlich gemappt? Sind das wirklich alles 
Map<Integer, nextMap> 
Objekte, die die Ziffern von 0 bis 9 auf die nächste "Ebene" abbilden, in der dann wiederum eine
Map<Integer, nextMap> 
liegt (und so weiter) ...? :autsch:

Dann würden sich noch ein paar Detailfragen stellen, z.B. wie oft ändern sich diese Werte, ändern sich immer nur die Werte von einem Schiff und muss es dann neu einsortiert werden, oder ändern sich die Werte nie oder immer alle von allen Schiffen auf einmal oder so... ?

Aber mal pragmatisch-zielorientiert: WENN du sowas hättest wie 
List<Ship> shipsSortedByPower = ... 
Also eine Liste, die ALLE Schiffe enthält, sortiert nach ihrer "Stärke" (d.h. nach der Größe des Wertes von 'absolutePosition'), würdest du dann um diese 9-Dimensionale HashMap herumkommen?


----------



## Patneu (21. Jun 2011)

Marco13 hat gesagt.:
			
		

> Vermutlich wirst du dich schon selbst gefragt haben, ob du da nicht irgendwas grundsätzliches falsch machst...



Das ist sogar sehr gut möglich. Ich bin im Moment noch absoluter Anfänger, deshalb kenne ich auch nur einige sehr grundlegende Sachen. Wenn es bessere Wege gibt, etwas zu tun, bin ich immer lernwillig. 



			
				Marco13 hat gesagt.:
			
		

> Aber um den Rest auch noch zu verstehen: Die "absolutePosition" beschreibt also NICHT eine räumliche Position in irgendeiner Form, sondern nur eine Position in einer Art "Rangliste"?
> Und es KANN vorkommen, dass zwei oder mehr Schiffe alle die gleichen Eigenschaften (und damit insbesondere auch die gleiche absolutePosition) haben, aber es eben trotzdem unterschiedliche Schiffe sein sollen? Kann man also sagen, dass dieses Rumgerechne NUR dem Zweck dient, die Schiffe entsprechend ihrer "absolutenPosition" zu ordnen? Und dass die absolutePosition keine "direkte" Eigeschaft des Schiffes ist, und eigentlich gar nicht gebraucht würde, wenn man das Sortieren auch "irgendwie anders" hinkriegen würde?



Kann man so sagen, ja. "absolutePosition" ist zumindest kein Wert, mit dem später noch irgendetwas errechnet wird, er gibt nur die Position des Schiffs in der Kampf-Reihenfolge an.



			
				Marco13 hat gesagt.:
			
		

> Und: Was wird bei dieser "HashMap" eigentlich gemappt? Sind das wirklich alles
> Map<Integer, nextMap>
> Objekte, die die Ziffern von 0 bis 9 auf die nächste "Ebene" abbilden, in der dann wiederum eine
> Map<Integer, nextMap>
> liegt (und so weiter) ...? :autsch:



Öhm, ehrlich gesagt ja, so war das gedacht. 



			
				Marco13 hat gesagt.:
			
		

> wie oft ändern sich diese Werte, ändern sich immer nur die Werte von einem Schiff und muss es dann neu einsortiert werden, oder ändern sich die Werte nie oder immer alle von allen Schiffen auf einmal oder so... ?



Also die Werte "partikelAngriff", "energieAngriff", "schilde", "panzer", "hitpoints", "antrieb" und "absolutePosition" sind nach der Eingabe für jedes Schiff fest und ändern sich nicht mehr. Es kann aber sein, dass neue Schiffe mit den gleichen Eigenschaften hinzukommen oder die vorhandenen wieder entfernt werden müssen.

Ändern können sich andere Variablen, die später noch dazukommen sollen und bspw. den ausgeteilten oder genommenen Schaden des Schiffs wiedergeben sollen oder wie viele Hitpoints noch verbleiben. Diese Werte haben auf die Positionierung aber keinen Einfluss.

Man kann also sagen, dass sich die Positionierung der Schiffe nicht mehr ändern kann, was aber passieren wird, ist, dass wenn ein Schiff keine Hitpoints mehr hat, es in ein Array "verschoben" werden muss, dass die zerstörten Schiffe enthält. Dann muss es natürlich aus dem Array/Map/List, dass die Positionierung der Schiffe für den Kampf wiedergibt entfernt werden.



			
				Marco13 hat gesagt.:
			
		

> Aber mal pragmatisch-zielorientiert: WENN du sowas hättest wie
> List<Ship> shipsSortedByPower = ...
> Also eine Liste, die ALLE Schiffe enthält, sortiert nach ihrer "Stärke" (d.h. nach der Größe des Wertes von 'absolutePosition'), würdest du dann um diese 9-Dimensionale HashMap herumkommen?



Theoretisch schon, allerdings gäbe es ein paar Schwierigkeiten bzw. Dinge, die diese List ermöglichen müsste:
- Die List muss "durchzählbar" sein, d.h. ich muss mit einer Schleife die Schiffe in der richtigen Reihenfolge durchgehen können
- Schiffe müssen an der richtigen Position hinzugefügt werden können
- Schiffe müssen so entfernt werden können, dass die verbleibenden Schiffe nachrücken und die Reihenfolge erhalten bleibt
- die Reihenfolge von Schiffen mit gleichem "absolutePosition"-Wert muss zufällig bestimmt werden können, weil sie im ursprünglichen Programm (dessen Kämpfe simuliert werden) "nicht definiert" ist, wie mir gesagt wurde
- es muss möglich sein, immer die nächsten 3 Schiffe zu finden, also erst 1-3, 4-6, 7-9 usw., weil diese Schiffe gegen das 1. gegnerische Schiff kämpfen


----------



## Marco13 (21. Jun 2011)

Die genannten Punkte treffen alle auf eine List zu (genau dafür sind Lists da)

Lediglich zwei Punkte sind noch ein bißchen schwammig:
_- Schiffe müssen an der richtigen Position hinzugefügt werden können_

Das kriegt man auf die eine oder andere Art auf jeden Fall hin. Am einfachsten wäre es, wenn man die Liste einmal erstellt und dann sortiert. Man kann aber auch leicht sortiert einfügen, d.h. man kann ja die Position finden, wo ein Schiff einzuordnen ist.

_- die Reihenfolge von Schiffen mit gleichem "absolutePosition"-Wert muss zufällig bestimmt werden können, weil sie im ursprünglichen Programm (dessen Kämpfe simuliert werden) "nicht definiert" ist, wie mir gesagt wurde_

Was heißt "zufällig bestimmt"? Angenommen, man hat drei Schiffe mit gleichen Werten, soll die Reihenfolge dann "zufällig" sein, oder "bestimmt"? (ist es "egal" welche Reihenfolge sie haben, oder wie... ???:L )


----------



## Patneu (21. Jun 2011)

Marco13 hat gesagt.:
			
		

> Am einfachsten wäre es, wenn man die Liste einmal erstellt und dann sortiert.



Das heißt also, ich würde die Schiffe vorerst gar nicht sortiert einfügen, sondern erstmal der Reihe nach "so wie sie kommen"?

Nur wie finde ich die Schiffe dann wieder, wenn der Benutzer sie evtl wieder entfernen möchte? Ich nehme an, die List müsste dann vor dem eigentlichen "Einsatz" schon einmal sortiert werden?

Und wie genau kann ich die List denn sortieren. Die Klasse selbst scheint dafür keine Methode zu haben, oder übersehe ich da was?



			
				Marco13 hat gesagt.:
			
		

> Was heißt "zufällig bestimmt"? Angenommen, man hat drei Schiffe mit gleichen Werten, soll die Reihenfolge dann "zufällig" sein, oder "bestimmt"? (ist es "egal" welche Reihenfolge sie haben, oder wie... )



Die Reihenfolge muss zufällig sein. Das heißt, wenn ich jetzt bspw 3 Schiffe mit identischem "absolutePosition"-Wert habe und beim 1. ist "panzer" = 50 und "schilde" = 0, beim 2. ist "panzer" = 0 und "schilde" = 50 und beim 3. ist "panzer" = 25 und "schilde" = 25.

Dann ist laut den Programmierern des Spiels, dessen Kämpfe ich simulieren will, die Reihenfolge dieser Schiffe "nicht definiert", weil es keinen Parameter gibt, der die Reihenfolge weiter festlegt bzw. sie ihn nicht kennen. Und da ich mir nicht vorstellen kann, wie bei einem Computer-Programm überhaupt etwas "nicht definiert" sein soll :bahnhof:, scheint es am zweckmäßigsten, wenn die weitere Positionierung zufällig ist.

Die Schiffe sollen - nach der Positionierung nach dem "absolutePosition"-Wert - aber nur ein einziges Mal am Anfang zufällig positioniert werden. "Am Anfang" heißt hierbei: sobald alle Schiffe vollständig eingegeben sind und der Benutzer auf "Starten" drückt. Wenn danach Schiffe zerstört werden, sollen die verbleibenden nur nachrücken und keine neue zufällige Positionierung erfolgen.



Zwei Sachen habe ich auch noch vergessen, die gegeben sein müssten:

- Es kann, wie schon erwähnt, negative Positions-Werte geben, es sollte möglich sein, diese einzugeben (das ist nicht zwingend wichtig, das Programm würde aber etwas ungenauer, wenn das nicht geht)
- Die Menge der Schiffe, die die List aufnehmen kann, müsste varriieren können (zu Anfang einer der Gründe für die HashMaps), weil ja nicht von vornherein bekannt ist, wie viele Schiffe der Benutzer eingibt.


----------



## Samuel72 (22. Jun 2011)

Folgender Vorschlag:

Für die Liste benutzt du eine java.util.LinkedList

Die Liste wird sortiert mit java.util.Collections.sort(liste)

Dazu muss deine Klasse Schiff das Interface java.util.Comparable implementieren:
Die Methode compareTo vergleicht dann einfach die absolutePosition zweier Schiffe.

Ich würde jedem Schiff außerdem noch ein double - Feld littleRandom hinzufügen:
Eine Zufallszahl, die zur absolutePosition hinzugefügt wird und die so klein ist, dass sie bei verschiedenen absolutePosition nicht ins Gewicht fällt, aber bei gleichen absolutePosition den Ausschlag gibt.


----------



## Landei (22. Jun 2011)

[c]LinkedList[/c] ist fast immer [c]ArrayList[/c] unterlegen.


----------



## Marco13 (22. Jun 2011)

@Landei: Wenn "häufig" Elemente "relativ weit vorne" eingefügt oder entfernt werden müssen, wäre LinkedList theoretisch besser, aber... die Frage, wie man eine 9-dimensionale HashMap vermeidet erscheint mit im Moment viel, viel elementarer...

Im Prinzip hat Samuel72 schon angedeutet, worauf meine ganze Nachfragerei hinauslaufen sollte: Als erstes könntest du dir eine Liste mit Schiffen erstellen, und diese vom Benutzer füllen lassen

```
private List<Schiff> schiffe = new ArrayList<Schiff>();

void eingabe()
{
    Schiff schiff = wirdVomBenutzerEingegeben();
    schiffe.add(schiff);
}
```

In dieser Liste liegen die Schiffe dann erstmal in der Reihenfolge, in der der Benutzer sie eingegeben hat.

Wenn dann später "gekämpft" werden soll, kann man die Liste sortieren (oder eine Kopie der Liste erstellen, und DIE dann sortieren). Dazu kann man die Schiffe "Comparable" machen, aber flexibler ist es, wenn man einen Comparator verwendet. Der Comparator beschreibt nur, wie man ZWEI Schiffe vergleicht, und wenn man das weiß, kann man damit die gesamte Liste sortieren. Der Comparator könnte z.B. so aussehen:

```
class SchiffComparator implements Comparator<Schiff>
{
    @Override
    public int compare(Schiff s0, Schiff s1)
    {
        float a0 = berechneAbsolutePosition(s0);
        float a1 = berechneAbsolutePosition(s1);
        return Float.compare(a0, a1);
    }

    private float berechneAbsolutePosition(Schiff s)
    {
        // So, wie du's bisher gemacht hast...
    }
}
```

Wenn man den hat, kann man die Schiff-Liste sortieren, und dann die Operationen durchführen, die du angedeutet hast

```
void kampf()
{
    // Sortiere die Schiffe
    List<Schiff> sortierteSchiffe = new ArrayList<Schiff>(schiffe);
    Collections.sort(sortierteSchiffe, new SchiffComparator());

    // Drei Schiffe kämpfen lassen
    Schiff s0 = sortierteSchiffe.get(0);
    Schiff s1 = sortierteSchiffe.get(1);
    Schiff s2 = sortierteSchiffe.get(2);
    Schiff verlierer = kämpfe(s0,s1,s2);
    
    // Verlierer aus der Liste entfernen
    sortierteSchiffe.remove(verlierer);
}
```

Kannst ja erstmal schauen, ob das bei dir alles funktioniert und alle Anforderungen erfüllt - sollte aber, soweit ich das bisher verstanden habe, passen.

BTW: Wenn in der Liste mehrere "gleich starke" Schiffe vorkommen, wird ihre Reihenfolge beim Sortieren beibehalten (die haben dann also die Reihenfolge, in der sie eingegeben wurden). Das sollte wohl OK sein...


----------



## Samuel72 (22. Jun 2011)

Landei hat gesagt.:


> [c]LinkedList[/c] ist fast immer [c]ArrayList[/c] unterlegen.


Danke für den Hinweis. Du hast natürlich recht - insbesondere, da 
	
	
	
	





```
Collections.sort
```
 die LinkedList sowieso in ein Array auslagert. Bisher habe ich immer LinkedList verwendet - ich sollte mir wohl überlegen, wann ein Umsteigen sinnvoll ist.


----------



## Marco13 (23. Jun 2011)

Im Idealfall muss nur eine Zeile von 
List<X> list = new LinkedList<X>();
in
List<X> list = new ArrayList<X>();
geändert werden


----------

