Polarkoordinaten

Für komplexe Zahlen gibt es außer der Darstellung als Zahlenpaar aus einem Realteil und einem Imaginärteil noch eine zweite, verbreitete Repräsentationsmöglichkeit.

Wenn man den Realteil und den Imaginärteil einer komplexen Zahl als kartesische Koordinaten eines Punktes in einer Ebene versteht, dann kann man diesen Punkt auch durch seinen Abstand vom Ursprung des Koordinatensystems und den Winkel zwischen dem Ortsvektor des Punktes und der positiven x-Achse festlegen.

Genau diese Darstellung durch Abstand und Winkel nennt man Polarkoordinaten.

Die Methode to-polar rechnet eine komplexe Zahl in Polarkoordinaten um. Dazu bestimmt sie den Abstand zum Ursprung anhand der Quadratwurzel (siehe Quadratwurzeln mit dem Halley-Verfahren berechnen) der Summe der Quadrate der kartesischen Koordinaten. Der Winkel wird anhand des Quotienten und der Vorzeichen der Koordinaten berechnet.

(defmethod to-polar ((this complex) precision)
  t
  (list
    (square-root
      (* this (conjugate this))
      precision)
    (quadrant-arctangent
      (. this real)
      (. this imaginary)
      precision)))

Über die Polarkoordinaten ist der Absolutwert einer komplexen Zahl definiert. Es handelt sich dabei um den Abstand zum Ursprung.

(defmethod abs ((this complex) precision)
  t
  (first (to-polar this precision)))

Für gewöhnliche Zahlen wird auf die Grundfunktion abs zurück gegriffen.

(defmethod abs (r precision)
  (number? r)
  (approximate (abs r) precision))

Die Funktion quadrant-arctangent benutzt den Arkustangens des Quotienten der kartesischen Koordinaten und deren Vorzeichen, um den Winkel zu bestimmen. Ein Sonderfall tritt auf, wenn die x-Koordinate Null ist.

(defproc quadrant-arctangent (x y precision)
  (if
    (= x 0)
    (if
      (< y 0)
      (* 3/2 (pi precision))
      (* 1/2 (pi precision)))
    (let
      ((a (arctangent (/ y x) precision)))
      (if
        (< x 0)
        (+ a (pi precision))
        a))))

Der Arkustangens wird über eine Potenzreihe berechnet. Diese Potenzreihe konvergiert nur, wenn ihr Argument betragsmäßig kleiner als Eins ist. Deswegen wird eine Transformation vorgeschaltet, wenn das Argument zu groß ist.

(defproc arctangent (x precision)
  (cond
    ((< x 0)
      (- (arctangent (- x) precision)))
    ((> x 1)
      (- (* 1/2 (pi precision)) (arctangent (/ x) precision)))
    ((= x 1)
      (* 2 (arctangent (/ x (+ 1 (square-root (+ 1 (* x x)) precision))) precision)))
    (t
      (let
        ((summand nil)
         (sum 0)
         (power x)
         (divisor 1))
        (loop
          (setq summand (/ power divisor))
          (setq sum (+ sum summand))
          (setq power (* power x x))
          (setq divisor (if (greater? divisor 0) (- (+ divisor 2)) (+ (- divisor) 2)))
          (when (< (abs summand) precision)
            (return (approximate sum precision))))))))

Die Hilfsfunktion pi berechnet die Kreiszahl mit der gewünschten Genauigkeit über die Formel von Machin.

(defproc pi (precision)
  (- (* 16 (arctangent 1/5 precision))
     (* 4 (arctangent 1/239 precision))))

Die Methode from-polar macht die zu to-polar gegenteilige Transformation, von Polarkoordinaten zu kartesischen Koordinaten. Dabei ergibt sich die x-Koordinate aus dem Cosinus und die y-Koordinate aus dem Sinus des Winkels, jeweils multipiziert mit dem Radius. Der Radius steht für die Entfernung des Punkts vom Koordinatenursprung.

(defmethod from-polar (pair precision)
  (list? pair)
  (from-polar
    (first pair)
    (second pair)
    precision))

(defmethod from-polar (radius angle precision)
  (and
    (number? radius)
    (>= radius 0)
    (number? angle))
    (* radius (cosine-sine angle precision)))
      
Cosinus uns Sinus werden über die komplexe Exponentialfunktion und deren Potenzreihe berechnet. Die Reihe konvergiert schneller, wenn das Argument kleiner ist. Deswegen wird auch hier eine Transformation (nach dem Satz von de Moivre) vorgeschaltet, wenn das Argument zu groß ist.

(defproc cosine-sine (x precision)
  (if
    (> (abs x) 1/4)
    (let
      ((y (cosine-sine (/ x 2) precision)))
      (* y y))
    (let
      ((summand nil)
       (imaginary-unit (new complex 0 1))
       (sum 0)
       (power 1)
       (divisor 1)
       (index 1))
      (loop
        (setq summand (/ power divisor))
        (setq sum (+ sum summand))
        (setq power (* power x imaginary-unit))
        (setq divisor (* divisor index))
        (setq index (+ 1 index))
        (when (< (* summand (conjugate summand)) (* precision precision))
          (return (approximate sum precision)))))))


polar-coordinates.sheet.xml