Collatz-Folgen

Hallo!
Ich bin völliger Anfänger, was das Programmieren angeht und habe die Aufgabe, ein Java Programm zu schreiben, das für alle Zahlen bis 40 die Anzahl der Collatz-Folgen und den jeweils höchsten Wert ausgibt. Jetzt habe ich allerdings das Problem, dass wenn ich mein Programm ausführe dieses erst bei der Zahl 25 beginnt und die Anzahl der Collatz-Folgen sowie die Werte nicht stimmen.
Wenn probeweise den Zahlenwert von 40 auf 10 runtersetze funktioniert es und die Werte stimmen auch.
Wo habe ich hier meinen Fehler? (Wie gesagt ich bin Anfänger :D)


 public class Collatz4
{
   public static void main (String args[])
   {
       int n;
       int k;
       int max;
       int zahl;
       k = 0;
       max = 0;
       zahl = 2;
       
      while (zahl <= 40)
      {
               n = zahl;
               while (n > 1)
               {
                       if(n%2 == 0)
                       {
                           n = n/2;
                           k = k + 1;
                        }
                        else
                        {
                            n = n * 3 + 1; 
                            k = k + 1;
                        }
                        if (n > max)
                        {
                            max = n;
                        }
      
      
               }
                   
                    
             System.out.println("");     
             System.out.println("Die Collatz-Folge von:" + zahl + ". Hat die Laenge: " + k);
             System.out.println("Der groesste zwischendurch erreichte Wert ist: " + max);
             zahl = ++zahl;
      }
   }
}



Versuche doch mal dein Programm zu vereinfachen.

Starte mal mit einer Methode die den Nachfolger zu einer Zahl ermittelt. Das ist wohl der Grundbaustein.

  ???
}```

Dann kannst du eine Funktion erstellen die die Länge einer Collatzfolge berechnet.

public static int laengeCollatzfolge(int start) {
int laenge = 0;
int aktuell = start;
do {
aktuell = nachfolgerVon(aktuell);
laenge++;
} while(aktuell != 1);
return laenge;
}


Für das maximum hilft sicher eine max-Funktion

public int max(int a,int b) {
return ???
}


Um nun das Maximum zu berechnen wird eine Funktion ähnlich der zur Berechnung der Länge helfen. Anstatt dort aber mit länge =1 fängt man wohl einfacher mit max = 0 an. Anstatt laenge aufzuadieren kann man ein neues Maximum mit max = max(max, aktuell) herausbekommen.


Mit den kleinen einzelnen Funktionen kannst du das ganze viel einfacher überprüfen und testen, als in einem großen ganzen.
Wenn das dann passt, dann kannst du das ganze in einer Schleife über alle zahlen von 1 - 40 durchlaufen lassen

Nu, aber für Maximum und Länge das Ding zweimal durchnudeln ?
Ich hätt es halt etwa so gemacht

richtig ?
[spoiler]```public class Collatz {

public static void main(String[] args) {
	for (int i =  1 ; i < 41; i++){
		int[] result = chkLenMaxCollatz(i);
		System.out.printf("Startzahl %5d Länge %5d Max %5d %n",i,result[0],result[1]);
	}

}
private static int nachfolger(int aktuell){
	return (aktuell % 2 == 0? aktuell/2 : 3 * aktuell + 1);
}
private static int max(int a, int b){
	return (a > b ? a : b);
}
private static int[] chkLenMaxCollatz(int start){
	int colMax = start;
	int colLen = 0;
	int i = start;
	if (i>0){
		do {
			i = nachfolger(i);
			colMax = max(colMax,i); 
			colLen++;
		} while (i != 1);
	}
	return new int[] {colLen,colMax};
}

}
//Startzahl 1 Länge 3 Max 4
//Startzahl 2 Länge 1 Max 2
//Startzahl 3 Länge 7 Max 16
//Startzahl 4 Länge 2 Max 4
//Startzahl 5 Länge 5 Max 16
//Startzahl 6 Länge 8 Max 16
//Startzahl 7 Länge 16 Max 52
//Startzahl 8 Länge 3 Max 8
//Startzahl 9 Länge 19 Max 52
//Startzahl 10 Länge 6 Max 16
//Startzahl 11 Länge 14 Max 52
//Startzahl 12 Länge 9 Max 16
//Startzahl 13 Länge 9 Max 40
//Startzahl 14 Länge 17 Max 52
//Startzahl 15 Länge 17 Max 160
//Startzahl 16 Länge 4 Max 16
//Startzahl 17 Länge 12 Max 52
//Startzahl 18 Länge 20 Max 52
//Startzahl 19 Länge 20 Max 88
//Startzahl 20 Länge 7 Max 20
//Startzahl 21 Länge 7 Max 64
//Startzahl 22 Länge 15 Max 52
//Startzahl 23 Länge 15 Max 160
//Startzahl 24 Länge 10 Max 24
//Startzahl 25 Länge 23 Max 88
//Startzahl 26 Länge 10 Max 40
//Startzahl 27 Länge 111 Max 9232
//Startzahl 28 Länge 18 Max 52
//Startzahl 29 Länge 18 Max 88
//Startzahl 30 Länge 18 Max 160
//Startzahl 31 Länge 106 Max 9232
//Startzahl 32 Länge 5 Max 32
//Startzahl 33 Länge 26 Max 100
//Startzahl 34 Länge 13 Max 52
//Startzahl 35 Länge 13 Max 160
//Startzahl 36 Länge 21 Max 52
//Startzahl 37 Länge 21 Max 112
//Startzahl 38 Länge 21 Max 88
//Startzahl 39 Länge 34 Max 304
//Startzahl 40 Länge 8 Max 40 ```[/spoiler]

