Autor Beitrag
cerebrum
Hält's aus hier
Beiträge: 10



BeitragVerfasst: Sa 28.09.02 18:37 
Hallo!
Wie kann ich bei Angabe einer Funktionsvorschrfit (z.B. f(x)=x^2) diesen Graphen auf einem Canvas darstellen? :?:
tommie-lie
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 4373

Ubuntu 7.10 "Gutsy Gibbon"

BeitragVerfasst: Sa 28.09.02 19:52 
Wie du's auf Kästchenpapier auch machen würdest. Du legst einen Pixel in der horizontalen als 0-Wert fest. Dann gibst du der Funktion die x-Werte (also horizontale Pixel, gehend von 0) als Parameter und mit dem Rückgabewert gehst du bei den Pixeln in die Höhe. Allerdings wird dann der Graph bei Werten unter 100 ein bisschen klein... Man müsste dann evtl 10 Pixel für 1 setzen und die Funktion mit Dezimalzahlen rechnen, also 0,1; 0,2 usw, damit man auch die Werte zwischen diesen 10 Pixeln erfasst.

_________________
Your computer is designed to become slower and more unreliable over time, so you have to upgrade. But if you'd like some false hope, I can tell you how to defragment your disk. - Dilbert
cerebrum Threadstarter
Hält's aus hier
Beiträge: 10



BeitragVerfasst: Sa 28.09.02 20:22 
Also habe ich dann in einer Variablen die Funktion stehen und ersetze x immer durch einen höheren Wert. Aber wie kann ich dann den Ausdruck in der Variablen so ausführen lassen, dass er einen Wert zurückliefert???
MathiasH
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 699

WinXP, Win98SE, Debian, Win95
D5 Stand, D6 Prof
BeitragVerfasst: So 29.09.02 11:46 
dafür brauchst du nen Parser, der die Funktion analysiert und dann berechnet, ist verdammt schwer zu bauen :arrow: such mal nach "(Math)Parser"

MathiasH

_________________
"Viel von sich reden, kann auch ein Mittel sein, sich zu verbergen."
Friedrich Nietzsche
mazias
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 19



BeitragVerfasst: Mo 30.09.02 16:15 
...so einen Parser zu bauen ist gar nicht mal so schwer:
Man kann jede Gleichung so zerlegen das immer zwei Operanden alleine stehen, z.B: a*b*c*d = a*(b*(c*d))). Jetzt braucht man ne Rekursive Funktion die sich immer selbst aufruft wenn ne klammer kommt. Das ist im Prinzip schon alles, beim Zerlegen der Gleichung noch auf Punkt vor Strichrechnung achten und das ist dann das Wesentliche.

-Matze
MathiasH
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 699

WinXP, Win98SE, Debian, Win95
D5 Stand, D6 Prof
BeitragVerfasst: Mo 30.09.02 19:19 
naja eine funktion besteht aus mehr als *+-/() !!!!!

MathiasH

_________________
"Viel von sich reden, kann auch ein Mittel sein, sich zu verbergen."
Friedrich Nietzsche
DeCodeGuru
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1333
Erhaltene Danke: 1

Arch Linux
Eclipse
BeitragVerfasst: Mo 30.09.02 20:25 
Zitat:
naja eine funktion besteht aus mehr als *+-/() !!!!!


Wo er recht hat, hat er recht :mrgreen: Außerdem muss man noch beachten, dass Punkt- vor Strich-Rechnung gilt -> Du musst auch auf die Reihenfolge achten, wie die einzelnen Schritte ausgeführt werden.

_________________
Viele Grüße
Jakob
mazias
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 19



