# Buchstabenhäufigkeit + Verschiebung



## Greenkobolt (25. Nov 2019)

Hey Leute,

ich soll für die Uni folgende Aufgabe lösen:



> *Buchstabenhäufigkeit*
> *Aufgabe*
> Ihr Programm erhält zwei Zeichenketten als Kommandozeilenparameter. Die erste etwas längere Zeichenkette wurde mit dem Caesar-Verfahren verschlüsselt. Die zweite Zeichenkette enthält Buchstaben, die vermutlich am häufigsten im Klartext vorkamen. Ihre Aufgabe besteht nun darin, den in der ersten Zeichenkette am häufigsten vorkommenden Buchstaben zu ermitteln und auszugeben. Weiterhin soll für jeden Buchstaben der zweiten Zeichenkette die Verschiebung vom Zeichen zum häufigsten Zeichen der ersten Zeichenkette ermitteln und der Wert ausgegeben werden. Es werden nur Buchstaben berücksichtigt, wobei Groß- und Kleinschreibung ignoriert werden. Alles was kein Buchstabe ist, wird ignoriert. Es gibt immer genau einen Buchstaben, der in der ersten Zeichenkette am häufigsten vorkommt.
> *Vorgehensweise*
> ...



Ich habe bisher viel recherchiert über Häufigsten Buchstaben ausgeben usw. Allerdings fehlt mir der Sinn bzw. die Erklärung dazu. Ich stecke einfach fest und bin relativ ansatzlos.

Würde das ganze irgendwie mit Char-Arrays lösen, aber dann steht in der Aufgabe, dass man das mit Integer macht und die dann zu einem Char castet. 
Möchte da ganz offen sprechen: Da fehlt wohl auch einfach Grundwissen, dass ich aufgrund von den KiTa Keimen meiner Tochter und den daraus resultierenden Krankheiten leider immer mal verpasse vermittelt zu bekommen.

Kann sich vielleicht ein guter Samariter erbarmen, und mir erklären wie und wo und überhaupt?


----------



## Greenkobolt (25. Nov 2019)

Habe bis jetzt sowas in der Art hier. Der gibt mir hier natürlich ne "out of bounds exception".
Hab das ganze auch mit der ASCII Size 256 gemacht (Die Methode angepasst und ergoogelt). Ich glaube aber, dass das nicht zur Aufgabenstellung passt.


```
public class Buchstabenhaeufigkeit {
    public static void main(String[] args) {
        String str1 = "Vi ompb lwkp!".toLowerCase().replaceAll("\\s", "");
        String str2 = "h";
       
        int len = str1.length();
        char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray(); // int[] alphabet = new int[256] -> dann funktioniert
       
        for(int i = 0; i < len; i++) {
            alphabet[str1.charAt(i)]++;
           
        }
        int maxi = -1 ;
        char ergebnis = ' ';
       
        for(int i = 0; i < len; i++) {
            if (maxi < alphabet[str1.charAt(i)]) {
                maxi = alphabet[str1.charAt(i)];
                ergebnis = str1.charAt(i);
            }
        }
        System.out.println(ergebnis);
    }
   
}
```

Was denkt ihr?


----------



## mihe7 (25. Nov 2019)

Greenkobolt hat gesagt.:


> Möchte da ganz offen sprechen: Da fehlt wohl auch einfach Grundwissen, dass ich aufgrund von den KiTa Keimen meiner Tochter und den daraus resultierenden Krankheiten leider immer mal verpasse vermittelt zu bekommen.


LOL, die Tränendrüsen werden immer dreister  

Im Rechner wird alles mit zwei Zuständen dargestellt, die gerne mit '0' und '1' visualisiert werden. Um nun Zeichen auf den Schirm zu bekommen, braucht es eine Tabelle, die einer Kombination aus mehreren dieser Zustände ein entsprechendes Zeichen zuordnet. Gleichzeitig lassen sich diese Kombinationen als binäre Zahl auffassen. Daraus folgt, dass jedem Zeichen in einer solchen Zeichentabelle eine Zahl zugeordnet ist. D. h. ein Zeichen kann als Zahl und Zahlen können als Zeichen dargestellt werden.

In Java kann man mit Zeichen daher etwas rechnen: 'a' - 'a' bedeutet nichts anderes als: die Zahl, die für das 'a' steht minus der Zahl, die für das 'a' steht. Gibt also 0. Außerdem stehen die Buchstaben nacheinander in der Zeichentabelle, so dass 'b'-'a' 1 und 'z'-'a' 25 liefert.

Umgekehrt kann man aus einer Zahl ein Zeichen machen, so ist `(char)65` das 'A'.



