Übergabe einer Funktion per Tastatur



  • hallo,
    ich bin gerade dabei aus Spaß eine Numerische Integration zu programmieren. Also eine Näherung zur Berechnung eines Integrals. Eigentlich ist es nur ein bisschen plus minus mal geteilt Rechnung mit einfachen Formeln. Das Programm funktioniert.

    Das Problem ist nur, dass ich die zu integrierende Funktion immer mit in den Quelltext schreiben muss. Dies tue ich über:

    for (i=0; i<=10000; i++)
    {
    FunktionMittel[i]=Mittel[i]*Mittel[i]+3*Mittel[i]+2;

    }

    Die Funktion oben entspricht fx=x^2+3*x+2. Es werden Funktionswerte an den Stellen berechnet.

    Meine Frage ist, wie kann ich diese Funktion per Tastaturabfragen eingeben lassen??

    Kurz zum Hintergrund: Ich habe eine beliebiges Intervall mit einer beliebigen Funktion.

    Diese Teile ich in 1000 (oder 10 - egal wie viele) Intervalle. Berechne deren Mittelpunkte und dazu die Funktionswerte an diesen Stellen.

    Wenn ich die Schrittweite (bei 10 Teilen im Intervall 0 - 10 ist die Schrittweite 1) mit der Summe der gemittelten Funktionswerte multipliziere ist das meine Näherung für das Integral.

    Wen es interessiert, das gesamte Programm sieht so aus. Kritik, Anregung zum Quelltext sind auch erwünscht, wer möchte...

    #include <stdio.h>
    #include <stdlib.h>

    // Ziel ist die Formel Q(f)=H*(f(m1)+...-f(mn)), die das Integral näherungsweise berechnet.

    int main()
    {

    double H; //Intervallgröße
    double a; //Anfang des Intervalls
    double b; //Ende des Intervalls
    int L; //Anzahl der Intervalle bzw. Zerlegungen

    printf("Geben Sie die Intervallgrenze a ein\n");

    scanf ("%lf",&a);

    printf("Geben Sie die Intervallgrenze a ein\n");

    scanf ("%lf",&b);

    L=10001;

    H=(b-a)/L; //Berrechnung der Intervallgröße

    printf("Intervall von %.2f bis %.2f, Anzahl der Intervalle = %i, Intervallgrosse = %.2f \n\n\n", a, b, L, H);

    double m1; //erster Mittelpunkt
    m1=(a+a+H)/2;

    //Berechnung der Mittelpunkte
    int i;
    double Mittel[10000]; // L-1 könnte könnte auch =4 sein. Also 5-1 = 4

    for (i=0; i<=10000; i++)
    {
    Mittel[i]=m1+i*H;
    }

    double FunktionMittel[10000]; //Funktionswerte mit den Mittelpunkten des Intervalls als x Werte.

    for (i=0; i<=10000; i++)
    {
    FunktionMittel[i]=Mittel[i]*Mittel[i]+3*Mittel[i]+2; //entspricht fx=x*x+3*x+2; // <--- HIER EINGABE DER FUNKTION!!
    //FunktionMittel[i]=Mittel[i]*Mittel[i];
    }

    //Integral näherung berechnen
    double Sum;
    double Intgr; //Lösungsvariable des Integrals

    Sum=0;

    for (i=0; i<=10000; i++) //Aufsummieren der Funktionswerte
    {
    Sum=Sum+FunktionMittel[i];
    }

    Intgr=H*Sum; //Berechnung von Q(f)=H*(f(m1)+...-f(mn))

    printf("Das Integral von f(x)=x^2+3*x+2 im Intervall von %.2f bis %.2f lautet %f.\n\n", a, b, Intgr);

    //Ausgabe des Ergebnisses

    }



  • Geht nicht, wie sollte es auch? Wenn das Ding einmal läuft, muss kein Compiler mehr in der Nähe sein, der aber zum Verständnis eines Ausdruckes notwendig wäre. Also musst du den Ausdruck selbst auswerten.

    Vielleicht mit einem kleinen Interpreter, für Scheme, EMCAScript oder Lua?
    🙂



  • mitglied2347 schrieb:

    Wen es interessiert, das gesamte Programm sieht so aus. Kritik, Anregung zum Quelltext sind auch erwünscht

    Frag dich mal, ob es wirklich nötig ist, 10000 double's im Speicher zu halten.
    🙂



  • Mit Interpretern EMCAScripts oder Lua kenne ich mich leider überhaupt nicht aus... 😞



  • mitglied2347 schrieb:

    Mit Interpretern EMCAScripts oder Lua kenne ich mich leider überhaupt nicht aus... 😞

    Wenn du nicht allzuviele Operationen umsetzen willst, könntest du auch schnell selbst was schreiben. Das wird aber sicher umfangreicher als der Rest. Da war erst neulich was:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-262782.html

    Vielleicht bekommst du's einfacher hin, wenn du eine andere Notation einführst. Infix ist nicht unbedingt leicht zu parsen.
    🙂



  • µngbd schrieb:

    mitglied2347 schrieb:

    Wen es interessiert, das gesamte Programm sieht so aus. Kritik, Anregung zum Quelltext sind auch erwünscht

    Frag dich mal, ob es wirklich nötig ist, 10000 double's im Speicher zu halten.
    🙂

    ne nötig nicht, aber das ganze ist ja auch nicht für einen echtzeitsteuerung gedacht, sondern nur just for fun...



  • mitglied2347 schrieb:

    Das Problem ist nur, dass ich die zu integrierende Funktion immer mit in den Quelltext schreiben muss.

    Schreib einfach einen kleinen Parser, der dir aus dem eingegebenen Ausdruck einen Syntaxbaum macht, den du dann für jeden Variablenwert rekursiv auswerten kannst.

    Das ist natürlich nicht so effizient; statt einen paar Instruktionen, die dir gleich das Ergebnis ausrechnen, hast du dann einen Haufen einzelne Funktionsaufrufe für die ganzen Additionen und Multiplikationen usw., die in dem Ausdruck vorkommen. Du kannst es eventuell noch etwas optimieren, indem du bestimmte Teile des Baums vereinfachst, also z.b. Polynonome erkennst und die dann direkt ausrechnest.

    Aber wenn es wirklich so effizient wie möglich sein soll, bleibt nur die Möglichkeit, mit dem eingegebenen Ausdruch einen Compiler aufzurufen, der dir wirklich Code erzeugt. Das ist natürlich irgendwie eklig sowas zur Laufzeit zu machen.



  • Naja, die Kirche kann meines Erachtens vorläufig durchaus im Dorf bleiben; für diesen Fall dürfte der shunting yard algorithm eigentlich locker ausreichen.

    Auch ist er deutlich einfacher zu bewältigen als ein LL-Parser, was - ich entschuldige mich, mitglied2347, wenn ich deinen Kenntnisstand hier falsch einschätze - ein großer Vorteil sein dürfte. Rekursive Parser sind keine ganz simple Geschichte.



  • wie funktioniert denn so ein algorithmus kurz wörtlich beschrieben. Bei Wikipedia steht ja nur, wie es sortiert wird. Irgendwie müssen die Daten ja von der Eingabe dorthin gelangen, wo ich normalerweise, meine Gleichung in den Quelltext schreibe.



  • Die Daten werden als Zeichenkette (char Array) eingelesen und in ihre Bestandteile aufgeteilt ( geparst ).



  • Naja, letzten Endes schmeißt dir der Algorithmus die Gleichung in RPN aus, was ziemlich einfach auszuwerten ist. Letztendlich wird das im Code wohl etwa auf

    double evaluate_rpn(struct token rpn[], double x) {
      /* Auswertung hier */
    }
    

    hinauslaufen. Die Tokens selber musst du dir aus der Eingabe fummeln. Für einen einfachen Fall ohne Funktionstoken (wie sin, cos, etc.) kann das etwa so aussehen:

    static char const *const op_chars = "+-*/^()";
    
    enum token_type {
      tok_num,
      tok_op,
      tok_x
    };
    
    struct token {
      union {
        double num;
        char   op;
        char   var;
      } v;
    
      enum token_type type;
    };
    
    struct token *get_token(char const **pos, struct token *dest) {
      int n;
      double num;
      char const *line = *pos;
    
      while(line[0] == ' ') {
        ++line;
      }
    
      if(strchr(op_chars, line[0]) != NULL) {
        dest->v.op = line[0];
        dest->type = tok_op;
        *pos = line + 1;
      } else if(tolower(line[0]) == 'x') {
        dest->type = tok_x;
        *pos = line + 1;
      } else if(sscanf(line, "%lf%n", &num, &n) == 1) {
        dest->v.num = num;
        dest->type = tok_num;
        *pos = line + n;
      } else {
        return NULL;
      }
    
      return dest;
    }
    

    Aufruf dann etwa

    char *pos = line; /* line sei die eingelesene Zeile */
    struct token tok;
    
    while(get_token(&pos, &tok)) {
      /* Token verarbeiten */
    }
    


  • ok, der post kam etwas zu spät....

    aber wie wird aus den Daten wiederum code?? wenn ich eine Gleichung wie y=2x+4*cos(x) in einem String einlese, dann kann ich die einzelnen bestandteile in bestimmte typten aufteilen: Ziffern, Variablen, Operatoren. Bis dahin verstehe ich das.

    Aber wie setze ich das dann wieder so zusammen, dass damit gerechnet werden kann. Es bei mir nämlich so, dass ich für das x ein array einsetze, bei dem ich mit einer schleife bestimme, welches Element gerade ausgelesen wird. Ich möchte für jeden verschiedenen Zähler in meiner Schleife verschiedene Ergebnisse der Gleichung erhalten.



  • [quote="mitglied2347"]ok, der post kam etwas zu spät....

    aber wie wird aus den Daten wiederum code??[quote]
    Gar nicht. Du musst sie interpretieren.
    🙂



  • Letztendlich liefert dir der Shunting-Yard-Algorithmus eine Queue (wahlweise ein Array oder eine Liste) von Token, die die eingegebene Funktion in umgedrehter polnischer Notation bezeichnen. Was das ist und wie einfach die auszuwerten ist, steht im entsprechenden Wikipedia-Artikel, der vom Shunting-Yard-Algorithmus-Artikel, den ich hier verlinkt habe, verlinkt ist, beschrieben.

    Es ist möglich, den Shunting-Yard-Algorithmus so anzupassen, dass er direkt rechnet, aber da du die Funktion hier ja mehrmals mit verschiedenen x-Werten auswerten willst, macht das wenig Sinn.


Log in to reply