# Buchstaben zählen



## javaFanboy (1. Nov 2010)

Moin,

ich möchte gerne durch Buchstabenhäufigkeitsanalysen feststellen, in welcher Sprache ein gewisser Text verfasst ist. Dazu lade ich eine Textdatei und speichere diese in einem String (nur Kleinbuchstaben, keine Sonderzeichen und keine Leerzeichen).

Anschließend möchte ich die Buchstaben zählen und habe dazu diesen kleinen Code gefunden:

```
public static void main(String[] args) {
		Map<Character,Integer> amount = new HashMap<Character, Integer>();
		String example = "afdieKJDKFJDFKkdjfkjdfkjdfkKJFDfijakfjaöifdakfjafiafijfiajföiafaifaiföjaf";
		
		for(int i = 0; i < example.length(); i++){
			if(amount.containsKey(example.charAt(i))){
				int j = amount.get(example.charAt(i));
				j++;
				amount.put(example.charAt(i), j);
			}else{
				amount.put(example.charAt(i), 0);
			}
		}
		Iterator it = amount.entrySet().iterator();
		while(it.hasNext()){
			Entry temp = (Entry) it.next();
			System.out.println(temp.getKey() + ": " + temp.getValue());
		}
	}
```

Nun möchte ich aber irgendwie die Buchstaben sortiert ausgeben von a bis z, aber irgendwie will er nicht so richtig, sondern es wird kreuz und quer ausgeben. Außerdem würde ich das ganze gerne relativ zur Länge des Strings ausgeben, also als Prozentzahlen, aber auch daran scheitere ich irgendwie.

Hat jemand vielleicht einen Tipp für mich?
Danke.


----------



## eRaaaa (1. Nov 2010)

javaFanboy hat gesagt.:


> speichere diese in einem String (nur Kleinbuchstaben, keine Sonderzeichen und keine Leerzeichen).


Ich sehe auch Großbuchstaben


> Nun möchte ich aber irgendwie die Buchstaben sortiert ausgeben von a bis z, aber irgendwie will er nicht so richtig, sondern es wird kreuz und quer ausgeben.


Nehme TreeMap anstelle HashMap


> Außerdem würde ich das ganze gerne relativ zur Länge des Strings ausgeben, also als Prozentzahlen, aber auch daran scheitere ich irgendwie.



Wo genau? Prozentrechnung? Die Länge des Strings erhältst du mit 
	
	
	
	





```
length()
```


----------



## XHelp (1. Nov 2010)

HashMap sortiert nicht nach Buchstaben. Da müsstest du schon die was einfalle lassen. (z.B. Ursprungsstring in char-Array packen, gleiche löschen, sortieren).

Wo genau ist dein Problem mit prizentzahlen? Du hast die Gesamtlänge und Anzahl der jeweiligen Buchstaben... reicht doch.
_(wobei mir nicht klar ist, warum du bei der Anzahl bei 0 anfängst)_

Generell zu deinem Vorhaben: du solltest nicht nur einzelne buchstaben untersuchen, sondern generell Tokens: mit Länge 1, mit Länge 2, 3, 4 usw bis X, dann werden die Ergebnisse besser.


----------



## javaFanboy (1. Nov 2010)

Hallo,

wie kann ich denn ein einzelnen String in ein Array packe, welches ich dann auch noch sortieren kann?


----------



## XHelp (1. Nov 2010)

String#toCharArray()


----------



## javaFanboy (1. Nov 2010)

Danke für die Antwort. Da nimmt einen Java ja richtig Arbeit ab...

Nun habe ich das ganze also in ein Array konvertiert und dann sortiert. Anschließend wieder zu einem langen String zusammengefügt. Nun würde ich gerne dort wieder drüber laufen und die Buchstaben zählen. Wie könnte ich das denn jetzt am geschicktesten machen, damit ich am ende eine tabelle habe mit den absoluten häufigkeiten jedes einzelnen Buchstaben?


----------



## XHelp (1. Nov 2010)

Naja, am besten du machst dir paar Klassen. Vor allem wenn du dann später mit deiner Beispielkollektion vergleichst, wirst es vom Vorteil sein.
Und im voraus das ganze zu sortieren macht wenig Sinn, weil HashMap immer noch in einer eigenen Reihenfolge das ganze speichert. Also entweder dieses Array nur für die Ausgabe verwenden oder wie eRaaaa es gesagt hat: TreeMap


----------



## javaFanboy (1. Nov 2010)

Hallo,

ich hatte jetzt gedacht, dass ich wieder die absoluten Zahlen ermittle. Die müssen ja auch nicht sortiert sein. Dann habe ich eine Liste mit relativen Zahlen zur Verfügung und würde jetzt schauen, bei welcher Sprache der Abstand zu den einzelnen gegebenen Prozentzahlen am kleinsten sind und dieses dann ausgeben.

Welche Klassen würdest du denn benutzen? Ich bin noch nicht so konform mit der JavaProgrammierung...


----------



## XHelp (1. Nov 2010)

Du musst eine Summe über die Abstände bilden. Wo die Summe am kleinsten ist, ist die wahrscheinlichste Sprache.
Also du könntest mindestens eine Klasse "Sprache" erstellen, die eben die Tokens gespeichert hält. Der Sinn des ganzen ist: wenn ein Token nicht in der Sprache ist, musst du ja jetzt mit 
	
	
	
	





```
if (bla.contains...
```
 an jeder Stelle abfragen. So könntest du es nur einmalig in der "Sprache" machen und dann eine 0 zurückgeben. Dann kannst du auch bequem eine Utility-Klasse machen mit "vergleiche(Sprache a, Sprache b)" usw.
