Complex Numbers
From Erlang Community
(Difference between revisions)
| Revision as of 22:31, 21 November 2006 (edit) Gordon guthrie (Talk | contribs) ← Previous diff |
Revision as of 18:01, 29 October 2010 (edit) (undo) ChrisW (Talk | contribs) (I needed some more complex number functions, so I took the module that was previously here and enhanced it.) Next diff → |
||
| Line 5: | Line 5: | ||
| == Solution == | == Solution == | ||
| - | Unfortunately, Erlang does not come with built-in support for complex numbers. Luckily, we can create our own module to do so. | + | Unfortunately, Erlang does not come with built-in support for complex numbers. Luckily, we can create our own module to do so. |
| + | |||
| + | This module contains the basic functions to perform the following operations: | ||
| + | 1) Arithmetic | ||
| + | 2) Roots, powers and reciprocal | ||
| + | 3) Rectangular <-> Polar conversion | ||
| + | 4) Trigonometric | ||
| + | 5) Hyperbolic | ||
| + | |||
| <code> | <code> | ||
| - | + | -module(cplx). | |
| - | + | -export([make/1, make/2, is_complex/1, | |
| - | -module( | + | real/1, imag/1, |
| - | -export([make/2, is_complex/1, add/2, | + | add/2, subtract/2, multiply/2, divide/2, |
| - | | + | sqrt/1, root/2, pow/2, reciprocal/1, absolute/1, |
| + | polar/1, rect/1, | ||
| + | cos/1, sin/1, tan/1, cot/1, | ||
| + | cosh/1, sinh/1, tanh/1, coth/1]). | ||
| + | |||
| + | %% What does a complex number look like? | ||
| + | -record(complex, {r=0.0, i=0.0}). | ||
| + | |||
| + | %% Shorten the complex number validity test by wrapping it as a macro | ||
| + | %% This also allows the function to be used a guard. | ||
| + | -define(IsComplex(Z), is_record(Z, complex)). | ||
| + | |||
| + | is_complex(Z) -> ?IsComplex(Z). | ||
| + | |||
| + | %% Go make a complex number | ||
| + | make(Real, Imag) when is_integer(Real); is_float(Real), | ||
| + | is_integer(Imag); is_float(Imag) -> #complex{r=Real, i=Imag}. | ||
| + | make(Real) when is_integer(Real); is_float(Real) -> make(Real, 0); | ||
| + | make(Real) -> Real. | ||
| + | |||
| + | %% Return the real or imaginary parts of a complex number | ||
| + | real(Z) when is_integer(Z); is_float(Z) -> Z; | ||
| + | real(Z) when ?IsComplex(Z) -> Z#complex.r. | ||
| + | |||
| + | imag(Z) when ?IsComplex(Z) -> Z#complex.i. | ||
| + | |||
| + | %% ---------------------------------------------------------------------------- | ||
| + | %% Arithmetic functions | ||
| + | %% | ||
| + | %% Add complex to complex, or complex to real | ||
| + | add(Z1, Z2) when ?IsComplex(Z1), ?IsComplex(Z2) -> #complex{r=Z1#complex.r + Z2#complex.r, i=Z1#complex.i + Z2#complex.i}; | ||
| + | add(Z, R) when ?IsComplex(Z), is_integer(R); is_float(R) -> #complex{r=Z#complex.r + R, i=Z#complex.i}. | ||
| + | |||
| + | %% Subtract complex Z2 from complex Z1, or real from complex | ||
| + | subtract(Z1, Z2) when ?IsComplex(Z1), ?IsComplex(Z2) -> #complex{r=Z1#complex.r - Z2#complex.r, i=Z1#complex.i - Z2#complex.i}; | ||
| + | subtract(Z, R) when ?IsComplex(Z), is_integer(R); is_float(R) -> #complex{r=Z#complex.r - R, i=Z#complex.i}. | ||
| + | |||
| + | %% Multiply complex by complex, or complex by real | ||
| + | multiply(Z1, Z2) when ?IsComplex(Z1), ?IsComplex(Z2) -> | ||
| + | #complex{r=Z1#complex.r * Z2#complex.r - Z1#complex.i * Z2#complex.i, | ||
| + | i=Z1#complex.r * Z2#complex.i + Z1#complex.i * Z2#complex.r}; | ||
| + | multiply(Z1, R) when ?IsComplex(Z1), | ||
| + | is_integer(R); is_float(R) -> #complex{r=Z1#complex.r * R, i=Z1#complex.i * R}. | ||
| + | |||
| + | %% Divide complex Z1 by complex Z2 or complex by real | ||
| + | divide(Z1, Z2) when abs(Z2#complex.r) >= abs(Z2#complex.i) -> | ||
| + | P = Z2#complex.i / Z2#complex.r, | ||
| + | Q = Z2#complex.r + P * Z2#complex.i, | ||
| + | |||
| + | #complex{r=(Z1#complex.r + P * Z1#complex.i) / Q, | ||
| + | i=(Z1#complex.i - P * Z1#complex.r) / Q}; | ||
| + | |||
| + | divide(Z1, Z2) when ?IsComplex(Z1), ?IsComplex(Z2) -> | ||
| + | P = Z2#complex.r / Z2#complex.i, | ||
| + | Q = Z2#complex.i + P * Z2#complex.r, | ||
| + | |||
| + | #complex{r=(Z1#complex.r * P + Z1#complex.i) / Q, | ||
| + | i=(Z1#complex.i * P - Z1#complex.r) / Q}; | ||
| + | |||
| + | divide(Z,R) when ?IsComplex(Z), is_integer(R); is_float(R) -> | ||
| + | #complex{r=Z#complex.r / R, i=Z#complex.i / R}. | ||
| + | |||
| + | %% Absolute value of complex number Z -> |Z| | ||
| + | absolute(Z) when Z#complex.r == 0 -> Z#complex.i; | ||
| + | absolute(Z) when Z#complex.i == 0 -> Z#complex.r; | ||
| + | absolute(Z) when Z#complex.r > Z#complex.i -> abs(Z#complex.r) * math:sqrt(1 + math:pow(abs(Z#complex.i)/abs(Z#complex.r),2)); | ||
| + | absolute(Z) -> abs(Z#complex.i) * math:sqrt(1 + math:pow(abs(Z#complex.r)/abs(Z#complex.i),2)). | ||
| + | |||
| + | |||
| + | %% Square root of complex Z | ||
| + | %% sqrt(Z) when ?IsComplex(Z) -> root(Z,2). | ||
| + | sqrt(Z) when ?IsComplex(Z) -> | ||
| + | Modulus = math:pow((Z#complex.r * Z#complex.r) + (Z#complex.i * Z#complex.i), 0.5), | ||
| + | #complex{r=math:pow((Modulus + Z#complex.r)/2, 0.5), | ||
| + | i=sign(Z#complex.i) * math:pow((Modulus - Z#complex.r)/2, 0.5)}. | ||
| + | |||
| + | |||
| + | %% Nth root of complex Z | ||
| + | root(Z,N) when Z#complex.r >= 0, Z#complex.i == 0 -> #complex{r=math:pow(Z#complex.r, (1/N)), i=0}; | ||
| + | root(Z,N) when Z#complex.r < 0, Z#complex.i == 0 -> #complex{r=0, i=math:pow(abs(Z#complex.r), (1/N))}; | ||
| + | root(Z,N) when Z#complex.i /= 0 -> | ||
| + | Zpolar = polar(Z), | ||
| + | Modulo = math:pow(Zpolar#complex.r, (1/N)), | ||
| + | Alpha = (Zpolar#complex.i + 2 * math:pi()) / N, | ||
| + | |||
| + | #complex{r=Modulo * math:cos(Alpha), i=Modulo * math:sin(Alpha)}. | ||
| - | -record( complex, {real, imaginary}). | ||
| - | + | %% Raise complex Z to the power of N | |
| - | + | pow(Z,N) when Z#complex.i == 0, is_integer(N); is_float(N) -> #complex{r=math:pow(Z#complex.r,N), i=0}; | |
| + | pow(Z,N) when ?IsComplex(Z), is_integer(N); is_float(N) -> | ||
| + | Zpolar = polar(Z), | ||
| + | rect(#complex{r=math:pow(Zpolar#complex.r,N), i=Zpolar#complex.r * N}). | ||
| - | + | %% Reciprocal of complex Z | |
| - | + | reciprocal(Z) when ?IsComplex(Z) -> divide(#complex{r=1,i=0},Z). | |
| - | make(X) when record(X, complex) -> X; | ||
| - | make(Real) -> make(Real, 0)). | ||
| - | + | %% ---------------------------------------------------------------------------- | |
| - | + | %% Coordinate conversion functions | |
| - | + | %% | |
| - | + | %% Rectangular to polar conversion | |
| + | polar(Z) when Z#complex.r == 0 -> #complex{r=absolute(Z), i=(sign(Z#complex.i) * math:pi() / 2)}; | ||
| + | polar(Z) when Z#complex.r > 0 -> #complex{r=absolute(Z), i=math:atan(Z#complex.i / Z#complex.r)}; | ||
| + | polar(Z) when Z#complex.r < 0, Z#complex.i /= 0 -> #complex{r=absolute(Z), i=(sign(Z#complex.i) * math:pi() + math:atan(Z#complex.i / Z#complex.r))}; | ||
| + | polar(Z) when Z#complex.r < 0, Z#complex.i == 0 -> #complex{r=absolute(Z), i=math:pi()}. | ||
| - | + | %% Polar to rectangular conversion | |
| - | + | rect(Z) when Z#complex.r == 0 -> #complex{r=0, i=0}; | |
| - | + | rect(Z) when Z#complex.i == 0 -> #complex{r=Z#complex.r, i=0}; | |
| - | + | rect(Z) when ?IsComplex(Z) -> #complex{r=Z#complex.r * math:cos(Z#complex.i), i=Z#complex.r * math:sin(Z#complex.i)}. | |
| - | mult(X, Y) -> | ||
| - | A = make(X), B = make(Y), | ||
| - | make( (A#complex.real * B#complex.real) | ||
| - | - (A#complex.imaginary * B#complex.imaginary), | ||
| - | (A#complex.real * B#complex.imaginary) | ||
| - | + (B#complex.real * A#complex.imaginary) ). | ||
| - | divide(X,Y) -> | ||
| - | A = make(X), B = make(Y), | ||
| - | Divisor = math:pow(B#complex.real,2) + math:pow(B#complex.imaginary,2), | ||
| - | make( ((A#complex.real * B#complex.real) | ||
| - | + (A#complex.imaginary * B#complex.imaginary)) / Divisor, | ||
| - | ((A#complex.imaginary * B#complex.real) | ||
| - | - (A#complex.real * B#complex.imaginary)) / Divisor). | ||
| - | + | %% ---------------------------------------------------------------------------- | |
| + | %% Trigonometrical functions | ||
| + | %% | ||
| + | cos(Z) when ?IsComplex(Z) -> #complex{r=math:cos(Z#complex.r) * math:cosh(Z#complex.i), i=math:sin(Z#complex.r) * math:sinh(Z#complex.i) * -1}. | ||
| + | sin(Z) when ?IsComplex(Z) -> #complex{r=math:sin(Z#complex.r) * math:cosh(Z#complex.i), i=math:cos(Z#complex.r) * math:sinh(Z#complex.i)}. | ||
| + | tan(Z) when ?IsComplex(Z) -> divide(sin(Z), cos(Z)). | ||
| + | cot(Z) when ?IsComplex(Z) -> divide(cos(Z), sin(Z)). | ||
| - | + | ||
| + | |||
| + | %% ---------------------------------------------------------------------------- | ||
| + | %% Hyperbolic functions | ||
| + | %% | ||
| + | cosh(Z) when ?IsComplex(Z) -> #complex{r=math:cosh(Z#complex.r) * math:cos(Z#complex.i), i=math:sinh(Z#complex.r) * math:sin(Z#complex.i)}. | ||
| + | sinh(Z) when ?IsComplex(Z) -> #complex{r=math:sinh(Z#complex.r) * math:cos(Z#complex.i), i=math:cosh(Z#complex.r) * math:sin(Z#complex.i)}. | ||
| + | tanh(Z) when ?IsComplex(Z) -> divide(sinh(Z), cosh(Z)). | ||
| + | coth(Z) when ?IsComplex(Z) -> divide(cosh(Z), sinh(Z)). | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||
| + | %% ---------------------------------------------------------------------------- | ||
| + | %% Utility functions | ||
| + | %% | ||
| + | %% Return +1, 0 or -1 depending on comparison of N with 0 | ||
| + | sign(N) when N > 0 -> +1; | ||
| + | sign(N) when N < 0 -> -1; | ||
| + | sign(_) -> 0. | ||
| </code> | </code> | ||
| Line 78: | Line 188: | ||
| - | + | Hope this helps... | |
| + | --[[User:ChrisW|ChrisW]] 19:01, 29 October 2010 (BST) | ||
| [[Category:CookBook]][[Category:NumberRecipes]] | [[Category:CookBook]][[Category:NumberRecipes]] | ||
Revision as of 18:01, 29 October 2010
Problem
You wish to manipulate complex numbers (i.e., numbers with both a real and imaginary component.) This is commonly needed in engineering, science, and mathematics.
Solution
Unfortunately, Erlang does not come with built-in support for complex numbers. Luckily, we can create our own module to do so.
This module contains the basic functions to perform the following operations: 1) Arithmetic 2) Roots, powers and reciprocal 3) Rectangular <-> Polar conversion 4) Trigonometric 5) Hyperbolic
-module(cplx).
-export([make/1, make/2, is_complex/1,
real/1, imag/1,
add/2, subtract/2, multiply/2, divide/2,
sqrt/1, root/2, pow/2, reciprocal/1, absolute/1,
polar/1, rect/1,
cos/1, sin/1, tan/1, cot/1,
cosh/1, sinh/1, tanh/1, coth/1]).
%% What does a complex number look like?
-record(complex, {r=0.0, i=0.0}).
%% Shorten the complex number validity test by wrapping it as a macro
%% This also allows the function to be used a guard.
-define(IsComplex(Z), is_record(Z, complex)).
is_complex(Z) -> ?IsComplex(Z).
%% Go make a complex number
make(Real, Imag) when is_integer(Real); is_float(Real),
is_integer(Imag); is_float(Imag) -> #complex{r=Real, i=Imag}.
make(Real) when is_integer(Real); is_float(Real) -> make(Real, 0);
make(Real) -> Real.
%% Return the real or imaginary parts of a complex number
real(Z) when is_integer(Z); is_float(Z) -> Z;
real(Z) when ?IsComplex(Z) -> Z#complex.r.
imag(Z) when ?IsComplex(Z) -> Z#complex.i.
%% ----------------------------------------------------------------------------
%% Arithmetic functions
%%
%% Add complex to complex, or complex to real
add(Z1, Z2) when ?IsComplex(Z1), ?IsComplex(Z2) -> #complex{r=Z1#complex.r + Z2#complex.r, i=Z1#complex.i + Z2#complex.i};
add(Z, R) when ?IsComplex(Z), is_integer(R); is_float(R) -> #complex{r=Z#complex.r + R, i=Z#complex.i}.
%% Subtract complex Z2 from complex Z1, or real from complex
subtract(Z1, Z2) when ?IsComplex(Z1), ?IsComplex(Z2) -> #complex{r=Z1#complex.r - Z2#complex.r, i=Z1#complex.i - Z2#complex.i};
subtract(Z, R) when ?IsComplex(Z), is_integer(R); is_float(R) -> #complex{r=Z#complex.r - R, i=Z#complex.i}.
%% Multiply complex by complex, or complex by real
multiply(Z1, Z2) when ?IsComplex(Z1), ?IsComplex(Z2) ->
#complex{r=Z1#complex.r * Z2#complex.r - Z1#complex.i * Z2#complex.i,
i=Z1#complex.r * Z2#complex.i + Z1#complex.i * Z2#complex.r};
multiply(Z1, R) when ?IsComplex(Z1),
is_integer(R); is_float(R) -> #complex{r=Z1#complex.r * R, i=Z1#complex.i * R}.
%% Divide complex Z1 by complex Z2 or complex by real
divide(Z1, Z2) when abs(Z2#complex.r) >= abs(Z2#complex.i) ->
P = Z2#complex.i / Z2#complex.r,
Q = Z2#complex.r + P * Z2#complex.i,
#complex{r=(Z1#complex.r + P * Z1#complex.i) / Q,
i=(Z1#complex.i - P * Z1#complex.r) / Q};
divide(Z1, Z2) when ?IsComplex(Z1), ?IsComplex(Z2) ->
P = Z2#complex.r / Z2#complex.i,
Q = Z2#complex.i + P * Z2#complex.r,
#complex{r=(Z1#complex.r * P + Z1#complex.i) / Q,
i=(Z1#complex.i * P - Z1#complex.r) / Q};
divide(Z,R) when ?IsComplex(Z), is_integer(R); is_float(R) ->
#complex{r=Z#complex.r / R, i=Z#complex.i / R}.
%% Absolute value of complex number Z -> |Z|
absolute(Z) when Z#complex.r == 0 -> Z#complex.i;
absolute(Z) when Z#complex.i == 0 -> Z#complex.r;
absolute(Z) when Z#complex.r > Z#complex.i -> abs(Z#complex.r) * math:sqrt(1 + math:pow(abs(Z#complex.i)/abs(Z#complex.r),2));
absolute(Z) -> abs(Z#complex.i) * math:sqrt(1 + math:pow(abs(Z#complex.r)/abs(Z#complex.i),2)).
%% Square root of complex Z
%% sqrt(Z) when ?IsComplex(Z) -> root(Z,2).
sqrt(Z) when ?IsComplex(Z) ->
Modulus = math:pow((Z#complex.r * Z#complex.r) + (Z#complex.i * Z#complex.i), 0.5),
#complex{r=math:pow((Modulus + Z#complex.r)/2, 0.5),
i=sign(Z#complex.i) * math:pow((Modulus - Z#complex.r)/2, 0.5)}.
%% Nth root of complex Z
root(Z,N) when Z#complex.r >= 0, Z#complex.i == 0 -> #complex{r=math:pow(Z#complex.r, (1/N)), i=0};
root(Z,N) when Z#complex.r < 0, Z#complex.i == 0 -> #complex{r=0, i=math:pow(abs(Z#complex.r), (1/N))};
root(Z,N) when Z#complex.i /= 0 ->
Zpolar = polar(Z),
Modulo = math:pow(Zpolar#complex.r, (1/N)),
Alpha = (Zpolar#complex.i + 2 * math:pi()) / N,
#complex{r=Modulo * math:cos(Alpha), i=Modulo * math:sin(Alpha)}.
%% Raise complex Z to the power of N
pow(Z,N) when Z#complex.i == 0, is_integer(N); is_float(N) -> #complex{r=math:pow(Z#complex.r,N), i=0};
pow(Z,N) when ?IsComplex(Z), is_integer(N); is_float(N) ->
Zpolar = polar(Z),
rect(#complex{r=math:pow(Zpolar#complex.r,N), i=Zpolar#complex.r * N}).
%% Reciprocal of complex Z
reciprocal(Z) when ?IsComplex(Z) -> divide(#complex{r=1,i=0},Z).
%% ----------------------------------------------------------------------------
%% Coordinate conversion functions
%%
%% Rectangular to polar conversion
polar(Z) when Z#complex.r == 0 -> #complex{r=absolute(Z), i=(sign(Z#complex.i) * math:pi() / 2)};
polar(Z) when Z#complex.r > 0 -> #complex{r=absolute(Z), i=math:atan(Z#complex.i / Z#complex.r)};
polar(Z) when Z#complex.r < 0, Z#complex.i /= 0 -> #complex{r=absolute(Z), i=(sign(Z#complex.i) * math:pi() + math:atan(Z#complex.i / Z#complex.r))};
polar(Z) when Z#complex.r < 0, Z#complex.i == 0 -> #complex{r=absolute(Z), i=math:pi()}.
%% Polar to rectangular conversion
rect(Z) when Z#complex.r == 0 -> #complex{r=0, i=0};
rect(Z) when Z#complex.i == 0 -> #complex{r=Z#complex.r, i=0};
rect(Z) when ?IsComplex(Z) -> #complex{r=Z#complex.r * math:cos(Z#complex.i), i=Z#complex.r * math:sin(Z#complex.i)}.
%% ----------------------------------------------------------------------------
%% Trigonometrical functions
%%
cos(Z) when ?IsComplex(Z) -> #complex{r=math:cos(Z#complex.r) * math:cosh(Z#complex.i), i=math:sin(Z#complex.r) * math:sinh(Z#complex.i) * -1}.
sin(Z) when ?IsComplex(Z) -> #complex{r=math:sin(Z#complex.r) * math:cosh(Z#complex.i), i=math:cos(Z#complex.r) * math:sinh(Z#complex.i)}.
tan(Z) when ?IsComplex(Z) -> divide(sin(Z), cos(Z)).
cot(Z) when ?IsComplex(Z) -> divide(cos(Z), sin(Z)).
%% ----------------------------------------------------------------------------
%% Hyperbolic functions
%%
cosh(Z) when ?IsComplex(Z) -> #complex{r=math:cosh(Z#complex.r) * math:cos(Z#complex.i), i=math:sinh(Z#complex.r) * math:sin(Z#complex.i)}.
sinh(Z) when ?IsComplex(Z) -> #complex{r=math:sinh(Z#complex.r) * math:cos(Z#complex.i), i=math:cosh(Z#complex.r) * math:sin(Z#complex.i)}.
tanh(Z) when ?IsComplex(Z) -> divide(sinh(Z), cosh(Z)).
coth(Z) when ?IsComplex(Z) -> divide(cosh(Z), sinh(Z)).
%% ----------------------------------------------------------------------------
%% Utility functions
%%
%% Return +1, 0 or -1 depending on comparison of N with 0
sign(N) when N > 0 -> +1;
sign(N) when N < 0 -> -1;
sign(_) -> 0.
|
Here are some examples of this module's use:
1> c(complex).
{ok,complex}
2> A = complex:make(15.0e7,3).
{complex,1.50000e+8,3}
3> B = complex:add(1,Aa).
{complex,1.50000e+8,3}
4> io:format("~.16f~n", [complex:get_real(B)])
150000001.0000000000000000
ok
5> complex:is_complex(Aa).
true
6> C = complex:make(14.0e-4,157.2).
{complex,1.40000e-3,157.200}
7> complex:mult(A,C).
{complex,2.09528e+5,2.35800e+10}
8> io:format("~.16f ~.16fi\n",
8> [complex:get_real(D),complex:get_imaginary(D)]).
209528.3999999999900000 23580000000.0042000000000000i
ok
|
Hope this helps...
--ChrisW 19:01, 29 October 2010 (BST)

Digg It
Del.icio.us
Reddit
Facebook
Stumble Upon
Technorati

