Forum: PC-Programmierung Bruteforce Optimierung


von Karl (Gast)


Lesenswert?

Hallo,

ich habe mich schon länger gefragt, wie man am besten ein Programm 
schreibt, dass alle Möglichkeiten durchprobiert. Wenn man z.B. eine 
Matrix m x n .In der Matrix steht irgendwas, was entweder maxi- oder 
minimiert werden soll (Summe, Produkt etc.). Nun muss ich ja alle 
Möglichkeiten durchprobieren, wobei in jeder Zeile nur ein Feld 
verwendet werden darf. Hier mal ein Beispiel
1
9  1  1  1
2
1  9  1  1
3
1  1  9  1
4
1  1  1  9

Hier ist ja klar, dass man in jeder Zeile jeweils das Feld mit der 9 
nimmt. Aber wenn da noch andere Zahlen stehen wird es ja komplizierter. 
Und wenn man das für 100x100 Matrizen optimieren soll ist es ja per Hand 
fast unmöglich.
Solche Optimierungen treten ja in der Praxis denke ich recht oft auf. 
Außer Brute-Force fällt mir gerade gar nichts anderes ein. Aber wie 
würde man den Bruteforce Algorithmus aufbauen? Ich scheitere gerade 
daran alle Möglichkeit durchzuprobieren. Der Rest ist denke ich Recht 
einfach. Ich nehme ein Ergebnis Array. Darin speichere ich die Folge der 
Spalten. Und würde in der Schleife immer schauen, ob die Aktuelle 
Kombination besser ist, und wenn ja die in dieses Array schreiben. Aber 
an der Schleife scheitert es eben. Hat da jemand einen Tipp, wi man das 
ganze am effizientesten macht?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Nicht unbedingt immer "effizient" dafür effektiv, hängt stark davon ab 
wie gut man das Abbruchkriterium definieren kann:
http://de.wikipedia.org/wiki/Backtracking

von Karl (Gast)


Lesenswert?

Das lese ich mir mal durch. Also im Prinzip gibt es ja bei meinem 
Problem m! Möglichkeiten wenn man von einer m x m Matrix ausgeht. Ich 
Versuche jetzt erst einmal ein Programm zu schreiben, was aus einem 
Alphabet (A,B,C) alle Möglichkeiten ausgibt, was ja genau mein Problem 
darstellt.

Die Ausgabe müsste ja irgendwie so sein:

ABC
ACB
BAC
BCA
CBA
CAB
1
#include <iostream>
2
3
using namespace std;
4
int main (int argc, char * const argv[]) {
5
  char buch[]= {'A','B','C'};
6
  return 0;
7
}

Hast du eine Idee, wie man da die Schleife für die m! Möglichkeiten 
gestalten könnte?

von Klaus W. (mfgkw)


Lesenswert?

Du musst soviele Schleifen inienanderschachteln, wie du Variablen hast.
Hier also 3 (1., 2., 3. Stelle).

In der innersten Schleife rechnest du den Wert der Zielfunktion aus
(wie gut oder schlecht ist die aktuelle Kombination).
Falls sie besser ist als alle bisherigen, dann merken.

Wo ist jetzt das Problem, außer daß die Rechenzeit beliebig hoch geht,
je nach Anzahl der Kombinationsmöglichkeiten?

von Karl (Gast)


Lesenswert?

Mein Problem ist, dass es keine Dopplungen geben darf. Also AAA ist 
nicht erlaubt. Wie filtere ich das heraus.
1
  for (int i=0; i<3; ++i) {
2
    for (int j=0; j<3; j++) {
3
      for (int k=0; k<3; k++) {
4
        cout << i << j << k << endl;
5
      }
6
    }
7
  }

Schön wäre natürlich eine Rekursive Lösung.

von Klaus W. (mfgkw)


Lesenswert?

Karl schrieb:
> for (int i=0; i<3; ++i) {
>     for (int j=0; j<3; j++) {
>       for (int k=0; k<3; k++) {
>         cout << i << j << k << endl;
>       }
>     }
>   }
1
  for (int i=0; i<3; ++i)