Jein. Aber in anbetracht der Aufgabenstellung wäre dies zumindest mal ein Anfang.

Was ich nicht schön finde ist die Methode chkLenMaxCollatz. Dort direkt Elemente einzubauen, die von der eigentlichen Berechnung (die Collatz-Folge) ablenken finde ich nicht so toll.

Ich hab mal ein Beispiel gemacht, wie ich das ganze lösen würde.

    private static abstract class Fun {
	public void onStart(int i) {}
	public abstract void exec(int i);
	public abstract void printResult();
    }
    
    private static class Count extends Fun {
	private int counter = 1;
	public void exec(int i) {
	    counter++;
	}
	public void printResult() {
	    System.out.println("länge " + counter);
	}
    }
    
    private static class Max extends Fun {
	private int max;
	public void onStart(int i) {
	    max = i;
	}
	public void exec(int i) {
	    max = (max < i)? i: max;
	}
	public void printResult() {
	    System.out.println("max " + max);
	}
    }
    
    private static class Print extends Fun {
	public void onStart(int i) {
	    System.out.println("=== Collatz für " + i + " ===");
	    System.out.print(i+"; ");
	}
	public void exec(int i) {
	    System.out.print(i+"; ");
	}
	public void printResult() {
	    System.out.println();
	}
    }
    
    private static class MFun extends Fun {
	Fun[] funs;
	public MFun(Fun...funs){
	    this.funs = funs;
	}
	public void onStart(int i){
	    for(Fun f:funs){
		f.onStart(i);
	    }
	}
	public void exec(int i){
	    for(Fun f:funs){
		f.exec(i);
	    }
	}
	public void printResult(){
	    for(Fun f:funs){
		f.printResult();
	    }
	}
    }
    public static void main(String[] args) {
        for (int i =  1 ; i < 41; i++){
	    Fun f = new MFun(new Print(),new Count(),new Max());
            chkLenMaxCollatz(i, f);
	    f.printResult();
        }
	
    }
    private static int nachfolger(int aktuell){
        return (aktuell % 2 == 0? aktuell/2 : 3 * aktuell + 1);
    }
    
    private static void chkLenMaxCollatz(int start, Fun f){
        int i = start;
        if (i>0){
	    f.onStart(i);
            do {
	        i = nachfolger(i);
		f.exec(i);
            } while (i != 1);
        }
    }
}```

Das ganze ist aber wohl für jemanden der sich gerade mit dieser Aufgabe beschäftigt ein wenig Overkill. Zeigt aber wie das ganze erweitert werden kann, wenn man zusätzliche Berechnungen durchführen wollte.

Andererseits ist es mit dem Mehrmaligen durchlaufen auch so, daß selbst du für 20 zum Beispiel mehrmals die Collatzfolge berechnest. Für den Startwert 20, 25, 26, 27, 28, 29, 30 oder 40 zum Beispiel.
Vernünftigerweise würde man hier mit einer Map arbeiten.

oh, äh… naja Map… und oh nee
dann vielleicht doch lieber in der Richtung ?

oder ?
[spoiler]```public class Collatz {

public static void main(String[] args) {
	for (int i =  1 ; i < 41; i++){
		CollatzFolge folge = new CollatzFolge(i);
		System.out.printf("Startzahl %5d Länge %5d Max %5d %n",i,folge.getMax(),folge.getLen());
	}

}

}