Greenkobolt hat gesagt.:


> Was denkt ihr?


Dein Alphabet ist also OK, Du hast ein Array mit 26 Buchstaben, deren Indizes von 0 bis 25 gehen.

Jetzt kannst Du Dir überlegen, wie Du zu einem Buchstaben, den geeigneten Index findest: 'c' - 'a' liefert offensichtlich den Index zum c in Deinem Alphabet.


----------



## kneitzel (25. Nov 2019)

Also als erstes nutzt Du char als Zahl, das ist ok. Aber nun hast Du den Effekt, dass 'a' nun einmal nicht 0 ist.
Aber damit 'a' 0 und 'b' eben 1 ist, kannst Du ja einfach 'a' abziehen.

`alphabet[str1.charAt(i)-'a']++;`

Aber: Das gilt dann nur für die Buchstaben a-z. Du hast aber z.B. noch ein 'V' und ein '!' bei Dir in der Zeichenkette. Das ist etwas, das diesen Ansatz kaputt macht.


----------



## Greenkobolt (25. Nov 2019)

mihe7 hat gesagt.:


> LOL, die Tränendrüsen werden immer dreister



Die Tränendrüsen werden auch immer "dicker" weil die Bindehautentzündung mich öfter besucht als der Kaffee am Morgen. Nein du hast natürlich recht. Ich habe da ziemlich viel "mimimimimi" geschrieben. 



JustNobody hat gesagt.:


> Aber: Das gilt dann nur für die Buchstaben a-z. Du hast aber z.B. noch ein 'V' und ein '!' bei Dir in der Zeichenkette. Das ist etwas, das diesen Ansatz kaputt macht.



Sonderzeichen sollen sowieso ignoriert werden. Heißt ich würde dann eventuell das ganze so angehen:


```
String str1 = "Vi ompb lwkp!".toLowerCase().replaceAll("\\s", "").replaceAll("[^a-z]", "")
```

Dann könnte das funktionieren. Alleine das Gespräch darüber "beflügelt" meinen Denkapparat ein wenig.


----------



## kneitzel (25. Nov 2019)

Wenn Du `replaceAll("^[a-z]", "")` machst, dann musst Du `replaceAll("\\s", "")` nicht mehr machen.

Und Du hast einen Tippfehler: `replaceAll("[^a-z]", "")`

Das ^ am Anfang steht für den Anfang des zu matchenden Textes bzw. für einen Zeilenanfang bei multiline Matches. Das wollen wir hier nicht. Innerhalb von [] als erstes Zeichen ist es die Invertierung (also alle Zeichen außer den angegebenen).


----------



## Greenkobolt (25. Nov 2019)

JustNobody hat gesagt.:


> Wenn Du `replaceAll("^[a-z]", "")` machst, dann musst Du `replaceAll("\\s", "")` nicht mehr machen.
> 
> Und Du hast einen Tippfehler: `replaceAll("[^a-z]", "")`
> 
> Das ^ am Anfang steht für den Anfang des zu matchenden Textes bzw. für einen Zeilenanfang bei multiline Matches. Das wollen wir hier nicht. Innerhalb von [] als erstes Zeichen ist es die Invertierung (also alle Zeichen außer den angegebenen).



Habe gerade nochmal nur das String "formatieren" durchgetestet. 

`String str1 ="Vi ompb lwkp!".toLowerCase().replaceAll("^[a-z]", "");
        System.out.println(str1);`

Wenn ich es so mache, dann kommt Ausgabe folgendes:
`i ompb lwkp!`

Wenn ich das "^" in die eckigen Klammern setzte passt es. 

`String str1 ="Vi ompb lwkp!".toLowerCase().replaceAll("[^a-z]", "");
        System.out.println(str1);`

Output:

`viompblwkp`


----------



## kneitzel (25. Nov 2019)

Du hast Deinen Beitrag editiert. 

Genau das meinte ich. Du hattest das ^ zuerst außerhalb der eckigen Klammern. Da hat es eine andere Bedeutung. Aber so ist es in Ordnung.


----------



## Greenkobolt (25. Nov 2019)

JustNobody hat gesagt.:


> Du hast Deinen Beitrag editiert.
> 
> Genau das meinte ich. Du hattest das ^ zuerst außerhalb der eckigen Klammern. Da hat es eine andere Bedeutung. Aber so ist es in Ordnung.


Oh ja stimmt, sorry tut mir leid! 
Aber so passt es ja.

Wenn ich den von dir vorgeschlagenen `-'a'` einsetze, bekomme ich als Output "w". Müsste ja aber "p" kriegen. 
Mein Fehler liegt sicher bei der allgemeinen Verwendung von `-'a'`