2
  {
3
    for (int j=0; j<3; j++)
4
    {
5
      if(j!=i)
6
      {
7
        for (int k=0; k<3; k++)
8
        {
9
          if( k!=i && k!=j )
10
          {
11
            cout << i << j << k << endl;
12
          }
13
        }
14
      }
15
    }
16
  }

von Klaus W. (mfgkw)


Lesenswert?

Karl schrieb:
> Schön wäre natürlich eine Rekursive Lösung.

Wenn du es mit Schleifen schon nicht schaffst, wieso soll es dann
unbedingt rekursiv sein? Schneller oder besser wird das davon auch
nicht.

von Karl (Gast)


Lesenswert?

Danke. So eine Lösung hatte ich auch schon. Das Problem dabei sind nur 
die ganzen Prüfungen auf Gleichheit bzw. die vielen Schleifen. Wenn man 
jetzt z.B. eine 100 x 100 Matrix hat ist das ja sehr unflexibel.

von Arc N. (arc)


Lesenswert?

Darf's C++ sein?
Dann könnte das mit next_permutation
http://www.sgi.com/tech/stl/next_permutation.html  und einer passenden 
Sortierung/Filterung gelöst werden.

(funktionale Sprachen sind bei so etwas deutlich im Vorteil)

von Klaus W. (mfgkw)


Lesenswert?

Karl schrieb:
> Mein Problem ist, dass es keine Dopplungen geben darf. Also AAA ist
> nicht erlaubt. Wie filtere ich das heraus.
>   for (int i=0; i<3; ++i) {
>     for (int j=0; j<3; j++) {
>       for (int k=0; k<3; k++) {
>         cout << i << j << k << endl;
>       }
>     }
>   }

Karl schrieb:
> Danke. So eine Lösung hatte ich auch schon.

Und wieso fragst du dann danach?

von Klaus W. (mfgkw)


Lesenswert?

Arc Net schrieb:
> Darf's C++ sein?
> Dann könnte das mit next_permutation
> http://www.sgi.com/tech/stl/next_permutation.html  und einer passenden
> Sortierung/Filterung gelöst werden.
>
> (funktionale Sprachen sind bei so etwas deutlich im Vorteil)

Ja, mit C++ könnte man hier wahrscheinlich vieles reißen.
Aber auch da wird man vorher wissen müssen, was man will und das
auch formulieren können.

Je nachdem, ob wirklich die beste Lösung nötig ist (was ohne
zusätzliches Wissen vom Problem an irgendeiner Form von brute
force nicht vorbeiführen wird) oder nur eine sehr gute nahe der
besten reicht, kann man auch ganz andere Algorithmen in Betracht
ziehen. Aber wer weiß das schon!

von Karl H. (kbuchegg)


Lesenswert?

Karl schrieb:
> Mein Problem ist, dass es keine Dopplungen geben darf. Also AAA ist
> nicht erlaubt. Wie filtere ich das heraus.

Im Allgemeinen Fall dadurch, dass du dir eine Funktion schreibst, die 
die aktuell untersuchte Lösung auf Zulässigkeit prüft. Ist die Lösung 
nicht zulässig, dann wird sie gleich wieder verworfen.

>
>
1
>   for (int i=0; i<3; ++i) {
2
>     for (int j=0; j<3; j++) {
3
>       for (int k=0; k<3; k++) {
4
>         cout << i << j << k << endl;
5
>       }
6
>     }
7
>   }
8
>
>
> Schön wäre natürlich eine Rekursive Lösung.

Und damit bist du dann beim allgemeinen Backtracking Ansatz

void Untersuche( Aufgabenparameter )
{
  if( ist die Lösung komplett( Aufgabenparameter ) ) {

    eventuell:
    if( Lösung mittels( Aufgabenparameter ) ist besser als
        bisherige Lösungen )
      Aufgabenparameter als bisher beste Lösung peichern

    ansonsten
      Lösung ausgeben
  }

  else {
    variiere Aufgabenparameter systematisch in allen
    Möglichkeiten für einen etwas längeren Lösungsversuch {

      if( ist zulässig( Aufgabenparameter ) )
        Untersuche( Aufgabenparameter );
    }
  }
}

