Formeln lösen per Autoit?

  • Ich habe mir heute in der Schule gedacht man kann doch sicher ein Programm erstellen welches in eine Formel mit einer Variablen eine Belibige Zahlenfolge einsetzt und irgendwann müsste das richtige Ergebnis ja dabei sein. Das habe ich auch versucht nur scheine ich irgendwo einen Fehler gemacht zuhaben da er mir immer auspuckt das die Lösung 0.01 ist jemand eine Idee?


    Spoiler anzeigen
    [autoit]

    #include <ButtonConstants.au3>
    #include <EditConstants.au3>
    #include <GUIConstantsEx.au3>
    #include <ProgressConstants.au3>
    #include <StaticConstants.au3>
    #include <WindowsConstants.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $z = 0 ;Wert der Test Zahl
    $ergebnis = "nicht null"

    [/autoit] [autoit][/autoit] [autoit]

    #Region ### START Koda GUI section ### Form=
    $Form1_1_1 = GUICreate("Variablen Rechner für Mathe", 381, 197, 192, 124)
    $links = GUICtrlCreateInput("", 8, 32, 169, 21)
    $Label1 = GUICtrlCreateLabel("Formel", 160, 8, 59, 24)
    GUICtrlSetFont(-1, 12, 800, 0, "MS Sans Serif")
    $help = GUICtrlCreateButton("Hilfe", 280, 56, 75, 25)
    GUICtrlSetFont(-1, 8, 800, 0, "MS Sans Serif")
    $Label5 = GUICtrlCreateLabel("Suche", 32, 88, 55, 24)
    GUICtrlSetFont(-1, 12, 800, 0, "MS Sans Serif")
    $Label6 = GUICtrlCreateLabel("Ergebnis", 32, 136, 75, 24)
    GUICtrlSetFont(-1, 12, 800, 0, "MS Sans Serif")
    $start = GUICtrlCreateButton("Start", 32, 56, 75, 25)
    GUICtrlSetFont(-1, 8, 800, 0, "MS Sans Serif")
    $Lable = GUICtrlCreateLabel("Getestete Zahlen", 272, 88, 91, 17)
    GUICtrlSetFont(-1, 8, 800, 0, "MS Serif")
    $ergebnis = GUICtrlCreateLabel("", 32, 160, 80, 30)
    GUICtrlSetFont(-1, 17, 400, 0, "MS Sans Serif")
    $rechts = GUICtrlCreateInput("", 208, 32, 169, 21)
    $Label2 = GUICtrlCreateLabel("=", 184, 24, 16, 30)
    GUICtrlSetFont(-1, 17, 400, 0, "MS Sans Serif")
    GUISetState(@SW_SHOW)
    #EndRegion ### END Koda GUI section ###

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
    Case $GUI_EVENT_CLOSE
    Exit
    Case $start
    _los()
    EndSwitch
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    Func _los()
    While 1
    $z = $z + 0.01

    [/autoit] [autoit][/autoit] [autoit]

    $linksgelesen = GUICtrlRead($links)
    $rechtsgelesen = GUICtrlRead($rechts)

    [/autoit] [autoit][/autoit] [autoit]

    $linksreplace = StringReplace($linksgelesen, "x", $z)
    $rechtsreplace = StringReplace($rechtsgelesen, "x", $z)

    [/autoit] [autoit][/autoit] [autoit]

    $linksexe=Execute(GUICtrlRead($linksreplace))
    $rechtsexe=Execute(GUICtrlRead($rechtsreplace))
    GUICtrlSetData($links,$linksexe)
    GUICtrlSetData($rechts,$rechtsexe)
    $ergebnis = $linksexe - $rechtsexe

    [/autoit] [autoit][/autoit] [autoit]

    If $ergebnis = 0 Then
    MsgBox(0, "", "Die Lösung ist " & $z & ".")
    ExitLoop
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $1 = StringReplace($linksreplace, $z, "x")
    $2 = StringReplace($rechtsreplace, $z, "x")

    [/autoit] [autoit][/autoit] [autoit]

    GUICtrlSetData($links,$1)
    GUICtrlSetData($rechts,$2)

    WEnd
    EndFunc ;==>_los

    [/autoit]

    Einmal editiert, zuletzt von Sortoc (22. November 2009 um 12:48)

  • Es geht mir auch weniger um das rechnen sondern eher um das Schreiben des Programmes die Gleichung ist halt [Linker Term - Rechter Term]=0 Dann wäre die für x eingesetzte Zahl richtig. Bin halt nochn Autoit anfänger da wollt ich nen bisschen rumprobiern das Programm sollte erstmal gucken ob im Linken oder/und im Rechten Term ein x ist das x soll dann in einer while schleife immer wieder gegen eine etwas größere Zahl ausgetauscht werden z.B. 1.Durchlauf 2+x = 8 x=1-> 2+1=8 -> 3-8 ungleich 0 also startet der nächste Durchlauf 2+x=8 -> 2+2=8 usw. bis am ende halt 0 raus kommt. Nur spuckt das Script immer in der ersten Runde schon aus das die Lösung 0.01 ist was bei 2+x=4 irgendwie nicht sein kann. :S

  • Hm ok. Meinst du sowas? Ist ein Risiko, denn wenn es keine Lösung gibt hängt das Skript in der Schleife fest

    Spoiler anzeigen
    [autoit]


    global $x = 0

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    while 1
    ;Term: 2x +1 = 9
    if $x <> (9-1)/2 Then
    $x += 1
    else
    MsgBox(1, "Lösung", "Die Lösung ist: " & $x)
    ExitLoop
    EndIf
    Sleep(100)
    wend

    [/autoit] [autoit][/autoit] [autoit]

    exit

    [/autoit]

    Einmal editiert, zuletzt von nuts (19. November 2009 um 21:32)

  • Die Aufgabenstellung die du dir vorgenommen hast ist tatsächlich sehr gut um programmieren zu üben.
    Du kannst auf die Art und Weise quasi beliebige Formeln lösen (bzw. sehr nahe an den tatsächlichen Wert annähern) solange sie nur von einer Variablen abhängen (geht auch mit mehr Variablen - ist dann aber nicht mehr so trivial).
    Interessant weil man Endlosschleifen, welche durchaus vorkommen können, sinnvoll abfangen muss.
    Außerdem kann man nur Lösungen in einem bestimmten Bereich erhalten - nie für eine gesamte Funktion.
    Also bei mehreren möglichen Lösungen wird man dennoch maximal eine Lösung erhalten.

    Ich empfehle dir daher die bereits existierenden gängigen Ansätze dazu mal durchzuarbeiten.
    Dies wären unter anderen das Bisektionsverfahren (auch Dichotomie genannt) - sehr einfach, der Newton-Algorithmus, Sekantenverfahren und Regula Falsi.

    Viel Erfolg

  • Ok ich werde mich mal an diese andere Verfahren klemme trozdem würde es mich freuen wenn sich nochmal jemand mein Script anguckt irgendwo muss ja ein fehler sein wenn er mir die "Startzahl" als Endergebnis auswirft. :pinch:

  • du kannst dir mal in Zeile 58 die Zwischenergebnisse angucken:

    [autoit]

    MsgBox(0,0,$rechtsexe&@CRLF&$linksexe)

    [/autoit]


    beide Ergebnisse sind = 0.. warum? weil du mit den beiden Zeilen:

    [autoit]

    $linksexe=Execute(GUICtrlRead($linksreplace))
    $rechtsexe=Execute(GUICtrlRead($rechtsreplace))

    [/autoit]


    eine Guicontrol ausliest die es nicht gibt (return = 0).. du solltest also
    Guictrlread() weglassen, dann könnte es klappen!
    Die weiteren Veränderungen der Inputwerte solltest du auch sein lassen.
    Ich würde an deiner stelle den String einlesen, als konstante speichern und
    bei jedem schleifendurchlauf den x-Wert erhöhen und dann ersetzen.
    Sollte das Ergebnis dann gleich sein, dann funktioniert es. :D
    (Etwas schwerer ist es sicherlich mithilfe eines Programms nach X aufzulösen,
    aber das kannste dir dann ja als nächstes Ziel setzen - ohne viele Spielereien wie:
    Wurzeln, E-Funktionen, Logarithmen etc. ;) )

    Ich hoffe ich konnte dir helfen und dir einen neuen Denkanstoß geben, um das "Projekt"
    weiterzuführen. Bei weiteren Fragen versuche ich weiter mitzuhelfen. :)

    Wer immer nur das tut, was er bereits kann - wird auch immer nur das bleiben, was er bereits ist!

  • Vielen Dank für den Tipp mit dem GuiCtrlRead doch das bringt mich auch schon zu meinem nächsten Problem er gibt mir jetzt zwar Integer als Lösung aus aber nicht alle Lösungen sind ganz Zahlig wenn ich also

    [autoit]

    For $zahl = 1 to 10 Step 0.001

    [/autoit]

    nehme und die schleife durchlaufen lasse passiert es das er nach dem ~70-80 Durchlauf stat 0.001 0.00000001 dazu gibt wodurch dann die Formel mit 4.1010000001 getestet wird was natürlich blöd ist.

    Gibt es eine möglichkeit die Nachkommastellen einer Variable zu "Maximiren" oder weis jemand eine Lösung das nicht 0.00000001 stat 0.001 verwendet wird?

    Hoffe man versteht das bin noch nicht ganz Wach :D

  • Schau dir mal das Newtonverfahren an, du startest mit einer ziemlich großen Schrittweite um den Nulldurchgang zu finden. Danach teilst du den letzten "Schritt" wieder in 10 weitere auf und suchst damit den Nulldurchgang der Funktion. Das heisst, du benutzt KEINE feste Schrittweite. Das machst du so lange, bis du ein genügend genaues Ergebnis hast. Also gewissermassen das "herantasten" an die Nullstelle von beiden Seiten. Natürlich kannst du damit nur lokale Nullstellen bestimmen (in der Umbebung um deinen Startpunkt).
    Bei z.B. 3 vorhandenen Nullstellen muß das Verfahren etwas "erweitert" werden. Du hangelst dich im Prinzip an der Funktion entlang und berechnest die Schrittweite je nach Tangentensteigung.
    Da immer dieselbe Schleife nur mit anderer Schrittweite durchlaufen werden muss, bietet sich die Rekursion hier besonders an.
    Viel zu tun^^, aber du hast es so gewollt....
    Hier die grafische Darstellung

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    Einmal editiert, zuletzt von Andy (22. November 2009 um 13:21)

  • 8| ich glaube ich warte mal lieber bis wir das Newton Thema in der Schule fertig haben ich blick da garnix. Es reicht ja das er mir die Ganzen Zahlen ausrechnet wir arbeiten eh noch nicht mit Komma stellen :D

  • Spoiler anzeigen
    [autoit]

    ; Utilty that utilizes Newton's method to find solutions to equations
    ; See http://en.wikipedia.org/wiki/Newton%27s_method for an explanation of the math.
    ; by Diego Hernandez < d (dot) hernandez 0 9 at g mail >

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    #include <Array.au3>
    #include <String.au3>

    [/autoit] [autoit][/autoit] [autoit]

    HotKeySet ("{ESC}", "Terminate") ; press ESC to emergency-exit

    [/autoit] [autoit][/autoit] [autoit]

    Local $expression = InputBox ("Enter equation", "E.g. x^2=16", "x^2=16") ; make sure you use * to multiply
    If @error Then Exit

    [/autoit] [autoit][/autoit] [autoit]

    $expression = "&" & $expression & "&"
    $right_side = _StringBetween ($expression, "=", "&")
    $left_side = _StringBetween ($expression, "&", "=")
    $expression = $left_side[0] & " - (" & $right_side[0] & ")" ; one side of the equation is now 0; now apply Newton's method.

    [/autoit] [autoit][/autoit] [autoit]

    Local $x_guesses_array[2] ; every 2000 iterations (this is more than enough in many cases) the program will compare
    ; the values for the root and if it has converged enough, it will display the answer in a MsgBox.
    $x_guess = Number(InputBox ("Enter approximation for root", "Enter a guess reasonably close to the root so Newton's method can converge properly. Thank you!","1"))
    ; The program will find the root closest to your initial guess. If it is complex, it will enter into an infinite cycle.
    $c = 0

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    $x_guess = $x_guess - (_Fx($expression, $x_guess)/_SlopeOfTangent($expression, $x_guess))
    ; see http://en.wikipedia.org/wiki/Newton%27s_method for an explanation of the math behind Newton's method
    $c +=1
    If IsInt ($c/1000) = 1 and IsInt ($c/2000) = 0 Then
    $x_guesses_array[0] = $x_guess
    $x_guesses_array[1] = 0
    EndIf
    If IsInt ($c/2000) Then
    ; every 2000 iterations, the program will compare its current x value with the x-value stored from the previous 1000th iteration.
    ; if they are close enough, the root has been sufficiently determined. It will ExitLoop and display the root.
    $x_guesses_array[1] = $x_guess
    ; _ArrayDisplay ($x_guesses_array)
    If Abs($x_guesses_array[1] - $x_guesses_array[0]) < 0.001 Then ExitLoop
    EndIf
    If $c >= 10000 Then
    MsgBox (0, "Message", "You have reached a complex root or other weird occurrence. Please try again with a different initial guess. Thank you!")
    Exit ; I have tried to make it user-friendly
    EndIf
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    MsgBox (0, "Solution", Round($x_guess, 4))

    [/autoit] [autoit][/autoit] [autoit]

    Func Terminate ()
    Exit
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _SlopeOfTangent($expr, $at_point, $var = "x", $places=4)
    Local $dx = 0.00000001
    Local $x = $at_point
    Local $y = $x + $dx
    Local $slope = Execute(StringReplace($expr,$var,"$y")) - Execute(StringReplace($expr,$var,"$x"))
    Return Round($slope/$dx,$places)
    EndFunc ;_SlopeOfTangent()==>

    [/autoit] [autoit][/autoit] [autoit]

    Func _Fx ($expr, $at_point)
    Return Execute (StringReplace($expr, "x", "$at_point"))
    EndFunc

    [/autoit]

    gefunden im englischen forum....