```
for(int i = 0; i < len; i++) {
                    alphabet[str1.charAt(i)-'a']++;
                  
                }
                int maxi = -1 ;
                char ergebnis = ' ';
              
                for(int i = 0; i < len; i++) {
                    if (maxi < alphabet[str1.charAt(i)-'a']) {
                        maxi = alphabet[str1.charAt(i)-'a'];
                        ergebnis = str1.charAt(i);
                    }
                }
                System.out.println(ergebnis);
```

Hast du eine Idee?


----------



## Kirby.exe (26. Nov 2019)

Also um die Verschiebung zwischen dem am meisten vorkommenden Charakter im ersten String und den einzelnen Charakter aus dem 2 String muss mann doch eigentlich nur den Index des am meisten vorkommenden Charakter z.b. g - den Index von Beispielsweise n % 26 (wegen 26 Buchstaben) oder nicht?


----------



## kneitzel (26. Nov 2019)

Schauen wir einmal, was dieser Code macht:

```
char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray();
```

Was ist das Ergebnis davon?

Und was passiert, wenn Du nun folgenden Code ausführst:

```
alphabet[str1.charAt(i)-'a']++;
```

Also was ist z.B. in alphabet[0] nach dem ersten Befehl?
Und was ist in alphabet[0], wenn ein 'a' gefunden wurde?

Erkennst Du das Problem / den Denkfehler?


----------



## kneitzel (26. Nov 2019)

Kirby_Sike hat gesagt.:


> Also um die Verschiebung zwischen dem am meisten vorkommenden Charakter im ersten String und den einzelnen Charakter aus dem 2 String muss mann doch eigentlich nur den Index des am meisten vorkommenden Charakter z.b. g - den Index von Beispielsweise n % 26 (wegen 26 Buchstaben) oder nicht?


Das ist prinzipiell auch eine gute Idee, nur eben ist da dann evtl. nicht auf Anhieb ersichtlich, wenn
a) Ein Zeichen außerhalb des Bereichs ist (Modulo 26 wird für jedes Zeichen klappen).
b) die Zuordnung ist nicht wirklich intuitiv, wobei es ja egal ist, ob a im index 0 steht oder nicht. Zugriff kann ja visuell erfolgen als 'a'%26 so man das wollte.

Also ja: Die Idee ist gut und würde aus meiner Sicht auch zum Ziel führen.


----------



## Kirby.exe (26. Nov 2019)

JustNobody hat gesagt.:


> Das ist prinzipiell auch eine gute Idee, nur eben ist da dann evtl. nicht auf Anhieb ersichtlich, wenn
> a) Ein Zeichen außerhalb des Bereichs ist (Modulo 26 wird für jedes Zeichen klappen).
> b) die Zuordnung ist nicht wirklich intuitiv, wobei es ja egal ist, ob a im index 0 steht oder nicht. Zugriff kann ja visuell erfolgen als 'a'%26 so man das wollte.
> 
> Also ja: Die Idee ist gut und würde aus meiner Sicht auch zum Ziel führen.



Die Frage die ich mir ständig stelle ist, wie zur Hölle soll ich die Verschiebung returnen wenn ich zwei Charakter im zweiten String habe xD ich habe den Kram nämlich in eine eigene Methode gepackt. Also wenn ich zwei Verschiebungswerte ausgeben muss


----------



## Greenkobolt (26. Nov 2019)

JustNobody hat gesagt.:


> Schauen wir einmal, was dieser Code macht:
> 
> ```
> char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray();
> ...



Habe gerade kurz überlegen müssen, und lieber einmal alles durchgetestet und ja. Wenn ein 'a' gefunden wird, dann ist alphabet[0] = 'b'.
Allerdings bleibt mein Gedankengang auch genau da stecken. Um das zu vermeiden müsste ich ja irgendwie das `-'a'` durch eine allgemeinere Variante ersetzen...


----------



## mihe7 (26. Nov 2019)

Ihr solltet die Aufgabe nochmal genau lesen: "Sie müssen als für jedes Zeichen der ersten Zeichenkette ermitteln, wie häufig dieser vorkam." - vor diesem Hintergrund die Frage: wenn ihr Häufigkeiten wollt, warum zählt ihr dann Zeichen aus alphabet hoch?


----------



## Kirby.exe (26. Nov 2019)

Bin ich blöd oder führt er die Modulo Rechnung nicht aus? xD bekomme ständig -7 raus obwohl 6-13%26= 19 lul 


```
for (int j = 0; j< alphabet.length; j++) {
                if (alphabet[j] == max) {
                     shift1 = j;
                     System.out.println(shift1);
                }
                if (alphabet[j] == str2.charAt(t)) {
                     shift2 = j;
                     //t++;
                     System.out.println(shift2);
                }
            }
            result = (shift1-shift2)%26;
            
            System.out.println(result);
