# n verschachtelte Schleifen?



## Roccosi7 (30. Dez 2012)

Hi!
Ich möchte ein Script schreiben, das die vom User in einem String vorgegebenen Buchstaben zu allen möglichen Folgen kombiniert.
Wenn der User also 'abc' angibt, soll das Script 
aaa
aab
aac
aba
(...)

In eine Datei schreiben.
Ich habe das ganze jetzt erstmal so gelöst:

```
char chars[] = str.toCharArray();

for (j:chars)
{
     for (k:chars)
    {
        for (l:chars)
        {
             bw.write(j + "" + k + "" +l);
        }
    }
}
```

1.Frage:
Ich muss jetzt immer "" zwichen den Chars einfügen, da sonst die Ascii-Werte addiert werden und in der Klammer dann ein int-Wert anstatt eines Strings entsteht.Wie kann ich das eleganter verhindern?

2.Frage
Das Script gibt bis jetzt ja nur alle möglichen 3-stellingen Kombinationen aus.Ich möchte aber, dass der User sowohl alle möglichen 10.000-stelligen Kombinationen aus z.B 0 und 1 wie auch alle 2-stelligen aus z.B dem gesamten abc bekommen kann.
Wie kann ich das bewerkstelligen?
Also das der User die Länge der ausgegebenen Kombinationen bestimmen kann.

Zu dem ist das Script nicht besonders performant...liegt vielleicht an den Arrays? hab gehört die sollen langsam und Speicherintensiv sein?
Und das man lieber Sammlungsklassen verwenden kann? 
(keine Ahnung was damit gemeint ist und wie das funktionieren soll....)

Für Hilfe wäre ich sehr dankbar 

Lg


----------



## langhaar! (30. Dez 2012)

Roccosi7 hat gesagt.:


> 2.Frage
> Das Script gibt bis jetzt ja nur alle möglichen 3-stellingen Kombinationen aus.Ich möchte aber, dass der User sowohl alle möglichen 10.000-stelligen Kombinationen aus z.B 0 und 1 wie auch alle 2-stelligen aus z.B dem gesamten abc bekommen kann.
> Wie kann ich das bewerkstelligen?



Indem du Rekursion verwendest. Such mal im Forum. Ähnliche Problemstellung gab es schon oft.

Das Programm benötigt eine Schleife, in der alle gewünschte Ausprägungen eines char erzeugt werden und dann ruft sich das Programm selber auf.  
Wenn du nicht weisst, was Rekursion ist und wie sie funktioniert wird es allerdings nicht einfach.


----------



## Landei (30. Dez 2012)

```
public static void allCombinations(String s, int length) {
        allComb(s, length, "");
    }

    private static void allComb(String s, int length, String prefix) {
        if (length == 0) {
            System.out.println(prefix);
        } else {
            for(char c : s.toCharArray()) {
               allComb(s, length-1, prefix + c);
            }
        }
    }

    //Aufruf
    allCombinations("abc", 3);
```


----------



## Roccosi7 (31. Dez 2012)

Hey,
Danke für die Hilfe!
Das eine Funktion sich auch selber aufrufen kann, daran hatte ich nicht gadacht.


> Wenn du nicht weisst, was Rekursion ist und wie sie funktioniert wird es allerdings nicht einfach.


...kann man ja lernen 

Lg


----------



## Roccosi7 (31. Dez 2012)

Ein Problem habe ich noch:
Ich möchte bei der Funktion von "Landei" den fertigen String nicht auf dem Bildschirm ausgeben sondern in eine Datei schreiben.
Allerdings möchte ich den Bufferedwriter nicht mit jedem Funktionsaufruf neu initialisieren.
Und ausserhalb des Try-Blocks im Konstruktor ist das Objekt ja nicht verfügbar.

Weiss jemand abhilfe?

Lg


----------



## Landei (31. Dez 2012)

Du kannst den Writer entweder jeder Methode mitgeben, oder als Feld der Klasse definieren (ich habe alles statisch, also müsste auch das Feld statisch sein, oder die statischen Methoden in Instanzmethoden geändert werden). Eine weitere Lösung wäre, alle Ergebnisse z.B. in einer Liste zu sammeln, und die dann am Ende in eine Datei zu schreiben - eigentlich die flexibelste Lösung, allerdings werden alle Ergebnisse im Speicher gehalten, was bei zu vielen Ergebnissen zu Speicherüberläufen führen kann. Um das zu umgehen, könnte man das Ergebins auch als Iterator verpacken, aber das wäre nicht ganz einfach zu schreiben.

Hier mal eine mögliche Listen-Version:


