downhill simplex
-
Hi habe bis jetzt nur einen Bergsteiger Algorithmus geschrieben. Nun wollte ich mich an einen Simplex rantasten. ( Habe einen fertigen gefunden )
simplex downhill Algorithmus :
Hier der Code : Würde mich sehr freuen wenn sich jemand meine Notizen anschaut ,ob ich mit meinen Vermutungen richtig liege. Ganz besonders in Bezug auf die 3 Punkte
P[1][1]= 1.0; P[1][2]=2.0;…………..
Außerdem wenn ich ein Maximum suche müsste ich doch nur die Vorzeichen ändern , also aus < wird >. Habe es auch so bei meinem selbstgeschriebenen Bergsteiger Alg. Gemacht. .lg/********************************************************** * Multidimensional minimization of a function FUNC(X) * * where X is an NDIM-dimensional vector, by the downhill * * simplex method of Nelder and Mead. * * ------------------------------------------------------- * * SAMPLE RUN: Find a minimum of function F(x,y): * * F=Sin(R)/R, where R = Sqrt(x*x+y*y). * * * * Number of iterations: 22 * * * * Best NDIM+1 points: ( Die Koordinaten ) * * 4.122686 1.786915 * * 4.166477 1.682698 * * 4.142454 1.741176 * * * * Best NDIM+1 mimimum values: //DER Y_WERT * * -0.2172336265 * * -0.2172336281 * * -0.2172336271 * * * * ------------------------------------------------------- * * REFERENCE: "Numerical Recipes, The Art of Scientific * * Computing By W.H. Press, B.P. Flannery, * * S.A. Teukolsky and W.T. Vetterling, * * Cambridge University Press, 1986" * * [BIBLI 08]. * * * * C++ Release By J-P Moreau, Paris. * **********************************************************/ #include <stdafx.h> #include <iostream.h> #include <stdio.h> #include <math.h> #define MP 22 //Anzahl der Iterationen #define NP 21 //Maximum value for NDIM=20 //Anzahl der Parameter "Dimension des Parameterraums" typedef double MAT[MP][NP]; MAT P; double Y[MP], PT[MP]; int I,ITER; //Anzahl der Iterationsschritte int J,NDIM; // NDIM = Dimensionen double FTOL; // Ist der Kleinste Funktionswert ? // Toleranzen // user defined function to minimize double FUNC(double *P) { double R; R=sqrt(P[1]*P[1]+P[2]*P[2]); if (fabs(R) < 1e-12) return 1.0; else return sin(R)/R; } void AMOEBA(MAT P, double *Y, int NDIM, double FTOL, int *ITER) { /*------------------------------------------------------------------- ! Multidimensional minimization of the function FUNC(X) where X is ! an NDIM-dimensional vector, by the downhill simplex method of ! Nelder and Mead. Input is a matrix P whose NDIM+1 rows(Zeilen) are NDIM- ! dimensional vectors which are the vertices ( Ecken )of the starting simplex ! (Logical dimensions of P are P(NDIM+1,NDIM); physical dimensions ! are input as P(NP,NP)). Also input is the vector Y of length NDIM ! +1, whose components must be pre-initialized to the values of FUNC ! evaluated at the NDIM+1 vertices (rows) of P; and FTOL the fractio- ! nal convergence tolerance to be achieved in the function value. On ! output, P and Y will have been reset to NDIM+1 new points all within ! FTOL of a minimum function value, and ITER gives the number of ite- ! rations taken. Zusammengefasst: - In der Funktion FUNC(x) soll das Minimum gefunden werden. - X ist ein N-Dimensioaler Vektor. - Y ist der Eingangsverktor - Als eingangswert dient die Matrix P mit Dimension+1 Zeilen welche die Eckt Punkte ( Anfangswerte) beschreibt. - P(NP,NP) beschreibt die Anzahl der Parameter !-------------------------------------------------------------------*/ // Label: e1 const int NMAX=20, ITMAX=500; //Maximale Anzahl von Dimensionen und Interationen //Expected maximum number of dimensions, three parameters which define // the expansions and contractions, and maximum allowed number of //iterations. double PR[MP], PRR[MP], PBAR[MP]; double ALPHA=1.0, BETA=0.5, GAMMA=2.0; int I,IHI,ILO,INHI,J,MPTS; double RTOL,YPR,YPRR; MPTS=NDIM+1; *ITER=0; e1:ILO=1; if (Y[1] > Y[2]) { IHI=1; INHI=2; } else { IHI=2; INHI=1; } for (I=1; I<=MPTS; I++) { if (Y[I] < Y[ILO]) ILO=I; if (Y[I] > Y[IHI]) { INHI=IHI; IHI=I; } else if (Y[I] > Y[INHI]) if (I != IHI) INHI=I; } //Compute the fractional range from highest to lowest and return if //satisfactory. RTOL=2.0*fabs(Y[IHI]-Y[ILO])/(fabs(Y[IHI])+fabs(Y[ILO])); if (RTOL < FTOL) return; //normal exit if (*ITER == ITMAX) { printf(" Amoeba exceeding maximum iterations.\n"); return; } *ITER= (*ITER) + 1; for (J=1; J<=NDIM; J++) PBAR[J]=0.0; for (I=1; I<=MPTS; I++) if (I != IHI) for (J=1; J<=NDIM; J++) PBAR[J] += P[I][J]; for (J=1; J<=NDIM; J++) { PBAR[J] /= 1.0*NDIM; PR[J]=(1.0+ALPHA)*PBAR[J] - ALPHA*P[IHI][J]; } YPR=FUNC(PR); if (YPR <= Y[ILO]) { for (J=1; J<=NDIM; J++) PRR[J]=GAMMA*PR[J] + (1.0-GAMMA)*PBAR[J]; YPRR=FUNC(PRR); if (YPRR < Y[ILO]) { for (J=1; J<=NDIM; J++) P[IHI][J]=PRR[J]; Y[IHI]=YPRR; } else { for (J=1; J<=NDIM; J++) P[IHI][J]=PR[J]; Y[IHI]=YPR; } } else if (YPR >= Y[INHI]) { if (YPR < Y[IHI]) { for (J=1; J<=NDIM; J++) P[IHI][J]=PR[J]; Y[IHI]=YPR; } for (J=1; J<=NDIM; J++) PRR[J]=BETA*P[IHI][J] + (1.0-BETA)*PBAR[J]; YPRR=FUNC(PRR); if (YPRR < Y[IHI]) { for (J=1; J<=NDIM; J++) P[IHI][J]=PRR[J]; Y[IHI]=YPRR; } else for (I=1; I<=MPTS; I++) if (I != ILO) { for (J=1; J<=NDIM; J++) { PR[J]=0.5*(P[I][J] + P[ILO][J]); P[I][J]=PR[J]; } Y[I]=FUNC(PR); } } else { for (J=1; J<=NDIM; J++) P[IHI][J]=PR[J]; Y[IHI]=YPR; } goto e1; } void main() { NDIM=2; // Anzahl der Variablen FTOL=1e-8; // tolerance //define NDIM+1 initial vertices (one by row) // Die Eckpunkte werden hier deferiert ( Alles Vermutungen ) P[1][1]= 1.0; P[1][2]=2.0; //Pro Zeile immer nur einer wir haben 2 Variablen (2 Dimensionen) also müssen wir 3 Punkte Definieren P[2][1]=-2.0; P[2][2]=-3.0; //Wenn ich richtig liege, gibt man für jeden Parameter zwei verschiedene Werte vor, berechnet den ersten Punkt P[3][1]= 4.0; P[3][2]=2.0; // mit allen Erstwerten und die anderen NPunkte auch mit den Erstwerten, nur das jeweils einer der Parameter durch seinen // zweitwert ersetzt wird. // Wir berechnen an jedem Punkt 1,2,-2,-3,4,2 die Funktion und dadurch wird unser Kordinatensatz verändert // Der eine Wert beschreibt die Koordinate der andere seinen Funktionswert // Oder ist er nur ein Funktionswert erweitert um eine Dimension //Initialize Y to the values of FUNC evaluated // In die Y Martix packen wir unsere Anfangswerte //at the NDIM+1 vertices (rows] of P for (I=1; I<=NDIM+1; I++) { PT[1]=P[I][1]; PT[2]=P[I][2]; Y[I]=FUNC(PT); } //call main function AMOEBA(P,Y,NDIM,FTOL,&ITER); //print results printf("\n Number of iterations: %d\n\n", ITER); printf(" Best NDIM+1 points:\n"); for (I=1 ; I<=NDIM+1; I++) { for (J=1; J<=NDIM; J++) printf(" %f", P[I][J]); printf("\n"); } printf("\n Best NDIM+1 mimimum values:\n"); for (I=1; I<=NDIM+1; I++) printf(" %14.10f\n", Y[I]); printf("\n"); system( "pause" ); } // end of file tamoeba.cpp
-
ich hoffe hier im Forum ist jemand der sich die mühe kurz macht. Würde mich sehr freuen
-
Tu dir einen Gefallen und such eine andere Implementierung. Diese hier ist einfach nur grauenhaft.
-
meinst du damit meine Notizen ?
//...das wird so nicht bleiben sind wie Notizzettel.
-
Nein, nicht die Notizen. Ich meine den Code den du gefunden hast. Dich trifft keine Schuld
-
du meinst wegen goto ?
nobody is perfect.
Aber trotzdem könntest du mir weiter helfen ?
-
worauf wir raus wollen:
double PR[MP], PRR[MP], PBAR[MP]; double ALPHA=1.0, BETA=0.5, GAMMA=2.0; int I,IHI,ILO,INHI,J,MPTS; double RTOL,YPR,YPRR;
Das dechiffriert hier niemand.
-
http://de.wikipedia.org/wiki/Downhill-Simplex-Verfahren
wiki hat dazu was interessantes.// Deklarationen
// Benutzervorgaben, -eingaben
int NP= ... ; // Anzahl der Parameter, die in FQS() eingehen,
// auch "Dimension des Parameterraums"double ParP[NP]={ ... }; // primärer Parametersatz
double ParS[NP]={ ... }; // sekundärer Parametersatz, gibt Schwankungsbreite vordouble FQS(int pf) // Zu minimierende Funktion, hängt
{ // von den Parametern Par0[pf][...] ab.
... // Muss je nach Problemstellung erstellt werden.
// So schreiben, dass sie für den Parametersatz Nr. pf
} // berechnet und am Schluss in Par0[pf][0] (s. u.)
// abgespeichert wird.// Ende Benutzervorgaben
// Initialisierung
// Simplex-Parameter
double NA = 1, NB = .5, NC = 2; // Alpha, Beta, Gamma// Parametersätze, NP+1 Stück für den Simplex selbst, plus 3 weitere
double Par0[NP+4][NP+1];
// darin 1. Index: 0..NP die (NP+1) Parametersätze des Simplex,
// dabei auf 0 immer der schlechteste Punkt des Simplex,
// auf 1 immer der beste Punkt des Simplex
// NP+1 Punkt P*, s. u.
// NP+2 Punkt P**, s. u.
// NP+3 Mittelpunkt P-Quer des Simplex, s. u.
// darin 2. Index: 0 Fehlerwert für diesen Satz
// 1..NP einzelne Parameterwerte dieses Satzes// Initialisierung
// Simplex aus Primär- und Sekundärwerten
Parametersätze 0 bis NP mit Primärwerten belegen;
In Parametersätzen 1 bis NP (i) jeweils
Parameter Nr. i mit Sekundärwert belegen;// Berechnung der Anfangs-Fehlersummen
für Parametersätze 0 bis NP: feh=FQS(i); // speichert Ergebnis in Par0[i][0], s. o.// Schleife für Iteration, Hauptschleife
bool fertig = false;
while (!fertig)
NT++; // Simplex-Schritt Nr. NT// schlechtesten und besten Punkt ermitteln (höchster bzw. niedrigster Fehlerwert)
Werte fmin und fmax auf Fehlerwert von Satz Nr. 0 vorbelegen;
für alle Parametersätze von 1 bis NP:
wenn Fehler für Satz Nr. i größer als fmax:
noch schlechterer Punkt, fmax auf diesen Wert, Punkt mit Nr. 0 tauschen
wenn Fehler für Satz Nr. i kleiner als fmin:
noch besserer Punkt, fmin auf diesen Wert, Punkt mit Nr. 1 tauschen// Konvergenzabfrage, sind wir fertig?
// Dies muss auch je nach Problem individuell erstellt werden,
// hier als Beispiel die Bedingung, dass alle Parameter nur noch um 1 Promille
// schwanken.fertig = true;
für alle Parameter in allen Parametersätzen von 0 bis NP:
wenn nur für einen Parameter die relative Abweichung des Wertes
zwischen schlechtestem und bestem Punkt größer als 0.001, dann:
fertig=false; // doch noch nicht fertig//Wenn fertig, dann beende mit bestem Punkt Par0[1][1..3] als Ergebnis des Algorithmus
// Mittelpunkt P-QUER des Simplex nach Index NP+3
Mittelwerte über Sätze 1 bis NP (also ohne schlechtesten Punkt Nr. 0!) bilden
und als Satz Nr. NP+3 speichern;// Punkt P* durch Reflexion des schlechtesten Punkts
// am Mittelpunkt P-QUER (mit Nelder/Mead-Parameter alpha=NA) nach Index NP+1
innerhalb eines Parametersatzes:
Par0[NP+1][i]=(1 + NA) * Par0[NP+3][i] - NA * Par0[0][i];
fs=Par0[NP+1][0]=FQS(NP+1); // und Fehler berechnen// Fallunterscheidung, ob Verbesserung erreicht
wenn fs<fmin // neuer Punkt P* im Vergleich zu bisher bestem
/ // ja, besser!
| // weiteren Schritt zu Punkt P** in dieselbe Richtung nach Index NP+2
| // (mit Nelder/Mead-Parameter gamma=NC)
| innerhalb eines Parametersatzes:
| Par0[NP+2][i]=(1 + NC) * Par0[NP+1][i] - NC * Par0[NP+3][i];
| fs=Par0[NP+2][0]=FQS(NP+2); // und Fehler berechnen
|
| wenn fs>=fmin // wieder Vergleich mit bisher bestem Punkt
| / // keine weitere Verbesserung: P** verwerfen, P* nach Index 0,
| | // also bisher schlechtesten Wert durch neuen (etwas besseren) ersetzen
| \ Parametersatz Nr. NP+2 nach Nr. 0 kopieren;
| else // von fs>=fmin
| / // ja, auch eine Verbesserung!
| | // auch besser als P* ?
| | wenn fs>Par0[NP+1][0]
| | / // nein, P* weiterverwenden und damit bisher schlechtesten auf Index 0 ersetzen
| | \ Parametersatz Nr. NP+1 nach Nr. 0 kopieren;
| | else // von fs>Par0[NP+1][0]
| | / // ja, P** weiterverwenden und damit bisher schlechtesten auf Index 0 ersetzen
\ \ \ Parametersatz Nr. NP+2 nach Nr. 0 kopieren;else // von fs<fmin
/ // nicht besser als bisher bester, nun prüfen, ob P* wenigstens überhaupt eine Verbesserung
| wenn fs von P* für wenigstens einen der Punkte 0..NP kleiner ist, dann:
| / // ja, besser als mindestens einer, mit P* den bisher schlechtesten Punkt auf Index 0 ersetzen
| \ Parametersatz Nr. NP+1 nach Nr. 0 kopieren;
| else // von fs von P*
| / // nein, weiterhin schlechter als die anderen Punkte
| | // Zusatzprüfung: sogar schlechter als bisher schlechtester Punkt fmax?
| | wenn fs>fmax // schlechter als bisher schlechtester?
| | // ja, erstmal nichts tun
| | else // von fs>fmax
| | / // nein, also wenigstens bisher schlechtesten Punkt damit ersetzen
| \ \ Parametersatz Nr. NP+1 nach Nr. 0 kopieren;
| // neuer Punkt P** durch Kontraktion zwischen bisher schlechtestem Punkt (Index 0)
| // und P-QUER (Index NP+3) nach Index NP+2 (mit Nelder/Mead-Parameter beta=NB)
| innerhalb eines Parametersatzes:
| Par0[NP+2][i]= NB * Par0[0][i] + (1 - NB) * Par0[NP+3][i];
| fs=Par0[NP+2][0]=FQS(NP+2); // und Fehler berechnen
| // P** besser als bisher schlechtester Punkt?
| wenn fs<fmax // besser als bisher schlechtester?
| / // ja, bisher schlechtesten durch P** ersetzen
| \ Parametersatz Nr. NP+2 nach Nr. 0 kopieren;
| else // von fs<fmax
| / // nein, keinerlei Verbesserung, Notfall
| | // folgt allgemeine Kompression um Faktor 2 um den bisher besten Punkt herum
| | für alle Parametersätze außer Nr. 1 (bisher bester): // also j=0 und 2..NP
| | innerhalb eines Parametersatzes j:
| | Par0[j][i]=(Par0[j][i] + Par0[1][i]) / 2;
\ \ fs=Par0[j][0]=FQS(j); // und Fehler berechnen
// Schleifenende