```


----------



## Kirby.exe (26. Nov 2019)

Nevermind habe gelesen das modulo in java nicht äquivalent zu modulo in der Mathematik ist


----------



## kneitzel (26. Nov 2019)

Kirby_Sike hat gesagt.:


> Bin ich blöd oder führt er die Modulo Rechnung nicht aus? xD bekomme ständig -7 raus obwohl 6-13%26= 19 lul


13 % 16 = 13 und 6 - 13 = -7.  (Edit: Rangfolge ist natürlich so, dass erst Modulo gerechnet wird!)

Im Augenblick verstehe ich auch nicht genau, was Du da genau machen willst.

Also wenn man wirklich nur ein Array mit Zählern hat und jeder Zähler steht für einen Buchstaben, dann wäre etwas wie dies hier denkbar (Um Modulo einzusetzen):
`counter[str1.charAt(i) % 26]++;`

Aber in der Aufgabe ist auf die Verwendung des " -'a'" explizit hingewiesen. Ob man dies aber nun verwendet oder nicht ist natürlich offen.

Also für mich ist die Frage: Brauchen wir ein Char-Array mit den Buchstaben des Alphabets, wenn uns nur a-z interessieren?


----------



## mihe7 (26. Nov 2019)

Kirby_Sike hat gesagt.:


> Nevermind habe gelesen das modulo in java nicht äquivalent zu modulo in der Mathematik ist


Jo, in Java heißt das Ding streng genommen auch remainder und es gilt `a % b == a - b*(a/b)`, wobei '/' die ganzzahlige Division ist. 

D. h. `-7 % 26 == -7 - 26*(-7/26) == -7 - 26*0 == -7`. 

Die mathematische Variante verwendet statt der ganzzahligen Division die Gaußklammer, so dass wegen -1 <  -7/26 < 0 aus -7/26 die -1 wird. Dann ergibt sich `-7 mod 26 == -7 - 26*(-1) == 26 - 7 == 19`.


----------



## Kirby.exe (26. Nov 2019)

Also ich habe die Aufgabe gelöst, das Problem ist die Test sind in Eclipse alle richtig. Jedoch die Test Umgebung der Uni schmiert bei dem Code ab, ich schicke den Teil einfach rein vielleicht findet ihr ja den Fehler  ich sehe ihn jedenfalls nicht


```
char [] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray();
        char max = getMaxOccuringChar(str);
        int shift1 = 0;
        int shift2 = 0;
        int tempResult = 0;
        int t = 0;
        
        while(t < str2.length()) {
            for (int j = 0; j< alphabet.length; j++) {
                if (alphabet[j] == max) {
                     shift1 = j;
                     //System.out.println(shift1);
                }
                if (alphabet[j] == str2.charAt(t)) {
                     shift2 = j;
                     //t++;
                     //System.out.println(shift2);
                }
            }
            tempResult = shift1-shift2;
            while (tempResult < 0) {
                tempResult += 26;
            }
            int result = tempResult % 26;
            System.out.println(result);
            t++;
       }
```


----------



## mihe7 (26. Nov 2019)

Kirby_Sike hat gesagt.:


> Jedoch die Test Umgebung der Uni schmiert bei dem Code ab


Gibts da auch eine Meldung/Ausgabe?


----------



## Kirby.exe (26. Nov 2019)

mihe7 hat gesagt.:


> Gibts da auch eine Meldung/Ausgabe?


Sry nevermind, ich bin einfach ein Holzkopf xD habe vergessen t++; in die While Schleife zu tun in der Testumgebung  Jetzt fühle ich mich verarscht xD


----------



## mihe7 (26. Nov 2019)

LOL. Noch ein kleiner Hinweis:

```
tempResult = shift1-shift2;
            while (tempResult < 0) {
                tempResult += 26;
            }
            int result = tempResult % 26;
```

Sei n := alphabet.length

Es gilt 0 <= shift1 < n sowie 0 <= shift2 < n, d. h. -n < shift1 - shift2 < n. Du brauchst also nur einmal n zu addieren.
Es ist m % n == (m+n) % n, d. h. Du kannst n immer addieren.
Kurz:

```
int result = (shift1 - shift2 + alphabet.length) % alphabet.length;
```


----------



## Kirby.exe (26. Nov 2019)

Thäääänk U <3


----------