BeitragVerfasst: Mo 30.09.02 21:43 
...also wenn ihrs mir nicht glaubt werd ich mal ein bisschen präziser:
Zuerst machen wir eine Liste mit allen gängigen Operanden, geordnet so das Punkt vor Strich kommt und Potenzen vor Punkt und Sinus und Cosinus usw...
Dann nehmen wir uns den Funktionsstring und durchsuchen ihn nach Operanden(natürlich zuerst die höherwertigen) und setzen unsere Klammern links und rechts, und dann das ganze nochmal bis immer max 2 Argumente links und rechts neben einem Operanden stehen [z.B: f*a*b^2+d/c = f*a*(b^2)+d/c = (f*a)*(b^2)+(d/c) = ((f*a)*(b^2))+(d/c).
Das dann auszurechnen ist kein Problem (rekursive Funktion)...
Addy
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 43



BeitragVerfasst: Di 01.10.02 11:53 
Die Technik, durch Klammer-Setzen eine Priorität der Operanden zu erzwingen, habe ich vor einiger Zeit auch schon probiert. Das Ergebnis: der Programmcode wurde sehr kompliziert und der Algorithmus fehleranfällig.

Anderer Vorschlag (ungetestet): der gesamte Term wird geparst und in seine Bestandteile zerlegt (ein Bestandteil ist z.B. ein Operand, eine Zahl oder eine Klammer), die in einem dynamischen Array vom Typ (symbolischer Name :wink:) TBestandteil gespeichert werden. Jedes Element vom Typ TBestandteil repräsentiert entweder eine Zahl, einen Operanden etc., aber immer nur einen Bestandteil. Der Term
5*(a-3.5)
wird in die Bestandteile
5
*
(
a
-
3.5
)
zerlegt. Nun werden zuerst die Klammern ausgerechnet (höchste Priorität). Da Klammern auch geschachtelt sein können, empfiehlt es sich, eine rekursive Funktion zu benutzen, die den Wert der Klammer zurückliefert. Dabei sollte der Gesamtterm 5*(a-3.5) als (5*(a-3.5)) betrachtet werden, damit für Klammern und Gesamtterm ein und dieselbe Funktion benutzt werden kann.
In diesem aktuellen Zwischenterm a-3.5 werden zuerst alle Variablen durch ihre Werte ersetzt (z.B. bei a=4: 4-3.5). Danach werden die Rechenzeichen ihrer Priorität nach gesucht (hier: das Minuszeichen) und ausgewertet: Die Bestandteile links und rechts des Operanden werden gemäß dem Operanden miteinander ... ääh ... operiert :wink: und alle 3 Bestandteile werden durch einen ersetzt, nämlich dem Ergebnis der Operation (die Zahl, hier: 0.5). Wenn in der gesamten Klammer nur noch ein Bestandteil (eine Zahl) steht, wird die öffnende und die schließend Klammer aus dem Array gelöscht, so dass nur noch die Zahl vorhanden ist. Unser Bestandteil-Array sieht dann so aus:
5
*
0.5
Das wird dann wie oben beschrieben ausgewertet, bis wieder nur noch eine Zahl (2.5) dasteht: das Ergebnis :beer:.

Ich glaube, das Prinzip ist klar. Es hört sich alles sehr aufwändig und kompliziert an, aber ich bin sicher, es ist es nicht.
Die Vorteile sind (u.a.):
- Nachdem der String in das Bestandteil-Array eingelesen wurde, muss man nie mehr an einem String rumfummeln, was die Geschwindigkeit erhöht, den Quellcode wesentlich verkürzt, vereinfacht und fehlersicherer macht. Dieser Vorteil ist auf keinen Fall zu verachten.
- Nachdem die Grundstruktur vorhanden ist, kann man mit minimalem Aufwand weitere Operatoren hinzufügen. Ebenfalls ein sehr wichtiger Vorteil.
mazias
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 19



BeitragVerfasst: Di 01.10.02 18:29 
hi,
ich hab mir grad 2 Std. freigenommen und mal meine Theorie in die Praxis umgesetzt. Insgesamt sind es grade mal 200 Zeilen, und es funktioniert perfekt.
Der Algorhytmus schafft z.B. so ne Formel: 7,126*x^1.3/( x*X+(7+ 3*(x-x^ (2*x ))/x))

da man hier leider nicht's hochladen kann, poste ich mal den ganzen Quelltext

ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, math;

type
  pcalcpart = ^tcalcpart;
  tcalcpart = record
                  left, right: pcalcpart;
                  op         : integer;
                  x,
                  hasvalue   : Boolean;
                  value      : extended;
                  text       : string;
              end;

  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    Memo1: TMemo;
    TreeView1: TTreeView;
    Button2: TButton;
    Edit_x: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    compformel: pcalcpart;
    { Public declarations }
  end;

const
  ops: Array[1..5] of String = ('-', '+', '/', '*', '^');

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure display(text: string);
begin
    form1.memo1.lines.add(text);
end;

function createcalcpart(ptext: string = ''): pcalcpart;
begin
    result := new(pcalcpart);
    with result^ do begin
        left := nil; right := nil; op := -1; x := false; hasvalue := false;
        text := ptext;
    end;
    // debug
    display(result.text);
end;

procedure displaystructure(p: pcalcpart; node: ttreenode; treeview: ttreeview);
begin
    node.Text := p.text;
    if p.left <> nil then
        displaystructure(p.left, treeview.Items.AddChild(node,''), treeview);
    if p.right <> nil then
        displaystructure(p.right, treeview.Items.AddChild(node,''), treeview);
    if p.x = true then
        node.Text := node.Text + 'X';
    if p.hasvalue then
        node.Text := node.Text + floattostr(p.value);
end;

procedure decode1(p: pcalcpart);
var c, d : integer;
    depth: byte;
    newp : pcalcpart;
begin
    depth := 0;
    if p.text = '' then
        exit;
    // gucken ob Operator vorhanden ist
    for d := low(ops) to high(ops) do begin
        c := 1;
        while c <= length(p.text) do begin
            // Klammern umgehen
            if p.text[c] = '(' then
                inc(depth);
            if p.text[c] = ')' then
                dec(depth);
            if depth > 0 then begin
                inc(c);
                continue;
            end;
            if depth < 0 then begin
                showmessage('Flasche Klammersetzung');
                exit;
            end;
            // niederwertigsten Operator suchen
                if copy( p.text, c, length(ops[d]) ) = ops[d] then begin
                    p.left  := createcalcpart( copy(p.text, 1, c - 1) );
                    p.right := createcalcpart( copy(p.text, c + length(ops[d]), length(p.text) - c - length(ops[d]) + 1) );
                    decode1(p.left);
                    decode1(p.right);
                    p.text := ops[d];
                    // wir haben einen gefunden und sind hier fertig
                    exit;
                end;
            inc(c);
        end;
        // Falsche Klammersetzung
        if depth <> 0 then begin
            showmessage('Flasche Klammersetzung');
            exit;
        end;
    end;
    // wir haben keinen Operator gefunden, das kann folgende Gründe haben...
    // 1. Nur vorne und hinten eine Klammer, z.B.: '(x+y)'
    if (p.text[1] = '(')and(p.text[length(p.text)] = ')')then begin
        p.text := copy(p.text, 2, length(p.text) - 2);
        decode1(p);
        exit;
    end;
    // 2. Nur noch ein X da
    if p.text = 'X' then begin
        p.x := true;
        p.text := '';
        exit;
    end;
    // 3. Nur noch ne Zahl da
    for c := 1 to length(p.text) do
        if not(p.text[c] in ['0'..'9', ',']) then
            break;
    if c = length(p.text) + 1 then begin // die Schleife ist durchgelaufen also ist es ne Zahl
        p.hasvalue := true;
        p.value := strtofloat(p.text);
        p.text := '';
        exit;
    end;
    // 4. Fehler
    showmessage('Fehlerhafte Formel.');
end;

function calc(p: pcalcpart; x: extended): extended;
var left, right: extended;
begin
    if p.text <> '' then begin
        left  := calc(p.left , x);
        right := calc(p.right, x);
        if p.text = '-' then result := left - right;
        if p.text = '+' then result := left + right;
        if p.text = '*' then result := left * right;
        if p.text = '/' then result := left / right;
        if p.text = '^' then result := power(left, right);
    end;
    if p.x then
        result := x;
    if p.hasvalue then
        result := p.value;
    // debug
    display(floattostr(result));
end;

procedure TForm1.Button1Click(Sender: TObject);
var c: integer;
    s: string;
begin
    s := edit1.Text;
    // Schritt 1
    // spaces raus, punkt zu komma
    for c := length(s) downto 1 do
        if s[c] = ' ' then
            delete(s, c, 1)
        else
            if s[c] = '.' then
                s[c] := ',';
    s := uppercase(s);
    // Schritt 2
    // Computergerecht zerlegen
    compformel := createcalcpart(s);
    decode1(compformel);
    displaystructure(compformel, treeview1.Items.add(nil, 'Formel'), treeview1);
end;

procedure TForm1.Button2Click(Sender: TObject);
var res: extended;
begin
    display('----');
    res := calc(compformel, strtofloat(edit_x.Text));
    display('----------');
    display( 'f(' + edit_x.text + ') = ' + floattostr( res ));
end;

end.


und hier die dfm
ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
object Form1: TForm1
  Left = 281
  Top = 127
  Width = 702
  Height = 342
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 16
    Top = 48
    Width = 14
    Height = 13
    Caption = 'x ='
  end
  object Label2: TLabel
    Left = 16
    Top = 16
    Width = 23
    Height = 13
    Caption = 'f(x) ='
  end
  object Edit1: TEdit
    Left = 48
    Top = 16
    Width = 361
    Height = 21
    TabOrder = 0
    Text = '7,126*x^1.3/( x*X+(7+  3*(x-x^ (2*x ))/x))'
  end
  object Button1: TButton
    Left = 416
    Top = 16
    Width = 105
    Height = 25
    Caption = 'Build Compformel'
    TabOrder = 1
    OnClick = Button1Click
  end
  object Memo1: TMemo
    Left = 8
    Top = 88
    Width = 681
    Height = 209
    Lines.Strings = (
      'vor dem berechnen erst auf "build compformel" clicken!')
    TabOrder = 2
  end
  object TreeView1: TTreeView
    Left = 528
    Top = 16
    Width = 161
    Height = 65
    Indent = 19
    TabOrder = 3
  end
  object Button2: TButton
    Left = 416
    Top = 48
    Width = 105
    Height = 25
    Caption = 'Calc'
    TabOrder = 4
    OnClick = Button2Click
  end
  object Edit_x: TEdit
    Left = 48
    Top = 48
    Width = 361
    Height = 21
    TabOrder = 5
    Text = '3'
  end
end


probierts mal aus und sagt mir eure Meinung

tschö
-Matze
Addy
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 43



BeitragVerfasst: Fr 04.10.02 14:20 
Das scheint ungefähr das zu sein, was ich gesagt habe...