Simulation einer schriftlichen Addition

Hallo zusammen.

Ich beschäftige mich seit Kurzem mit JAVA und bin ganz klar ein Anfänger. Daher bitte ich um etwas Verständnis. Eine interessante Aufgabe ist mir von jemandem vorgeschlagen worden, aber leider konnte ich diese nicht perfekt erfüllen. Diese ist eine Simulation einer schriftlichen Addition in JAVA. Der Code lässt sich klar kompilieren, jedoch ist die Addition manchmal falsch. „Manchmal“ tönt naürlich komisch, aber das Programm gibt oft das Richtige aus, aber manchmal kommt die falsche Antwort aus. Wie die schriftliche Addition funktioniert muss ich nicht erklären…demnächst poste ich mal Code. Wäre super, wenn mir jemand erklären könnte, wo mein Fehler liegt.

import java.util.*;

public class AdditionArray {
    public static int [] add (int []a, int []b){
        // Voraussetzung ist, dass a und b die gleiche Länge haben
        int [] c = new int [a.length]; //Feld für Ergebnis
        int i; //Schleifenzähler
        int  s; //Summe der aktuell berechneten Stelle
        int uebertrag = 0; //Übertrag für die nächste Stelle
        
        for (i = a.length-1; i >= 0; i--)
        {
            s = a** + b** + uebertrag;
            c** = s % 10;
            uebertrag = s/10;
            c[0]=a[0]+b[0]+uebertrag;
        }
       
        return c;
    }
    public static void main (String [] args){
        int l;
        Scanner keyboard = new Scanner(System.in); //Tastatureingabe
        System.out.println("Wie viele Stellen sollten die zu addierenden Zahlen besitzen? - ");
        l = keyboard.nextInt();
        keyboard.nextLine();
        
        int [] a = new int [l]; //1.Summand
        int [] b = new int [l]; //2.Summand
        int [] zahl3; //Ergebnis      
 
        //die beiden Summanden mit zufälligen Werten belegen
        Random r= new java.util.Random ();
        for (int i = 0; i < l; i++){
            a**= r.nextInt (10);
            b**= r.nextInt (10);
        }
        //Ergebnis berechnen und ausgeben
        zahl3 = add (a, b);
        System.out.printf("1.Summand: " + Arrays.toString(a));
        System.out.printf("
+");
        System.out.printf ("
2.Summand: " + Arrays.toString(b));
        System.out.printf("
-----------------------------------");
        System.out.printf ("
Die Summe: "+ Arrays.toString(zahl3));
        System.exit(0);
    }

}

Ich habe schon einige Sachen ausprobieren, aber keiner von meinen Lösungsvorschlägen konnte dieses Problem lösen. Der Fehler ist natürlich ganz klar in der Methode, und zwar in der Zeile

c[0]=a[0]+b[0]+uebertrag;

Die Ausgabe sieht z.B folgendermassen aus:
Wie viele Stellen sollten die zu addierenden Zahlen besitzen? -
5
1.Summand: [6, 4, 4, 7, 3]
+
2.Summand: [7, 1, 0, 9, 9]

Die Summe: [14, 5, 5, 7, 2]

Das ist offensichtlich falsch, aber oft gibt das Programm das Richtige aus. Alle bis zur ersten Stelle sind ja richtig. Ich verstehe nicht ganz wieso das Programm einen Übertrag addiert, obwohl keiner existiert. Bitte um Hilfe! Ich bin ein echter Anfäger, und würde gerne wissen, wo der Fehler liegt. Danke vielmals!! :slight_smile:

Die genannte Zeile brauchst du gar nicht. Dafür hinter der Schleife prüfen, ob uebertrag noch etwas enthält, und dann c in ein um eins größeres Ergebnis-Array kopieren, und uebertrag an die erste Stelle packen.

der zweimal vorgetragene Punkt ‚Programm rechnet oft richtig, aber manchmal falsch‘ ist etwas wirr,
natürlich rechnet es richtig wenn das schon identifizierte Problem, Auftreten eines Übertrags an einer bestimmten Stelle, nicht auftritt

besser schlicht darauf konzentrieren dass es mit bestimmen Eingaben falsch rechnet, fertig,
ein Beispiel hast du auch angegeben, gut, vielleicht noch etwas unnötig lang und vor allem ungünstig nicht im Code enthalten,

stattdessen komplizierte Konsolen-Eingabe inklusive Random, so kann niemand das Programm direkt zur Kontrolle/ Änderung ausführen,
besser eine Main-Methode gleich passend zum besprochenen Beispiel,
ich gehe mal auf noch ein einfacheres runter:
1.Summand: [7, 2]
+
2.Summand: [8, 3]

Die Summe: [16, 5]

Code in main-Methode dazu:

        int[] a = new int[]  {7, 2};
        int[] b = new int[]  {8, 3};
        int[] zahl3;

        zahl3 = add(a, b);

die verdächtige Code-Zeile hast du auch schon selber gefunden, warum da Ende?
was das Programm macht, sollte, wenn noch nicht im Kopf komplett nachzuvollziehen, mit Ausgaben bestätigt werden,

da hast du schon den Vorteil von Programmiersprachen, von System.out.println(), Kontrolle über Ablauf usw.,
beste Voraussetzungen während Chemiker nur auf die Farbe der Flamme schauen können :wink: ,
also nutze auch diesen Luxus


Vergleich mit der schriftlichen Addition ist ein guter Aufhangpunkt,
wenn man dort an der i-ten Stelle etwas berechnet, führt man da dann auch in JEDER Runde c[0]=a[0]+b[0]+uebertrag; aus?
schreibt man immer wieder an die 0-te/ höchste Ergebnisstelle?

diese Zeile ergibt einfach keinen Sinn,
maximal nach Schleifenende, also dort nur EINMAL überlegen, ob evtl. übrig gebliebender Übertrag weiter zu verarbeiten ist,
aber das dann auch überlegt:
wie sieht Ergebnis ohne Zusatzschritt aus (Array c + evtl. Übertrag?), welches Ergebnis ist gewünscht, wie dies halbwegs sinnvoll zu erreichen?

Dieser Code war nicht meine erste Variante. An der 0-ten Stelle schaut man natürlich, ob dieser ein Übertrag besitzt und schreibt diesen eine Stelle weiter links hin.
Ich dachte, das Array vergrössern, c[0] = uebertrag; zu schreiben, und somit sollte das Programm die richtige Lösung ausgeben. Leider war diese Überlegung falsch. die Ausgabe sieht mit den Arrays

int [] a = new int []{1,1}; //1.Summand
int [] b = new int[] {1,9}; //2.Summand
int [] zahl3; //Ergebnis ```

folgendermassen aus:

1.Summand: [8, 1]
+
2.Summand: [4, 9]
-----------------------------------
Die Summe: [1, 0, 0]

ist wiederum nicht korrekt. Meine Überlegungsweise ist momentan ganz falsch, das sehe ich ein. Ich  verstehe noch nicht ganz, wie der Computer diesen Code versteht. Beschäftige mich nur seit einigen Wochen mit JAVA und Programmiersprachen im Allgemeinen. Fehlt an Erfahrung, denke ich. Auf jeden Fall vielen Dank für die Hilfe; wäre natürlich super, wenn ihr weiter helfen könntet! Danke.

*** Edit ***

Landei, ich weiss nicht ganz wie ich vor der Schleife überprüfen soll, ob uebertrag noch etwas beinhaltet. Ich nehme an, ```c[0] = uebertrag``` ist das, was Sie meinten. Das funktioniert nicht, wie im obigen Post. Ich denke, ich verstehe Ihren Lösungsvorschlag nicht ganz. Danke für die Hilfe!

Ich meinte sowas:

    public static int[] add(int[] a, int[] b) {
        // Voraussetzung ist, dass a und b die gleiche Länge haben
        int[] c = new int[a.length]; //Feld für Ergebnis
        int i; //Schleifenzähler
        int s; //Summe der aktuell berechneten Stelle
        int uebertrag = 0; //Übertrag für die nächste Stelle

        for (i = a.length - 1; i >= 0; i--) {
            s = a** + b** + uebertrag;
            c** = s % 10;
            uebertrag = s / 10;
        }
        if (uebertrag > 0 ) {
            int[] d = new int[c.length + 1];
            System.arraycopy(c,0,d,1, c.length);
            d[0] = uebertrag;
            return d;
        } else {
            return c;
        }
    }

Statt mit arraycopy kann man die Werte auch mit einer Schleife von c nach d kopieren, aber es ist in jedem Fall nützlich, diese Methode zu kennen.

Übrigens braucht man hier niemanden zu siezen (da fühle ich mich mit meinen zarten 42 Jahren ja gleich richtig alt :grampa:)

Update:

Auch wenn die Aufgabe gut geeignet ist, den Umgang mit Arrays und Schleifen zu üben, ist ein Array für diese Aufgabe keine geeignete Datenstruktur. Besser wäre eine Liste oder ein Stack, aber man kann sich sowas auch selbst schreiben. Der folgende Code soll nur eine Anregung sein, wie so etwas gehen würde. Ich habe zwar versucht, alles möglichst einfach zu halten (und deshalb auch unschöne Konstrukte wie null verwendet), aber es macht nichts, wenn du nicht alles auf Anhieb verstehst.

public class Zahl {

    private final int ziffer;
    private final Zahl davor;

    private Zahl(Zahl davor, int ziffer) {
        this.davor = davor;
        this.ziffer = ziffer;
    }

    @Override
    public String toString() {
        if(davor != null) {
            return davor.toString() + ziffer;
        } else {
            return Integer.toString(ziffer);
        }
    }

    public static Zahl erzeugeZahl(int ... ziffern) {
        Zahl zahl = null;
        for(int ziffer : ziffern) {
            if (zahl == null && ziffer == 0)
                continue; //führende Nullen überspringen
            zahl = new Zahl(zahl, ziffer);
        }
        if (zahl == null) {
            return new Zahl(null, 0); //Sonderbehandlung 0
        }  
        return zahl;
    }

    public static Zahl addiere(Zahl eins, Zahl zwei) {
        if (eins == null) {
            return zwei;
        }
        if (zwei == null) {
            return eins;
        }
        int summe = eins.ziffer + zwei.ziffer;
        int ziffer = summe % 10;
        int uebertrag = summe  / 10;

        Zahl davor = addiere(eins.davor, zwei.davor);
        if (uebertrag > 0) {
            davor = addiere(davor, erzeugeZahl(uebertrag));
        }
        return new Zahl(davor, ziffer);
    }

    public static void main(String[] args) {
        Zahl z1 = erzeugeZahl(1,4,2,4,2);
        Zahl z2 = erzeugeZahl(9,5,3,0,2,6);

        System.out.format("%s + %s = %s", z1, z2, addiere(z1, z2));
    }
}

Der Code ist jetzt nicht kürzer, aber es geht viel weniger um Schleifenindizes und Arraylängen, sondern mehr darum, was eine Zahl eigentlich ist, und was es bedeutet, zwei Zahlen zu addieren. Die Grundidee ist, dass eine Zahl aus ihrer letzten Ziffer und einer “Zahl davor” besteht, diese wiederum aus der letzten Ziffer (die Zehner der Ursprungszahl) und der Zahl davor u.s.w., bis das Ende dieser Kette durch eine null signalisiert wird. Das macht es sehr leicht, auch mit unterschiedlich langen Zahlen umzugehen.

Danke vielmals für den Lösungsansatz! Ich werden den Code sicherlich sorgfältig nachgehen. Wie gesagt bin ich ein klarer Noob in diesem Bereich, vielen Dank für die Hilfe und Motivation! Hat noch einiges in deinem Update, das ich nicht verstehe, werde mich aber sicherlich in der Literatur darüber schlau machen. Vielen Dank! Die arraycopy Methode kannte ich nicht, aber die if Bedingung der Methode verstehe ich. Wäre aber nie auf eine solche Lösung gekommen, denke ich. Muss noch die Denkweise eines Programmierers üben, das bringen leider die online-Tutorials einem nicht bei. Naja, auf jeden Fall, vielen Dank!

Grüsse
Klodel

Vielleicht noch 'ne kurze Frage: wie kann man in der Ausgabe die Klammern und Kommata wegbekommen? Ich habe folgenden Ansatz versucht, jedoch führte es zu keiner Lösung:

String format = Arrays.toString(a);
format.replace(",","");
format.replace("[","");
format.replace("]","");
format.trim();

Mit einem normalen String funktioniert ja die replace Methode problemlos, aber ich denke, hier ist es ein wenig anders. Muss jedoch zugeben, mit diesem kleinen Nebenproblem habe ich mich nicht intensiv auseinandergesetzt. Hab noch etwas über Stringbuilder gelesen, aber das war etwas zu kompliziert für meinen Wissensstand :verzweifel:

*** Edit ***

Vielen Dank für den Lösungsansatz! Die Methdodearraycopy kannte ich nicht, abr ansonsten macht die Methode Sinn. Vielen Dank für die Hilfe und Motivation. Vom Update verstehe ich vieles nicht, aber ich werde sicherlich sorgfältig durchgehen und mich auch in der Literatur schlau machen! Danke. Bin noch ein ziemlicher Anfänger, die Denkweise eines Programmierers bringen einem die Online Tutorials nicht bei. Muss noch mehr üben. Danke vielmals!

Strings sind immutable, d.h. unveränderlich. Sowas wie
format.replace(",","");
verändert also nicht den “format”-String. Stattdessen liefert es einen neuen String zurück. Es müßte also
format = format.replace(",","");
lauten.
(Besonders elegant ist diese Lösung nicht, aber … wenn man sie in eine Methode verpackt…
String createStringForArray(int array[]) { … }
kann man das später noch leicht ändern)

Hmm, Java 8 macht da auch keine gute Figur, oder habe ich was übersehen?

static String createStringForArray(int array[]) {
   return Arrays.stream(array)
      .mapToObj(Integer::toString)
      .collect(Collectors.joining(""));
}

Da ist das wirklich eleganter:

static String createStringForArray(int[] array) {
    return Arrays.toString(array).replaceAll("\\D","");
}

Mit Strings wäre das sehr viel angenehmer:

        int i = s1.length() - 1, j = s2.length() - 1, z = 0;
        while (i >= 0 || j >= 0 || z > 0) {
            int x = 0, y = 0;
            if (i >= 0) {
                x = s1.charAt(i) - '0';
            }
            if (j >= 0) {
                y = s2.charAt(j) - '0';
            }
            s3 = (x + y + z) % 10 + s3;
            z  = (x + y + z) / 10;
            i--;j--;
        }
        System.out.println("s3 = " + s3);```