An deinem Beispiel, kanonisch umgesetzt
1
int constainsDuplicates( char * String )
2
{
3
  char * Check = String + 1;
4
5
  while( *String ) {
6
    Check = String + 1;
7
    while( *Check ) {
8
      if( *String == *Check )
9
        return 1;
10
      Check++;
11
    }
12
    String++;
13
  }
14
  return 0;
15
}
16
17
void Permute( int Length, char* String )
18
{
19
  if( Length == 3 ) {
20
    printf( "%s\n", String );
21
    return;
22
  }
23
24
  char c;
25
  for( c = 'A'; c < 'D'; ++c ) {
26
    String[Length] = c;
27
    String[Length+1] = '\0';
28
29
    if( !constainsDuplicates( String ) )
30
      Permute( Length + 1, String );
31
  }
32
}
33
34
int main()
35
{
36
  char Test[10];
37
38
  Permute( 0, Test );
39
40
  return 0;
41
}

Für einzelne Aufgabenstellungen gibt es des öfteren Vereinfachungen, wie 
zb in dieser Aufgabenstellung. Aber darum ging es mir nicht. Ich wollte 
absichtlich die Backtracking-Idee einfach 1:1 in ein Programm umsetzen.

Der kanonische Backtracking-Ansatz funktioniert allerdings immer. Er 
funktionioniert so gut, dass eine ganze Programmiersprache darum herum 
gebaut wurde: Prolog

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Ansonsten gibt es auch noch dies hier:
http://de.wikipedia.org/wiki/Lineare_Optimierung

Allerdings fand ich im Studium Backtracing irgendwie eingänglicher ;)

von mr. mo (Gast)


Lesenswert?


von Karl H. (kbuchegg)


Lesenswert?

mr. mo schrieb:
> also hier ist was ganz interessantes dazu :
>
> 
http://openbook.galileocomputing.de/c_von_a_bis_z/022_c_algorithmen_006.htm#mjf7d6e5fb93d300365b9df366326bcc13



Vorsicht: "Brute Force"
ist kein Name eines Algorithmuses, sondern bedeutet einfach nur 'Rohe 
Gewalt'.
Gemeint ist, dass man ein Problem löst, indem man einfach rohe 
Rechengewalt darauf loslässt. Also nicht algorithmisch elegant an ein 
Problem rangehen, überlegen, Zusammenhänge suchen, sondern einfach mit 
der vollen Rechenpower die man zur Verfügung hat das Problem knacken. In 
gewissen Sinne so, wie einst Alexander den gordischen Knoten gelöst hat: 
Nicht lange nach dem Schnurenden suchen sondern mit dem Schwert 
draufhauen.

von D. I. (Gast)


Lesenswert?

Gerade bei Optimierungsproblemen sollte man sich Gedanken über 
dynamische Programmierung machen, meist kommt da was schlaues bei rum, 
was deutlich effizienter ist als Brute Force.
Noch dazu kommt dass es Probleme gibt, bei denen man nicht mit einem 
Greedy-Ansatz das Optimum findet und man dann auch zu dynamischer 
Programmierung greift.

Einfaches Einsteigerbeispiel:

Gegeben sei eine Kette von Matrixmultiplikationen (Gültigkeit 
vorausgesetzt) und die Dimensionen der einzelnen Matrizen:

A x B x C x D x ... x Z

Wie muss man klammern um die minimale Anzahl an Operationen zu 
erreichen? Ginge auch Brute Force, aber eben brutal langsam ;)

Beispiel:

A = 1000x1000
B = 1000x900
C = 900x2
D = 2x400

Wie klammert man A x B x C x D so dass ich möglichst wenig rechne?

((A x B) x (C x D))
(((A x B) x C) x D)
(A x (B x (C x D)))
((A x (B x C)) x D)
(A x ((B x C) x D))

wären da die Möglichkeiten.

Gute Lektüre dazu:

http://www.amazon.de/Introduction-Algorithms-Thomas-H-Cormen/dp/0262533057/ref=sr_1_1?ie=UTF8&qid=1293225360&sr=8-1

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.