class CollatzFolge{
private int colMax;
private int colLen;
CollatzFolge(int start){
colMax = start;
colLen = 0;
int i = start;
if (i>0){
do {
i = nachfolger(i);
colMax = max(colMax,i);
colLen++;
} while (i != 1);
}
}
private static int nachfolger(int aktuell){
return (aktuell % 2 == 0? aktuell/2 : 3 * aktuell + 1);
}
private static int max(int a, int b){
return (a > b ? a : b);
}
public int getMax(){
return colMax;
}
public int getLen(){
return colLen;
}
}```[/spoiler]

Nee, das ist nicht so ganz meins. Wenn dann eher so.

    int max();
    int length();
    Collatz getFollower();
}```

Dann einen Endpunkt Definieren, hier z.B. die 1. Weil ich auch rekursion Nutze und da braucht es ein Rekursionsende.

```public class ONE implements Collatz{

    @Override
    public int max() {
        return 1;
    }

    @Override
    public int length() {
        return 0;
    }

    @Override
    public Collatz getFollower() {
        return Collatzs.getCollatz(4);
    }

    public String toString(){
        return "1";
    }
}

Und eine CollatzImpl


    private final int value;

    private int max = -1;

    public CollatzImpl(int value) {
        this.value = value;
    }

    @Override
    public int max() {
        if(max<0) {
            max = (getFollower().max()<value)? value: getFollower().max();
        }
        return max;
    }

    @Override
    public int length() {
        return 1 + getFollower().length();
    }

    @Override
    public Collatz getFollower() {
        return Collatzs.getCollatz(nachfolger());

    }

    private int nachfolger(){
        return (value % 2 == 0? value/2 : 3 * value + 1);
    }

    @Override
    public String toString() {
        return value+"; "+getFollower();
    }
}

Und das ganze Zusammenbauen in einer Collatzs-Klasse in der schon verwendete Collatzs in einer Map gecacht werden.

import java.util.Map;

public class Collatzs {

    private static Map<Integer, Collatz> cache = new HashMap<Integer, Collatz>();

    public static Collatz getCollatz(int i) {
        if(i == 1) {
            return new ONE();
        }
        Collatz c = cache.get(i);
        if(c == null){
            c = new CollatzImpl(i);
            cache.put(i,c);
        }
        return c;
    }

    public static void main(String[] args) {
        for(int i = 1; i<= 40; i++){
            Collatz folge = getCollatz(i);
            System.out.printf("Startzahl %5d Länge %5d Max %5d [%s]%n",i,folge.length(),folge.max(),folge);
        }
    }
}```

Wie schonmal erwähnt, die Collatzfolge ab 20 taucht öfters auf. Wenn du mit einer Schleife das Maximum berechnest, dann berechnest du zig mal das maximum der Folge von 20, was immer 20 ist.

Bei dir Sind 2 Variablen, die du nicht zurücksetzt:

In while(zahl<=40){…} musst du jedes mal die Variablen k und max wieder auf 0 setzen, da du sonst das Maximum/ die Schritte von der vorherigen Berechnung mit dabei hast.

Dafür das der erst bei 25 anfängt sehe ich aber keinen Grund.