Buchstabenhäufigkeit + Verschiebung

Greenkobolt

Mitglied
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
Sie müssen als für jedes Zeichen der ersten Zeichenkette ermitteln, wie häufig dieser vorkam. Dazu sollten Sie sich ein Feld der Länge 26 anlegen (es gibt 26 Buchstaben, wobei Sie davon ausgehen könne, dass anstatt ß nur ss, anstatt ä, ö oder ü entsprechend ae, oe oder ue geschrieben wird).
Sobald Sie die Häufigkeiten ermittelt haben, wählen Sie den Buchstaben aus, der am häufigsten vorkam und vergleichen diesen mit jedem Buchstaben der zweiten Zeichenkette und geben jeweils die Verschiebung an. Haben Sie z.B. herausgefunden, dass der häufigste Buchstabe g ist und in der zweiten Zeichenkette kommt nur en vor, so würde die folgende Ausgabe erwartet werden:


g
2
19

Damit e zu g wird, hättes jedes Zeichen um 2 Stellen verschoben werden müssen. Damit n zu g wird, hätte jedes Zeichen um 19 Stellen verschoben werden müssen.
Hinweise
Nutzen Sie aus, dass jedes Zeichen (char) durch eine Zahl dargestellt wird und die Buchstaben a bis z hintereinander vorkommen. Wenn Sie ein Feld der Länge 26 anlegen, steht die Position 0 für das a und 25 für das z. Wenn Sie 'a'-'a' rechnen, erhalten Sie immer 0, wenn Sie 'z'-'a' rechnen, erhalten Sie immer 25. Umgedreht können Sie aus der Position wieder das Zeichen erhalten (0 + 'a'ergibt den Wert für a und 25+'a' ergibt den Wert für z). Wie Sie bereits wissen, erhalten wir dann allerdings immer ein int-Wert, den Sie noch zu char casten müssen.

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

Mitglied
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.

Java:
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

Top Contributor
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'.

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.
 
K

kneitzel

Gast
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

Mitglied
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. :rolleyes:

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:

Java:
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. :D
 
Zuletzt bearbeitet:
K

kneitzel

Gast
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

Mitglied
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
 
K

kneitzel

Gast
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

Mitglied
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! :oops:
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'

Java:
 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

Top Contributor
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?
 
K

kneitzel

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

Was ist das Ergebnis davon?

Und was passiert, wenn Du nun folgenden Code ausführst:
Java:
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?
 
K

kneitzel

Gast
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

Top Contributor
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

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

Was ist das Ergebnis davon?

Und was passiert, wenn Du nun folgenden Code ausführst:
Java:
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?

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

Top Contributor
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

Top Contributor
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 :)

Java:
            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);
 
K

kneitzel

Gast
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

Top Contributor
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

Top Contributor
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

Java:
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

Top Contributor
LOL. Noch ein kleiner Hinweis:
Java:
            tempResult = shift1-shift2;
            while (tempResult < 0) {
                tempResult += 26;
            }
            int result = tempResult % 26;

Sei n := alphabet.length
  1. Es gilt 0 <= shift1 < n sowie 0 <= shift2 < n, d. h. -n < shift1 - shift2 < n. Du brauchst also nur einmal n zu addieren.
  2. Es ist m % n == (m+n) % n, d. h. Du kannst n immer addieren.
Kurz:
Java:
int result = (shift1 - shift2 + alphabet.length) % alphabet.length;
 

Neue Themen


Oben