(math) Fast Sinus

Hier kannst du häufig gestellte Fragen/Antworten und Tutorials lesen und schreiben.
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

(math) Fast Sinus

Beitrag von Kaeru Gaman »

dieses posting wurde inspiriert von einem detail in diesem thread:
http://forums.purebasic.com/german/viewtopic.php?t=5856


für viele sachen braucht man eine sinus funktion.

dass ein array schneller ist, wurde ja schon oft erwähnt.

dann taucht das problem auf, wie groß das array sein soll.

wenn das 360 felder groß ist, muss ich den winkel begrenzen.
ich müsste also ein Modulo einbauen,
und das eventuelle negative vorzeichen eliminieren...

aber

kann ich das auch anders begrenzen, ohne modulo?
muss ein vollkreis 360° haben?

wenn ich den vollkreis nicht in 360 schritte,
sondern in 1024, 2048 oder 4096 schritte einteile, also eine 2er-potenz,
dann kann ich den modulo mit bitweisem AND bestimmen,
und spare tonnen von rechenzyklen.


also

im folgenden beispiel wird ein sinus-array erstellt, mit 4096 feldern.
damit wird der vollkreis beschrieben 0°->360°

(jeder der bruchrechnung kann, weiß nun, das jeder schritt im array 360 4096stel grad weitergeht)

in Zeile 16 wird der winkel in "arc" umgerechnet, in den index

winkel ist also ein winkel in 0-360 grad, arc ein winkel mit 0-4096 grad.

(für eine anwendung würde ich allerdings empfehlen, gleich mit einem 4096-grad-vollkreis zu arbeiten)

den sinus eines beliebigen winkels hole ich jetzt mit SinA( arc & 4095)

das ist praktisch das modulo...

den cosinus hole ich aus derselben tabelle mit SinA( (arc + 1024) & 4095)
der cosinus ist schließlich ein 90° phasenverschobener sinus.

das plotten im fenster dient nur dem beweis, das die rechnung wirklich "dicht" ist,
also, das mittels des AND kein index außerhalb des arrays angesprochen wird.

das +15 bei der winkelerhöhung dient dazu, zu zeigen, dass es genau ist,
dass also immer auf 24tel kreisen geplottet wird.
bitte beliebig verändern...

hier nun das listing:

Code: Alles auswählen

Dim SinA.f(4095)
#pi = 3.14159265

For n=0 To 4095
    SinA(n) = Sin(#pi / 2048 * n )      ; #pi als erstes, damit ein float übergeben wird
Next

winkel = 0

OpenWindow(0,0,0,800,600, #PB_Window_ScreenCentered | #PB_Window_SystemMenu, "Sinustest")

Repeat
    StartDrawing(WindowOutput())

        arc = winkel * 4096 / 360                       ; umrechnung winkel -> index

        xp = 400 + SinA( arc & 4095 ) * 250             ; sinus aus dem array
        yp = 300 + SinA( (arc+1024) & 4095 ) * 250      ; cosinus ist 90° verschoben

        FrontColor(Random(255),Random(255),Random(255)) ; zufallsfarbe
        Circle(xp,yp,12+Random(12))                     ; kreis plotten, zufallsradius

        winkel +15                                      ; mal hier mit beliebigen werten spielen
        If winkel > 36000 : winkel = -36000 : EndIf    ; hundert vollkreise plus und minus

    StopDrawing()
    
    Delay(0)
    EVT = WindowEvent()

Until EVT = #PB_Event_CloseWindow
ich werde nen thread für rückfragen extra öffnen, ich hätte schon gerne feedback...
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Kekskiller
Beiträge: 752
Registriert: 14.09.2004 21:39
Kontaktdaten:

Beitrag von Kekskiller »

Also wenn man so Speed- und Speicher-bewusst sein will, dann braucht man doch nur 90° vorberechnen. Schlussendlich muss man diese XY-Koordinaten - je nach dem in welchem Quadranten der Winkel liegt - nur noch auf -/+ setzen.

So spart man sich noch mehr Speicher, hat gleiche (oder noch mehr) Genauigkeit. Nur der Speed ist wohl ein bisserl geringer.
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

das ist richtig.

ich habe deshalb ein 360°-Array verwendet, damit man so wenig wie möglich umrechnen muss.

der speicherverbrauch fällt meines erachtens nach nicht schwer ins gewicht.

ein Array mit Double und 131.072 Werten braucht grad mal ein einziges MB.
Peanuts!

(btw: es mag auffallen, das obiger code V3.94 ist)
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
dell_jockey
Beiträge: 2
Registriert: 26.03.2007 15:05
Kontaktdaten:

Beitrag von dell_jockey »

Kaeru,

herzlichen Dank für diese Code! Anbei die 4.0 Anpassungen:

Code: Alles auswählen

; PB 3.x code by Kaeru Gaman - adapted to PB4.x

Dim SinA.f(4095) 

For n=0 To 4095 
    SinA(n) = Sin(#PI / 2048 * n )      ; #pi als erstes, damit ein float übergeben wird 
Next 

winkel = 0 

OpenWindow(0,0,0,800,600,  "Sinustest", #PB_Window_ScreenCentered | #PB_Window_SystemMenu) 

Repeat 
   StartDrawing(WindowOutput(0)) 
      arc = winkel * 4096 / 360                            ; umrechnung winkel -> index 
      xp = 400 + SinA( arc & 4095 ) * 250                  ; sinus aus dem array 
      yp = 300 + SinA( (arc+1024) & 4095 ) * 250           ; cosinus ist 90° verschoben 
      FrontColor(RGB(Random(255),Random(255),Random(255))) ; zufallsfarbe 
      Circle(xp,yp,12+Random(12))                          ; kreis plotten, zufallsradius 
      winkel +15                                           ; mal hier mit beliebigen werten spielen 
      If winkel > 36000 : winkel = -36000 : EndIf          ; hundert vollkreise plus und minus 
   StopDrawing()
   Delay(0) 
   EVT = WindowEvent() 
Until EVT = #PB_Event_CloseWindow
Grüße,
dell_jockey
_______________
Forex Trading Ideas
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

freut mich, dass er dir nützt.
danke für die anpassung. :D
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
dell_jockey
Beiträge: 2
Registriert: 26.03.2007 15:05
Kontaktdaten:

Beitrag von dell_jockey »

um dieses Thema erneut aufzuwärmen: für eine Anwendung habe ich das ganze ein bisl umgeschrieben und möchte das jetzt hier präsentieren:

Code: Alles auswählen

; ---------------------------------------------------
;              SIN-COS-tables.pbi
; ---------------------------------------------------
; precomputed global SIN/COS lookup tables are
; referenced by macros to reduce function call
; overhead and avoid trig conversion calculations
; - fSIN ( degrees )    ; fast sine
; - fCOS ( degrees )    ; fast cosine
; ---------------------------------------------------
; based on Kaeru Gaman's following PureBoard entry:
; http://www.purebasic.fr/german/viewtopic.php?t=5884
; ---------------------------------------------------

Global _Trig_Resolution_.w = 16384   ; steps around unity circle, needs
                                     ; to be a power of 2, i.e.
                                     ; 512, 1024, 2048, 4096, 8192, ...
                                     ; the larger this value, the larger
                                     ; the tables become, but one also
                                     ; gets more precision
                                     
Global _Resolution_minus_One_.w = _Trig_Resolution_.w - 1                                     

Global Dim SinTable.f(_Resolution_minus_One_.w)
Global Dim CosTable.f(_Resolution_minus_One_.w)

Global Angle_to_Index.f = ( _Trig_Resolution_ / 360.0 )

_Trig_Res_halved_.w = _Trig_Resolution_.w >> 1              ; populate tables
For n = 0 To _Resolution_minus_One_.w                        
    SinTable(n) = Sin( (#PI / _Trig_Res_halved_.w) * n )
    CosTable(n) = Cos( (#PI / _Trig_Res_halved_.w) * n )
Next 

Macro fSIN( degrees )
  SinTable( Int((Angle_to_Index.f * degrees) + 0.5 ) & (_Resolution_minus_One_.w) )
EndMacro

Macro fCOS( degrees )
  CosTable( Int((Angle_to_Index.f * degrees) + 0.5 ) & (_Resolution_minus_One_.w) )
EndMacro


; ------------------------------------------
; Usage
; ------------------------------------------
; set __UsageDefined__ to #True to test this
; file standalone
; ------------------------------------------
Define.b __UsageDefined__ = #True  
If __UsageDefined__
  For i = 0 To 359 Step 15
    Debug( "SIN(" + Str(i)+") = " + StrD( fSIN( i ), 6) )
    Debug( "COS(" + Str(i)+") = " + StrD( fCOS( i ), 6) ) 
  Next
EndIf
Grüße,
dell_jockey
_______________
Forex Trading Ideas
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

war im Februar nicht da, sehe deinen Code jetzt erst per Zufall...

danke für deine Arbeit, sieht gut aus. :allright:

auch wenn die meilenlangen Underscores nicht mein Fall sind,
es ist dadurch tatsächlich allcompatibler...
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Beitrag von NicTheQuick »

Ich würde allerdings keine Words, sondern Longs vorziehen, weil die schnell
sind.
Bild
Antworten