```
public static List<String> allCombinations(String s, int length) {
        List<String> result =  new ArrayList<String>();
        allComb(s, length, "", result);
        return result; 
    }
 
    private static void allComb(String s, int length, String prefix, List<String> list) {
        if (length == 0) {
            list.add(prefix);
        } else {
            for(char c : s.toCharArray()) {
               allComb(s, length-1, prefix + c, list);
            }
        }
    }
```


----------



## Roccosi7 (31. Dez 2012)

Ich denke mal ein Array wäre keine wirkliche Lösung, da die Listen zum Teil im zweistelligen Gb-Bereich gehalten werden....
Ich bin (noch) nicht so erfahren in Java, gibt es da vielleicht eine einfache, auch für mich verständliche Variante ohne den Speicher zu flooden?


----------



## Roccosi7 (31. Dez 2012)

Kann man die Methode vielleicht irgendwie in den Try-Block mit einbauen?


----------



## Landei (31. Dez 2012)

Vielleicht ist in http://www.java-forum.org/codeschnipsel-u-projekte/81973-combinatorics.html ein passender (oder anpassbarer) Iterator dabei, der dir einfach nacheinander die Werte liefert (könnte 
	
	
	
	





```
ChoiceIterable
```
sein, wenn ich mich nicht irre)...


----------



## Roccosi7 (31. Dez 2012)

Ich weiss nicht ob ich ein Brett vor'm Kopf habe, aber ich versteh grad nicht was mir da ein Iterator bringen soll?
Gibt es da denn echt nichts einfacheres?


----------



## Spacerat (31. Dez 2012)

Roccosi7 hat gesagt.:


> Ich denke mal ein Array wäre keine wirkliche Lösung, da die Listen zum Teil im zweistelligen Gb-Bereich gehalten werden....
> Ich bin (noch) nicht so erfahren in Java, gibt es da vielleicht eine einfache, auch für mich verständliche Variante ohne den Speicher zu flooden?


Wenn kein Array, dann halt ein BufferedWriter. Lässt sich genauso gut initialisieren und übergeben. Nur sollte man einen solchen vor Verlassen der Hauptmethode ("allCombinations()") auch wieder schliessen.


----------



## Roccosi7 (31. Dez 2012)

Hab jetzt selbst eine Lösung gefunden:
Man muss der Funktion einfach nur den Writer übergeben.

```
public static void allComb (String s, int length,String pf) throws IOException 
    { 
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( 
            new FileOutputStream(pf))); 
            
            allCombRek(s, length,"",bw); 
        
            bw.close(); 
    }
 

    public static void allCombRek(String s, int length, String prefix,BufferedWriter bw) 
    throws IOException
    {
        if (length == 0) 
        {
            bw.write(prefix + "\n");
        } 
        else 
        {
            for(char c : s.toCharArray()) 
            {
                allCombRek(s, length-1, prefix + c,bw);
            }
        }
    }
```

Eigentlich ganz simpel, muss man nur drauf kommen 
Die Funktion muss jetzt zwar in einem Try-Block aufgerufen werden, aber immerhin.
Trotzdem vielen Dank für eure Hilfe!

Lg


----------



## Roccosi7 (31. Dez 2012)

Spacerat: Jetzt war ich auch grade selber drauf gekommen, trotzdem Danke


----------



## Landei (1. Jan 2013)

Na ja, ich schrub ja bereits "Du kannst den Writer entweder jeder Methode mitgeben,...".

Das Problem von solchem Code ist, dass er dadurch unflexibel und schlecht refactorbar wird, weil du den Algorithmus mit einer bestimmten Form der Ausgabe verquickst. Was ist, wenn du den Krams morgen lieber in eine Datenbank schreiben willst? Richtig, du friemelst tief im eigentlichen Algorithmus herum (etwa in dem du eine JDBC-Session statt des BufferedWriters übergibst). Mit anderen Worten: Du verletzt das SRP (das entgegen der Wikipedia-Darstellung auch für Methoden, Module u.s.w. gilt).

Genau deshalb habe ich zwei Lösungen vorgeschlagen, die das Erzeugen der Werte von deren Weiterverarbeitung *entkoppeln*: Eine Zwischenspeicherung in einer Liste (was aber zu Speicherproblemen führen kann) oder einen Iterator (der den nächsten Wert "just in time" berechnet, und deshalb kein Speicherproblem verursachen kann).

Auch wenn dein Code "funktioniert" und du ihn so einsetzen kannst, solltest du die Schwachpunkte kennen, und auch Techniken, wie man erstere "im richtigen Leben" vermeidet.


----------



## Roccosi7 (1. Jan 2013)

Landei:
Da magst du schon recht haben, das ganze zu entkoppeln würde es bei späterer Modifizierung einfacher machen, aber für meine Zwecke ist das so vollkommen ausreichend.
Ausserdem bin ich auch nicht so lange mit Java zugange, das kommt bestimmt alles noch.


----------

