Changed return type of nthRoot to List
Renamed getPhi to getArgument Changed and documented behavior of nthRoot wrt NaN, infinite components Improved nth root computation Added some test cases git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@731822 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
34bc1eed72
commit
4564adbf19
|
@ -19,7 +19,7 @@ package org.apache.commons.math.complex;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.math.MathRuntimeException;
|
import org.apache.commons.math.MathRuntimeException;
|
||||||
import org.apache.commons.math.util.MathUtils;
|
import org.apache.commons.math.util.MathUtils;
|
||||||
|
@ -867,50 +867,76 @@ public class Complex implements Serializable {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the angle phi of this complex number.
|
* <p>Compute the argument of this complex number.
|
||||||
* @return the angle phi of this complex number
|
* </p>
|
||||||
|
* <p>The argument is the angle phi between the positive real axis and the point
|
||||||
|
* representing this number in the complex plane. The value returned is between -PI (not inclusive)
|
||||||
|
* and PI (inclusive), with negative values returned for numbers with negative imaginary parts.
|
||||||
|
* </p>
|
||||||
|
* <p>If either real or imaginary part (or both) is NaN, NaN is returned. Infinite parts are handled
|
||||||
|
* as java.Math.atan2 handles them, essentially treating finite parts as zero in the presence of
|
||||||
|
* an infinite coordinate and returning a multiple of pi/4 depending on the signs of the infinite
|
||||||
|
* parts. See the javadoc for java.Math.atan2 for full details.</p>
|
||||||
|
*
|
||||||
|
* @return the argument of this complex number
|
||||||
*/
|
*/
|
||||||
public double getPhi() {
|
public double getArgument() {
|
||||||
return Math.atan2(getImaginary(), getReal());
|
return Math.atan2(getImaginary(), getReal());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the n-th root of this complex number.
|
* <p>Computes the n-th roots of this complex number.
|
||||||
* <p>
|
* </p>
|
||||||
* For a given n it implements the formula: <pre>
|
* <p>The nth roots are defined by the formula: <pre>
|
||||||
* <code> z_k = pow( abs , 1.0/n ) * (cos(phi + k * 2π) + i * (sin(phi + k * 2π)</code></pre></p>
|
* <code> z<sub>k</sub> = abs<sup> 1/n</sup> (cos(phi + 2πk/n) + i (sin(phi + 2πk/n))</code></pre>
|
||||||
* with <i><code>k=0, 1, ..., n-1</code></i> and <i><code>pow(abs, 1.0 / n)</code></i> is the nth root of the absolute-value.
|
* for <i><code>k=0, 1, ..., n-1</code></i>, where <code>abs</code> and <code>phi</code> are
|
||||||
* <p>
|
* respectively the {@link #abs() modulus} and {@link #getArgument() argument} of this complex number.
|
||||||
|
* </p>
|
||||||
|
* <p>If one or both parts of this complex number is NaN, a list with just one element,
|
||||||
|
* {@link #NaN} is returned.</p>
|
||||||
|
* <p>if neither part is NaN, but at least one part is infinite, the result is a one-element
|
||||||
|
* list containing {@link #INF}.</p>
|
||||||
*
|
*
|
||||||
* @param n degree of root
|
* @param n degree of root
|
||||||
* @return Collection<Complex> all nth roots of this complex number as a Collection
|
* @return List<Complex> all nth roots of this complex number
|
||||||
* @throws IllegalArgumentException if parameter n is negative
|
* @throws IllegalArgumentException if parameter n is less than or equal to 0
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public Collection<Complex> nthRoot(int n) throws IllegalArgumentException {
|
public List<Complex> nthRoot(int n) throws IllegalArgumentException {
|
||||||
|
|
||||||
if (n <= 0) {
|
if (n <= 0) {
|
||||||
throw MathRuntimeException.createIllegalArgumentException("cannot compute nth root for null or negative n: {0}",
|
throw MathRuntimeException.createIllegalArgumentException("cannot compute nth root for null or negative n: {0}",
|
||||||
new Object[] { n });
|
new Object[] { n });
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<Complex> result = new ArrayList<Complex>();
|
List<Complex> result = new ArrayList<Complex>();
|
||||||
|
|
||||||
// nth root of abs
|
if (isNaN()) {
|
||||||
|
result.add(Complex.NaN);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInfinite()) {
|
||||||
|
result.add(Complex.INF);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nth root of abs -- faster / more accurate to use a solver here?
|
||||||
final double nthRootOfAbs = Math.pow(abs(), 1.0 / n);
|
final double nthRootOfAbs = Math.pow(abs(), 1.0 / n);
|
||||||
|
|
||||||
// Compute nth roots of complex number with k = 0, 1, ... n-1
|
// Compute nth roots of complex number with k = 0, 1, ... n-1
|
||||||
final double phi = getPhi();
|
final double nthPhi = getArgument()/n;
|
||||||
|
final double slice = 2 * Math.PI / n;
|
||||||
|
double innerPart = nthPhi;
|
||||||
for (int k = 0; k < n ; k++) {
|
for (int k = 0; k < n ; k++) {
|
||||||
// inner part
|
// inner part
|
||||||
final double innerPart = (phi + k * 2 * Math.PI) / n;
|
|
||||||
final double realPart = nthRootOfAbs * Math.cos(innerPart);
|
final double realPart = nthRootOfAbs * Math.cos(innerPart);
|
||||||
final double imaginaryPart = nthRootOfAbs * Math.sin(innerPart);
|
final double imaginaryPart = nthRootOfAbs * Math.sin(innerPart);
|
||||||
result.add(createComplex(realPart, imaginaryPart));
|
result.add(createComplex(realPart, imaginaryPart));
|
||||||
|
innerPart += slice;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,6 +19,8 @@ package org.apache.commons.math.complex;
|
||||||
|
|
||||||
import org.apache.commons.math.TestUtils;
|
import org.apache.commons.math.TestUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -801,7 +803,7 @@ public class ComplexTest extends TestCase {
|
||||||
* </code>
|
* </code>
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public void testNthRoot_cornercase_thirdRoot_realPartEmpty() {
|
public void testNthRoot_cornercase_thirdRoot_realPartZero() {
|
||||||
// complex number with only imaginary part
|
// complex number with only imaginary part
|
||||||
Complex z = new Complex(0,2);
|
Complex z = new Complex(0,2);
|
||||||
// The List holding all third roots
|
// The List holding all third roots
|
||||||
|
@ -823,26 +825,82 @@ public class ComplexTest extends TestCase {
|
||||||
* Test cornercases with NaN and Infinity.
|
* Test cornercases with NaN and Infinity.
|
||||||
*/
|
*/
|
||||||
public void testNthRoot_cornercase_NAN_Inf() {
|
public void testNthRoot_cornercase_NAN_Inf() {
|
||||||
// third root of z = 1 + NaN * i
|
// NaN + finite -> NaN
|
||||||
for (Complex c : oneNaN.nthRoot(3)) {
|
List<Complex> roots = oneNaN.nthRoot(3);
|
||||||
// both parts should be nan
|
assertEquals(1,roots.size());
|
||||||
assertEquals(nan, c.getReal());
|
assertEquals(Complex.NaN, roots.get(0));
|
||||||
assertEquals(nan, c.getImaginary());
|
|
||||||
}
|
roots = nanZero.nthRoot(3);
|
||||||
// third root of z = inf + NaN * i
|
assertEquals(1,roots.size());
|
||||||
for (Complex c : infNaN.nthRoot(3)) {
|
assertEquals(Complex.NaN, roots.get(0));
|
||||||
// both parts should be nan
|
|
||||||
assertEquals(nan, c.getReal());
|
// NaN + infinite -> NaN
|
||||||
assertEquals(nan, c.getImaginary());
|
roots = nanInf.nthRoot(3);
|
||||||
}
|
assertEquals(1,roots.size());
|
||||||
// third root of z = neginf + 1 * i
|
assertEquals(Complex.NaN, roots.get(0));
|
||||||
Complex[] zInfOne = negInfOne.nthRoot(2).toArray(new Complex[0]);
|
|
||||||
// first root
|
// finite + infinite -> Inf
|
||||||
assertEquals(inf, zInfOne[0].getReal());
|
roots = oneInf.nthRoot(3);
|
||||||
assertEquals(inf, zInfOne[0].getImaginary());
|
assertEquals(1,roots.size());
|
||||||
// second root
|
assertEquals(Complex.INF, roots.get(0));
|
||||||
assertEquals(neginf, zInfOne[1].getReal());
|
|
||||||
assertEquals(neginf, zInfOne[1].getImaginary());
|
// infinite + infinite -> Inf
|
||||||
|
roots = negInfInf.nthRoot(3);
|
||||||
|
assertEquals(1,roots.size());
|
||||||
|
assertEquals(Complex.INF, roots.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test standard values
|
||||||
|
*/
|
||||||
|
public void testGetArgument() {
|
||||||
|
Complex z = new Complex(1, 0);
|
||||||
|
assertEquals(0.0, z.getArgument(), 1.0e-12);
|
||||||
|
|
||||||
|
z = new Complex(1, 1);
|
||||||
|
assertEquals(Math.PI/4, z.getArgument(), 1.0e-12);
|
||||||
|
|
||||||
|
z = new Complex(0, 1);
|
||||||
|
assertEquals(Math.PI/2, z.getArgument(), 1.0e-12);
|
||||||
|
|
||||||
|
z = new Complex(-1, 1);
|
||||||
|
assertEquals(3 * Math.PI/4, z.getArgument(), 1.0e-12);
|
||||||
|
|
||||||
|
z = new Complex(-1, 0);
|
||||||
|
assertEquals(Math.PI, z.getArgument(), 1.0e-12);
|
||||||
|
|
||||||
|
z = new Complex(-1, -1);
|
||||||
|
assertEquals(-3 * Math.PI/4, z.getArgument(), 1.0e-12);
|
||||||
|
|
||||||
|
z = new Complex(0, -1);
|
||||||
|
assertEquals(-Math.PI/2, z.getArgument(), 1.0e-12);
|
||||||
|
|
||||||
|
z = new Complex(1, -1);
|
||||||
|
assertEquals(-Math.PI/4, z.getArgument(), 1.0e-12);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify atan2-style handling of infinite parts
|
||||||
|
*/
|
||||||
|
public void testGetArgumentInf() {
|
||||||
|
assertEquals(Math.PI/4, infInf.getArgument(), 1.0e-12);
|
||||||
|
assertEquals(Math.PI/2, oneInf.getArgument(), 1.0e-12);
|
||||||
|
assertEquals(0.0, infOne.getArgument(), 1.0e-12);
|
||||||
|
assertEquals(Math.PI/2, zeroInf.getArgument(), 1.0e-12);
|
||||||
|
assertEquals(0.0, infZero.getArgument(), 1.0e-12);
|
||||||
|
assertEquals(Math.PI, negInfOne.getArgument(), 1.0e-12);
|
||||||
|
assertEquals(-3.0*Math.PI/4, negInfNegInf.getArgument(), 1.0e-12);
|
||||||
|
assertEquals(-Math.PI/2, oneNegInf.getArgument(), 1.0e-12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that either part NaN results in NaN
|
||||||
|
*/
|
||||||
|
public void testGetArgumentNaN() {
|
||||||
|
assertEquals(nan, nanZero.getArgument());
|
||||||
|
assertEquals(nan, zeroNaN.getArgument());
|
||||||
|
assertEquals(nan, Complex.NaN.getArgument());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue