Operatoren Programm verlässt Do-While Schleife nicht - Warum?

Steen

Mitglied
Guten Tag zusammen,

leider habe ich ein kleines Problem mit dem Beenden meiner Do-While-Schleife.
Und zwar schreibe ich gerade an einem Programm, das mit Hilfe einer Funktion, die berechnet, ob eine Zahl fröhlich ist, alle fröhlichen Zahlen von 1 bis 1500 ausgibt.
Fröhliche Zahlen sind so definiert, dass sich ihre Ziffern so lange quadrieren und aufaddieren lassen, bis 1 entsteht. Alle anderen Zahlen geraten in einen Zyklus der Zahlen 20, 4, 16, 37, 58, 89, 145, 42.

Leider verlässt mein Programm die Schleife, deren Abbruchbedingung in Zeile 11 liegt, nicht, obwohl das Ergebnis eindeutig entweder 1 oder 20 wird, das hab ich per Output direkt vor dem Check der Bedingung ausprobiert und per Hand berechnet.

Java:
public class FröhlicheZahlen {
	static int Happy (int Zahl){
		int sum = Zahl;
		do{
			Zahl = sum;
			sum = 0;
			do{
				sum = sum + (Zahl%10 * Zahl%10);
				Zahl = Zahl/10;
				} while (Zahl != 0);
		}while ((sum != 1)|(sum != 20));
		return sum;
	}
	public static void main(String[] args) {
		for(int i=1; i<=1500; i++){
			if(Happy (i)==1)
				Out.println(i);
		}
	}
}

Ich hoffe, ihr könnt mir helfen.

Liebe Grüße,
Steen
 
Java:
while ((sum != 1)|(sum != 20));

Tue dies solange

summe nicht gleich 1 ist
ODER
summe nicht gleich 20 ist



Es muss also summe = 1 UND summe = 20 sein, damit die schleife beendet.
 
Java:
(sum != 1) | (sum != 20)
ist ein bitweises OR der beiden Zwischenergebnisse, logisches OR wäre || (logisches AND wäre &&).

Macht in Deinem Fall aber keinen Unterschied weil bitweises OR/AND auch mit denn Bool-Operatoren klappt (aber nur mal für die Zukunft).

Deine Abbruchbedingung stimmt nicht so ganz mit dem Algorithmus überein. Es ist ja nicht gesagt, dass das Ergebnis irgendwann nach 1 oder 20 konvergiert. Ich habs mal ausprobiert und "Happy(66)" konvergiert sehr schnell nach 5.

Bernd
 
Hmm klar, danke sehr, da hätte ich auch selbst drauf kommen sollen..

Ich habe es jetzt mit
Code:
while ((sum = !1)&&(sum = !20));
ersetzt, dann müsste das ja funktionieren.

Das Programm läuft trotzdem nicht wie es sollte und mir gehen echt langsam die Ideen aus, wenn ich mein Programm per Stift und Zettel verfolge funktioniert das.. Hat jemand von euch eventuell eine Idee?

Java:
Deine Abbruchbedingung stimmt nicht so ganz mit dem Algorithmus überein. Es ist ja nicht gesagt, dass das Ergebnis irgendwann nach 1 oder 20 konvergiert. Ich habs mal ausprobiert und "Happy(66)" konvergiert sehr schnell nach 5.[/QUOTE]
Hallo Bernd, eigentlich müsste jede Zahl 1 oder 20 erreichen, wenn ich keinen Mist zusammengeschrieben habe. Danach sieht es jedoch gerade leider aus..



Danke für die Unterstützung!
Liebe Grüße,
Steen
 
Zuletzt bearbeitet:
Rechnen wir das ganze mal per Hand mit der 5 durch:

Java:
public class FröhlicheZahlen {
    static int Happy (int Zahl){           // Zahl = 5
        int sum = Zahl;                       // sum = 5
        do{
            Zahl = sum;
            sum = 0;                           // sum = 0
            do{
                sum = sum + (Zahl%10 * Zahl%10);  // sum = 0 + ( 5 * 5) = 25
                Zahl = Zahl/10;                             // Zahl = 0
                } while (Zahl != 0);                        // Bedingung nicht erfüllt, verlasse Schleife
        }while ((sum != 1)&&(sum != 20));            // Wieder nach oben mit sum=25
        return sum;
    }
    public static void main(String[] args) {
        for(int i=1; i<=1500; i++){
            if(Happy (i)==1)
                Out.println(i);
        }
    }
}

Mit 25:

Java:
public class FröhlicheZahlen {
    static int Happy (int Zahl){           
        int sum = Zahl;                      
        do{
            Zahl = sum;                       // Zahl = 25                                         
            sum = 0;                           // sum = 0
            do{
                sum = sum + (Zahl%10 * Zahl%10);  // sum = 0 + ( 5 * 5) = 25        2. Runde: sum = 25 + ( 2 * 2) =29
                Zahl = Zahl/10;                             // Zahl = 2                             2. Runde: Zahl = 0
                } while (Zahl != 0);                        // Bedingung erfüllt                  2. Runde Raus aus der Schleife mit sum = 29
        }while ((sum != 1)&&(sum != 20));
        return sum;
    }
    public static void main(String[] args) {
        for(int i=1; i<=1500; i++){
            if(Happy (i)==1)
                Out.println(i);
        }
    }
}


Also wenn ich das ganze selbst nachrechne, sollte das nicht bei 5 bleiben, tut es jedoch leider im Programm..

Grüße,
Steen
 
Java:
sum = sum + (Zahl%10 * Zahl%10);  // sum = 0 + ( 5 * 5) = 25        2. Runde: sum = 25 + ( 2 * 2) =29

Das denkst auch nur Du (und ich).

In wirklichkeit ist MOD (%) niedriger angesiedelt als "*" und daher rechnet er "sum + (Zahl * 10 MOD 10 MOD 10)

Also Klammern:

Java:
sum = sum + ((Zahl % 10) * (Zahl % 10));

Bernd
 
Java:
sum = sum + (Zahl%10 * Zahl%10);  // sum = 0 + ( 5 * 5) = 25        2. Runde: sum = 25 + ( 2 * 2) =29

Das denkst auch nur Du (und ich).

In wirklichkeit ist MOD (%) niedriger angesiedelt als "*" und daher rechnet er "sum + (Zahl * 10 MOD 10 MOD 10)

Also Klammern:

Java:
sum = sum + ((Zahl % 10) * (Zahl % 10));

Bernd

Ach danke, du bist ein Schatz. 🙂
Darauf wäre ich im Leben nicht gekommen, obwohl ich gerade erst in einer Vorlesung mit so einer Operationsreihenofolge konfrontiert wurde..
Jetzt funktioniert das Programm.

Dankender Gruß,
Steen
 
Bernd Hohmann;969969In wirklichkeit ist MOD (%) niedriger angesiedelt als "*" und daher rechnet er "sum + (Zahl * 10 MOD 10 MOD 10) Bernd[/QUOTE hat gesagt.:
stimmt so immer noch nicht ganz :

Zahl%10 * Zahl%10 ergibt : Zahl MOD (10*Zahl) MOD 10 ... da zwar die multiplikation zu erst durchgeführt wird ... aber die reihenfolge der einzelnen terme wird beibehalten ... oder hast du in mathe was anderes gelernt ?
 
Java:
(sum != 1) | (sum != 20)
ist ein bitweises OR der beiden Zwischenergebnisse, logisches OR wäre || (logisches AND wäre &&).

Macht in Deinem Fall aber keinen Unterschied weil bitweises OR/AND auch mit denn Bool-Operatoren klappt (aber nur mal für die Zukunft).

Deine Abbruchbedingung stimmt nicht so ganz mit dem Algorithmus überein. Es ist ja nicht gesagt, dass das Ergebnis irgendwann nach 1 oder 20 konvergiert. Ich habs mal ausprobiert und "Happy(66)" konvergiert sehr schnell nach 5.

Bernd

Sorry, das kann man so nicht stehen lassen. Richtig ist, dass
Code:
|
für numerische Typen das bitweise Oder ist. Für boolsche Operatoren ist es das boolsche Oder, genau wie
Code:
||
. Der Unterschied zwischen den beiden Versionen ist der, dass letzterer ein "Kurzschluss-Operator" ist, d.h. die Auswertung wird abgebrochen, wenn das Ergebnis feststeht. Vergleiche:

Java:
//der zweite Teilausdruck wird immer ausgewertet -> NPE bei null
if (s == null | s.equals("")) { ... } 

//der zweite Teilausdruck wird nicht ausgewertet, wenn der erste wahr ist -> keine NPE bei null
if (s == null || s.equals("")) { ... }

Analoges gilt für
Code:
&
und
Code:
&&
.

Java:
//der zweite Teilausdruck wird immer ausgewertet -> NPE bei null
if (s != null & s.equals("foo")) { ... } 

//der zweite Teilausdruck wird nicht ausgewertet, wenn der erste falsch ist -> keine NPE bei null
if (s != null && s.equals("foo")) { ... }


Nun fragt sich, wann
Code:
|
und
Code:
&
überhaupt für Booleans verwendet werden sollte. Die Antwort ist: So gut wie nie. Es kann sinnvoll sein, wenn der zweite Teilausdruck nicht nur boolean zurückliefert, sonder auch einen Seiteneffekt ausführt (z.B. Werte in einem Objekt verändert, eine Log-Ausgabe schreibt u.s.w.), aber gewöhnlich ist es eine schlechte Idee, so eine Methode überhaupt zu schreiben. Außerdem ist zu befürchten, dass selbst gestandene Programmierer dieses Detail nicht kennen, oder im aktuellen Code übersehen.
 
Zuletzt bearbeitet:
@Landei
leider falsch ...

| und & bleiben BIT-WISE ... es macht keinen unterschied ob man damit INTs oder BOOLs bearbeitet ... denn java stellt intern auch TRUE und FALSE nur mit INTs dar ... nämlich mit 0 und 1 ... siehe dazu source von Boolean
das was du mit dem unterschied meinst ist lediglich dadurch begründed das für die BIT-WISE auswertung ALLE teil-terme aufgelöst werden müssen um das richtige ergbenis zu liefern da hier das ergebnis "berechnet" wird ... was den logischen OP angeht ist es nun mal so das entsprechend der verknüpfung bereits vorher das richtige ergebnis geliefert werden kann und so die restlichen teilterme nicht mehr notwendig sind ...
 
Sorry, das kann man so nicht stehen lassen.

Das stimmt schon. | und & werden Binär ausgwertet (weil "boolean" ein verkapptes "int" ist), || und && hingegen werden logisch verglichen - jedenfalls ist das der aktuelle Stand der JVM.

Ich hab mal ein Beispiel gebastelt und das durch einen Decompiler geschubst (die dämlichen Zuweisungen brauch ich damit das der Compiler nicht wegoptimiert).

Java:
public class Test {
  public static void main(String str[]) {

     boolean TRUE = true;
     boolean FALSE = false;

     boolean a = TRUE | FALSE;
     boolean b = TRUE || FALSE;
     boolean c = TRUE & FALSE;
     boolean d = TRUE && FALSE;

     boolean e = blnTRUE() | blnFALSE();
     boolean f = blnTRUE() || blnFALSE();

  }

  public static boolean blnTRUE() { 
     int a=1, b=1; 
     return a==b;
  }

  public static boolean blnFALSE() {
     int a=1, b=0;
     return a==b;
  }

}

Ergebnis im Decompiler:

Java:
public class Test
{
  public static void main(String[] paramArrayOfString)
  {
    int i = 1;
    int j = 0;

    int k = i | j;
    int m = (i != 0) || (j != 0) ? 1 : 0;
    int n = i & j;
    int i1 = (i != 0) && (j != 0) ? 1 : 0;

    boolean bool = blnTRUE() | blnFALSE();
    int i2 = (blnTRUE()) || (blnFALSE()) ? 1 : 0;
  }

  public static boolean blnTRUE()
  {
    int i = 1; int j = 1;
    return i == j;
  }

  public static boolean blnFALSE() {
    int i = 1; int j = 0;
    return i == j;
  }
}

Interessant finde ich die Ergebnisse für "blnTRUE() OR blnFALSE()": mit "|" kommt dort ein "boolean" heraus, für "||" hingegen ein "int" - ich hätte das umgekehrt erwartet.

Auch bisserl drollig: "false" ist in den mir bekannten Sprachen "0", "not false = -1". Java macht da einen Sonderweg:


Java:
     boolean FALSE = false;
     boolean dummy = !FALSE;
(decompiler)
    int j = 0;
    int k = j == 0 ? 1 : 0;

Bernd
 
Zuletzt bearbeitet:
denn java stellt intern auch TRUE und FALSE nur mit INTs dar
(Hervorhebung von mir.) Jein. So verwendet z.B. die Oracle-JVM zur Speicherung von boolean-Arrays quasi byte-Arrays. Bei Berechnungen wiederum könnte (nicht sicher) dieselbe JVM dagegen gerne auch int verwenden, vor allem, wenn das schon so in der class-Datei drinstehen sollte.

Überhaupt soll es mich nicht wundern, wenn auf dem Stack (also bei den tatsächlichen Berechnungen bzw. lokalen Variablen) tendenziell int zum Einsatz kommt, während auf dem Heap (d.h. zur Speicherung von Ergebnissen bzw. bei statischen und nicht-statischen Fields) eher byte verwendet wird.

Man sollte also in solchen Fragen mindestens drei verschiedene Ebenen auseinanderhalten: Die Java-Sprachspezifikation, die JVM-Spezifikation und die konkrete Implementierung einer JVM.

Ark
 
naja ... der Java-Code selbst verwendet grundsätzlich INT ...
in wie weit dies intiligent von der jeweiligen VM intern umgesetzt wird müsste man natürlich in deren implementierung nachlesen ... aber fakt ist : PRIMITIVE datentypen werden ALLE java-intern numerisch behandelt ... weshalb | und & das ergebnis "errechnen" was nun mal implizit dazu führt das vorher alle terme aufgelöst werden müssen ... das hat überhaupt nichts mit bool vs int zu tun sondern mit der arbeitsweise der bit-wise operatoren selbst ...
 

Zurück
Oben