Falsche Schnittpunktberechnung

  • Hallo Com,
    ich habe mich heute an einer Schnittpunktberechnung zweier Funktionen versucht.
    Ich hab dafür folgenden Lösungsansatz geschrieben:

    Spoiler anzeigen
    [autoit]


    #include <Array.au3>
    $sF1 = "x^2"
    $sF2 = "3*x"
    $sF1 = StringReplace($sF1, "x", "($x)")
    $sF2 = StringReplace($sF2, "x", "($x)")

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

    $iStart = -1000
    $iEnd = 1000
    Global $aSchnittpunkt[1]
    $aSchnittpunkt[0] = 0

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

    For $x = $iStart To $iEnd
    If Execute($sF1) = Execute($sF2) Then
    ReDim $aSchnittpunkt[Ubound($aSchnittpunkt) + 1]
    $aSchnittpunkt[UBound($aSchnittpunkt) - 1] = Execute($sF1)
    $aSchnittpunkt[0] = UBound($aSchnittpunkt) - 1
    EndIf
    Next

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

    _ArrayDisplay($aSchnittpunkt)

    [/autoit]

    Nur leider funktioniert diese Schnittpuntberechnung nur, wenn der y-Achsenabschnitt null ist;
    also "3*x + 0" als Funktionsgleichung funktioniert, "3*x + 1" jedoch nicht.
    Und ich frage mich jetzt natürlich, warum nicht - ich sehe momentan einfach keinen Fehler, vielleicht sehe ich auch einfach den Wald vor lauter Bäumen nicht.
    Wär nett, wenn ihr mal drüber schauen könntet, vielleicht entdeckt ihr den Fehler, den ich gemacht habe.
    Viele Grüße und Dank im Vorraus,
    stayawayknight

    Einmal editiert, zuletzt von stayawayknight (9. Januar 2011 um 19:19)

  • Hmmm, die allermeisten Schnittpunkte liegen bei solchen Funktionen nicht bei ganzen Zahlen?

    [autoit]


    #include <Array.au3>

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

    $sF1 = "$x ^2"
    $sF2 = "3 * $x + 1"

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

    $iStart = -1000
    $iEnd = 1000
    Global $aSchnittpunkt[1]
    $aSchnittpunkt[0] = 0

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

    For $x = $iStart To $iEnd Step 0.1
    ConsoleWrite($x & @tab & Execute($sF1) & @tab & Execute($sF2) & @crlf)
    If (Execute($sF1) - Execute($sF2)) < 0.1 Then
    ReDim $aSchnittpunkt[UBound($aSchnittpunkt) + 1]
    $aSchnittpunkt[UBound($aSchnittpunkt) - 1] = Execute($sF1)
    $aSchnittpunkt[0] = UBound($aSchnittpunkt) - 1
    EndIf
    Next

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

    _ArrayDisplay($aSchnittpunkt)

    [/autoit]
  • Oh natürlich, hätt ich auch selbst draufkommen können! Da hat mir der Gedanke der For-Schleife wohl einen Streich gespielt, man sagt ja immer "vom Wert x bis zum Wert y", da ist mir wohl entfallen, dass die Kommastellen nicht berücksichtigt werden... Vielen Dank!
    :pinch:

    Edit: Sorry, wollte im 1. Beitrag das Präfix auf "gelöst" setzen, hab den dabei versehentlich gelöscht... (Heute bin ich wohl echt nicht gut drauf)
    Könnte jemand das bitte wieder herstellen? Danke!

    3 Mal editiert, zuletzt von stayawayknight (9. Januar 2011 um 18:57)

  • KaFu
    Ich denke die Zeile sollte eher If Abs((Execute($sF1) - Execute($sF2))) < 0.1 Then heißen - oder?

    Aber auch dann könnten mehrfache Funde auftreten bei flachen Anstiegen oder einer kleineren Schrittweite.
    Besser ist es durchzugehen und zu schauen welcher Funktionswert größer ist und dann mit dem nächsten Schritt zu vergleichen ob da ein Wechsel stattgefunden hat.
    Dann weiß man das dazwischen der Schnittpunkt liegt.
    Diesen kann man dann innerhalb dieser Grenze deutlich feiner mit numerischen Verfahren (z.B. Newton-Verfahren oder Regula Falsi) bestimmen.


  • Ich denke die Zeile sollte eher If Abs((Execute($sF1) - Execute($sF2))) < 0.1 Then heißen - oder?


    Jau :). Ist schon > 10 Jahre her, dass ich mich mit Funktionen beschäftigt habe (bei Vektorfeldern oder Transformationen kann ich mich gerade so noch an die Namen erinnern :cursing: ), spannend sich mal wieder damit zu beschäftigen.

  • Um das mal umzusetzen was ich gerade erzählt habe:

    Spoiler anzeigen
    [autoit]

    Global Const $FLT_EPSILON = _GET_FLT_EPSILON()

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

    ; Definition der Funktionen
    Global $sF1 = "x ^2"
    Global $sF2 = "3 * x + 1"

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

    ; Definition des Suchbereiches
    Global $iStart = -1000
    Global $iEnd = 1000
    Global $iStep = 1.1

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

    ; Größer/Kleiner Vergleichskriterium
    Global $bSw = _Fx($sF1, $iStart) > _Fx($sF2, $iStart)

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

    For $x = $iStart To $iEnd Step $iStep ; Gehe den Bereich grob ab
    If $bSw <> (_Fx($sF1, $x) > _Fx($sF2, $x)) Then ; Wenn Wechsel der Größer/Kleiner Beziehung dann muss Schnittpunkt dazwischen liegen
    $bSw = Not $bSw
    $dX_Schnitt = Schnittsuche($sF1, $sF2, $x - $iStep, $x) ; Starte die feine Schnittpunktsuche im Intervall
    $dY_Schnitt = (_Fx($sF1, $dX_Schnitt) + _Fx($sF2, $dX_Schnitt)) / 2 ;Y-Wert als Mittelwert beider Funktionswerte bestimmen
    MsgBox(0, "Schnittpunkt gefunden", "x = " & $dX_Schnitt & @CRLF & "y = " & $dY_Schnitt)
    EndIf
    Next

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

    Func _Fx(Const $sF, Const $dW)
    Return Number(Execute(StringReplace($sF, "x", "(" & $dW & ")")))
    EndFunc ;==>_Fx

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

    ; Bestimmt die Schnittpunkte 2er Funktionen in einem Intervall iterativ mit dem Regula Falsi Algorithmus
    ; Hinweis: Sollte noch ungültige Ergebnisse liefern wenn einer der Grenzen = dem Schnittpunkt
    Func Schnittsuche(Const $sF1, Const $sF2, $dA, $dB, $iMaxIt = 100)
    ;by AspirinJunkie
    Local $xA = $dA, $xB = $dB
    Local $Ya, $Yb, $x, $Yx
    Local $xOld = $dB

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

    For $n = 1 To $iMaxIt
    $Ya = _Fx($sF1, $xA) - _Fx($sF2, $xA) ;Differenz der Funktionen da Regula Falsi nach Nullstellen sucht
    $Yb = _Fx($sF1, $xB) - _Fx($sF2, $xB)
    $x = $xA - ($Ya * ($xB - $xA)) / ($Yb - $Ya)
    $Yx = _Fx($sF1, $x) - _Fx($sF2, $x)

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

    If Abs($x - $xOld) < (4 * $FLT_EPSILON) Or Abs($Yx) < (5 * $FLT_EPSILON) Then ; Wenn Differenz der Durchgänge unter einer gewissen Genauigkeit liegt abbrechen
    Return $x
    Else ;Festlegung der neuen grenzen für den neuen Durchlauf
    $xOld = $x
    If $Ya > $Yx Then
    $xA = $x
    Else
    $xB = $x
    EndIf
    EndIf

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

    Next
    Return SetError(1, $iMaxIt, $x)
    EndFunc ;==>Schnittsuche

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

    ; #FUNCTION#=================================================================================
    ; Name...........: _GET_FLT_EPSILON
    ; Description ...: Berechnet den binären Rundungsfehler und damit die Rechengenauigkeit
    ; Syntax.........: _GET_FLT_EPSILON
    ; Return values .: Epsilon
    ; Author ........: AspirinJunkie
    ;============================================================================================
    Func _GET_FLT_EPSILON()
    Local $x = 1
    Do
    $x /= 2
    Until 1 + $x <= 1
    Return $x
    EndFunc ;==>_GET_FLT_EPSILON

    [/autoit]

    2 Mal editiert, zuletzt von AspirinJunkie (9. Januar 2011 um 22:19)