Dann kannst du auch eine Klasse "Token" machen...
und was die sonst so einfällt, damit es etwas strukturierter aussieht.


----------



## Marco13 (1. Nov 2010)

Für die Minimallösung der beschriebenen Aufgabe (nur Kleinbuchstaben) wäre IMHO eigentlich ein Array schon ausreichend
counters[character-'a']++;
aber offenbar soll da ja was größeres draus werden, da kann eine Abstraktion nicht schaden. 

Übrigens wäre eine HashMap so wie ich das sehe schon besser geeignet. Wenn man 1 Million Buchstaben hat, hat man bei einer TreeMap 
1 Million * log(anzahlUnterschiedlicherBuchstaben)
Operationen beim Einfügen+Zählen, bei einer HashMap 
1 Million
Die Einträge der HashMap kann man nachher noch sortieren (das sind ja nur anzahlUnterschiedlicherBuchstaben Stück)


----------



## javaFanboy (2. Nov 2010)

Hallo,

also die TreeMap würde ja schon einmal sortiert das ganze zurück geben. Aber leider immer nur in absoluten Zahlen. Wie muss ich den Code (siehe oben) anpassen, dass ich relative Zahlen bekomme? Also irgendwo muss ich noch durch die StringLänge teilen...

Viele Grüße.


----------



## timbeau (2. Nov 2010)

Geht das nicht bei der Ausgabe oder einfach ein zusätzliches Array mit der Länge der TreeMap und dann in jedem Feld den Wert berechnen.


----------



## javaFanboy (2. Nov 2010)

Hallo,

habe jetzt unten die Ausgabe so angepasst:

```
Entry temp1 = (Entry)it.next();
            int i = Integer.parseInt(temp1.getValue().toString());
            double count = i / string.length();
            System.out.println(temp1.getKey() + ": " + Double.toString(count));
```

Aber ich erhalte jetzt nur 0.0 überall als Ergebnis...


----------



## Gast2 (2. Nov 2010)

```
i / string.length()
```
i ist ein int, string.length() ist ein int. Das Ergebis davon ist wieder ein int.
caste einen Operanden auf double und du erhälst deine nachkommastellen


----------



## bygones (2. Nov 2010)

ich würde da auch nicht das Rad neu erfinden. Die Datenstruktur die du suchst ist "Bag" und zb zu finden Multiset (Google Collections Library 1.0 (FINAL)) . 

schaut dann so aus

```
String example = "afdieKJDKFJDFKkdjfkjdfkjdfkKJFDfijakfjaöifdakfjafiafijfiajföiafaifaiföjaf";
Multiset multiset = HashMultiset.create(Lists.newArrayList(example.split("")));
```

Sortieren würde ich dann auch erst bei der Ausgabe und gar nicht beim produktiven Arbeiten.

wobei man auch mit den Google Collections das einfach lösen kann

```
String example = "afdieKJDKFJDFKkdjfkjdfkjdfkKJFDfijakfjaöifdakfjafiafijfiajföiafaifaiföjaf";
Multiset multiset = TreeMultiset.create(Lists.newArrayList(example.split("")));
```


----------



## Andi_CH (2. Nov 2010)

Es ist mir klar, dass meine Frage nur zum Teil zum Thema passt.

Ich habe irgendwo einmal eine wirklich gute Tabelle gefunden in der die verschiedenen Colletions, Bags und was auch immer verglichen werden konnten - sozusagen auch ein Entscheidungsbaum was optimal für ein bestimmtest Problem ist - Ein virtuelles Bier für den Hinweis wo ich das finde 

Ein Gedanke zum Problem : da ist vieles overkill.

(Das Problem der Häufigkeit habe ich schon einmal gelöst - wir haben aber Triphone genommen. Das sind Dreiergruppen inklusive whitespaces, diese aber auf jeweils einen reduziert - das war sehr zuverlässig für die Spracherkennung)

Meine Lösung damals in ANSI-C - ich iterierte durch den Text und inkrementierte in einem 3D int array hoch

Alles was folgt ist natürlich Pseudo  und für 1D Array


```
int[26] i;
for (char c in test) {
  int index = int(c)-int('a')
  i[index] +=1
}
```
Danach mit der Gesamtantzahl der Buchstaben normieren (wir blieben bei int - einfach mit 1000 multipliziert oder so - es ist lange her, ich weiss es nicht mehr im Detail)

Das macht man für Referenztexte sowie für zu untersuchende Texte

Kriterium um welche Sprache es sich handelt:

Loop über den int[26] array des zu prüfenden Textes und alle Referenzarrays
Differenz zur entsprechenden Position der Referenzarrays bilden und diese Aufsummieren.

die kleinste Differenz ergibt die Sprache.


----------



## javaFanboy (2. Nov 2010)

Hallo,

habe jetzt ein wenig umstrukturiert und erhalte nun die Tabelle, in einer Variable, welche ich jetzt überprüfen möchte, um welche Sprache es sich handelt.

Hier gibt es eine Auflistung der verschiedenen Sprachen mit ihrer Häufigkeit. Doch wie kann ich jetzt am Besten vorgehen um festzustellen, welche Sprache der Text hat?


----------



## XHelp (2. Nov 2010)

Wie gesagt: über den Betrag der Differenzen eine Summe bilden, und wo die Summe am kleinsten ist: ist es vermutlich die gesuchte Sprache.


----------

