Extrapolation in java - java

I've been able to use Apache Math's interpolation using the LinearInterpolator().interpolate(x1, y1). Unfortunately, I could not find a way to extrapolate.
How can I do linear extrapolation in java?
x1 = [1, 2, 3, 4, 5];
y1 = [2, 4, 8, 16, 32];
I would like to know the values of any x2 not just the one in the range of the x1.
If I try to extract the value of 6 I get an: OutOfRangeException if {#code v} is outside of the domain of the
* spline function (smaller than the smallest knot point or larger than the
largest knot point).
Edit: Here is my simple interpolate function. I would like an option to enable the extrapolation just like in MathLab(interp2). Using x1 and y1 arrays an input for that function I get the Apache's OutOfRangeException because the value 6 is not contained in the x1 array.
public static List<Double> interpolateLinear(double[] x1, double[] y1, Double[] x2) {
List<Double> resultList;
final PolynomialSplineFunction function = new LinearInterpolator().interpolate(x1, y1);
resultList = Arrays.stream(x2).map(aDouble -> function.value(aDouble)).collect(Collectors.toList());
return resultList;
}
Edit2: Had to read a little bit on the .value method of the PolynomialSplineFunction object to get it right but there it goes (all the credit goes to user Joni) Thanks man:
public static double[] interpolateLinear(double[] x1, double[] y1, double[] x2) {
final PolynomialSplineFunction function = new LinearInterpolator().interpolate(x1, y1);
final PolynomialFunction[] splines = function.getPolynomials();
final PolynomialFunction firstFunction = splines[0];
final PolynomialFunction lastFunction = splines[splines.length - 1];
final double[] knots = function.getKnots();
final double firstKnot = knots[0];
final double lastKnot = knots[knots.length - 1];
double[] resultList = Arrays.stream(x2).map(aDouble -> {
if (aDouble > lastKnot) {
return lastFunction.value(aDouble - knots[knots.length - 2]);
} else if (aDouble < firstKnot)
return firstFunction.value(aDouble - knots[0]);
return function.value(aDouble);
}).toArray();
return resultList;
}

You can get the first and last polynomial splines from the interpolator, and use those to extrapolate.
PolynomialSplineFunction function = new LinearInterpolator().interpolate(x1, y1);
PolynomialFunction[] splines = function.getPolynomials();
PolynomialFunction first = splines[0];
PolynomialFunction last = splines[splines.length-1];
// use first and last to extrapolate
You won't get 64 from 6 though. You should expect 48 from a linear extrapolation. Which goes to show that extrapolation is bound to give you wrong answers.

I have similar problem, the interpolation part is a cubic spline function, and math3.analysis.polynomials.PolynomialSplineFunction does not support the extrapolation.
In the end, I decide to write the linear extrapolation based on the leftest(/rightest) two points (i.e. x1,x2 and y1, y2). I need the extrapolation part to avoid that the function fails or get any very irregular values in the extrapolation region. In my example, I hard coded such that the extrapolated value should stay in [0.5* y1, 2 * y1] (left side) or [0.5 * yn, 2 *yn] (right side).
As mentioned by Joni, the extrapolation is dangerous, and it could leads to unexpected results. Be careful. The linear extrapolation can be replaced by any other kind extrapolation, depending on how you write the code (e.g. using the derivative at the right/left point and inferring a quadratic function for extrapolation.)
public static double getValue(PolynomialSplineFunction InterpolationFunction, double v) {
try {
return InterpolationFunction.value(v);
} catch (OutOfRangeException e) {
// add the extrapolation function: we use linear extrapolation based on the slope of the two points on the left or right
double[] InterpolationKnots = InterpolationFunction.getKnots();
int n = InterpolationKnots.length;
double first, second, firstValue, secondValue;
if (v < InterpolationKnots[0])
{ // extrapolation from the left side, linear extrapolation based on the first two points on the left
first = InterpolationKnots[0]; // the leftest point
second = InterpolationKnots[1]; // the second leftest point
}
else { // extrapolation on the right side, linear extrapolation based on the first two points on the right
first = InterpolationKnots[n - 1]; // the rightest point
second = InterpolationKnots[n - 2]; // the second rightest point
}
firstValue = InterpolationFunction.value(first);
secondValue = InterpolationFunction.value(second);
double extrapolatedValue = (firstValue - secondValue) / (first - second) * (v - first) + firstValue;
// add a boundary to the extrapolated value so that it is within [0.5, 2] * firstValue
if (extrapolatedValue > 2 * firstValue){ extrapolatedValue = 2 * firstValue;}
if (extrapolatedValue < 0.5 * firstValue) {extrapolatedValue = 0.5* firstValue;}
return extrapolatedValue;
}
}

Just sharing a complete example based on the answer provided by Joni:
import java.util.Arrays;
import org.apache.commons.math3.analysis.interpolation.LinearInterpolator;
import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
public class App {
public static void main(String[] args) {
double[] x1 = { 1, 2, 3, 4, 5 };
double[] y1 = { 2, 4, 8, 16, 32 };
double[] x2 = { 6, 7 };
double[] res = interpolateLinear(x1, y1, x2);
for (int i = 0; i < res.length; i++) {
System.out.println("Value: " + x2[i] + " => extrapolation: " + res[i]);
}
}
public static double[] interpolateLinear(double[] x1, double[] y1, double[] x2) {
final PolynomialSplineFunction function = new LinearInterpolator().interpolate(x1, y1);
final PolynomialFunction[] splines = function.getPolynomials();
final PolynomialFunction firstFunction = splines[0];
final PolynomialFunction lastFunction = splines[splines.length - 1];
final double[] knots = function.getKnots();
final double firstKnot = knots[0];
final double lastKnot = knots[knots.length - 1];
double[] resultList = Arrays.stream(x2).map(aDouble -> {
if (aDouble > lastKnot) {
return lastFunction.value(aDouble - knots[knots.length - 2]);
} else if (aDouble < firstKnot)
return firstFunction.value(aDouble - knots[0]);
return function.value(aDouble);
}).toArray();
return resultList;
}
}

Related

Implementing Adaptive function plotting

I'm attempting to implement the Adaptive function plotting algorithm using the psuedocode from these two examples (both examples the same really)
https://www.andr.mu/logs/acquiring-samples-to-plot-a-math-function-adaptive/
http://yacas.readthedocs.io/en/latest/book_of_algorithms/basic.html
The problems I've encountered (from my probably incorrect implementation) are:
(1) Duplicated coordinates are being created. I know this is because of the splitting, where both ends are kept for each spilt, so the end of one interval is the start of the other interval - same x value evaluated twice. But is there a way to setup the algorithm to avoid deplicated coordinates. I could avoid adding the start or end coordinate as a dirty fix (see comment in code below) but then I'd be missing one them for the whole interval.
(2) Some parts of the plot are missing coordinates for what is essentially a symmetrical function, which is strange. The algorithm should work the same way for both sides, yet it doesn't. This starts to happen when depth >= 6, it does an extra split to the right side of the function and nothing for the left side around the origin. The rest of the coordinates away from the origin seem to match up. The right side seems to get more splits than the left side overall.
Problem (2)
My Implementation of algorithm
static List<Double[]> linePoints;
public static void main(String[] args) {
linePoints = new ArrayList<>();
double startX = -50;
double endX = 50;
sampling(startX, endX, depth, tolerance);
/* Print all points to be plotted - x,y coordinates */
for (Double[] point : linePoints) {
System.out.println(point[0]+","+point[1]);
}
}
/* math function */
public static double f(double x){
return x*Math.sin(x);
}
static int depth = 6; /* 8 */
static double tolerance = 0.005; /* just a guess */
/* Adaptive sampling algorithm */
/* mostly followed along 2st website and used 1st website variable names */
public static void sampling(double xa, double xc, int depth, double tolerance){
/* step (1) of 2nd website - determine mid-intervals */
double xb = (xa+xc)/2; /* (xc-xa)/2; tried these from 1st website - didn't work out */
double xab = (xa+xb)/2; /* (xb-xa)/2; */
double xbc = (xb+xc)/2; /* (xc-xb)/2; */
/* evaluate the above points using math function - store in array */
double[] points = new double[5];
points[0] = f(xa); points[1] = f(xab); points[2] = f(xb); points[3] = f(xbc); points[4] = f(xc);
/* step (2) of 2nd website */
if (depth <= 0){
linePoints.add(new Double[]{xa, points[0]}); /* either I comment out this line for dirty fix */
linePoints.add(new Double[]{xab, points[1]});
linePoints.add(new Double[]{xb, points[2]});
linePoints.add(new Double[]{xbc, points[3]});
linePoints.add(new Double[]{xc, points[4]}); /* or comment out this line */
} else {
/* step (3) of 2nd website */
int counter = 0;
for (int i = 1; i < points.length-1; i++){
/* Check if prev, current, next values are infinite or NaN */
if ( (Double.isInfinite(points[i-1]) || Double.isNaN(points[i-1])) ||
(Double.isInfinite(points[i]) || Double.isNaN(points[i])) ||
(Double.isInfinite(points[i+1]) || Double.isNaN(points[i+1]))){
counter++;
continue;
}
/* Determine the fluctuations - if current is < or > both it's left/right neighbours */
boolean middleLarger = (points[i] > points[i-1]) && (points[i] > points[i+1]);
boolean middleSmaller = (points[i] < points[i-1]) && (points[i] < points[i+1]);
if (middleLarger || middleSmaller){
counter++;
}
}
if (counter <= 2){ /* at most 2 */
/* Newton-Cotes quadratures - check if smooth enough */
double f1 = (3d/8d)*points[0]+(19d/24d)*points[1]-(5d/24d)*points[2]+(1d/24d)*points[3]; /* add 'd' to end of number, otherwise get 0 always */
double f2 = (5d/12d)*points[2]+(2d/3d)*points[3]-(1d/12d)*points[4];
if (Math.abs(f1-f2) < tolerance * f2){
linePoints.add(new Double[]{xa, points[0]});
linePoints.add(new Double[]{xab, points[1]});
linePoints.add(new Double[]{xb, points[2]});
linePoints.add(new Double[]{xbc, points[3]});
linePoints.add(new Double[]{xc, points[4]});
} else {
/* not smooth enough - needs more refinement */
depth--;
tolerance *= 2;
sampling(xa, xb, depth, tolerance);
sampling(xb, xc, depth, tolerance);
}
} else {
/* else (count > 2), that means further splittings are needed to produce more accurate samples */
depth--;
tolerance *= 2;
sampling(xa, xb, depth, tolerance);
sampling(xb, xc, depth, tolerance);
}
}
}
FIX - Modifications to my code
Looking at Gene's example and multiplying the tolerance by 0.5 instead of 2 seemed to fix problem (2)
Genes example is a better and cleaner implementation of this algorithm and handles the duplicated coordinates
I think you've implemented faithfully, but the algorithm is broken. Asymmetric quadrature can easily give asymmetric results. I get the same kinds of weirdness.
But you can eliminate duplicate points by maintaining them in a sorted set and using the invariant that the endpoints have already been inserted when the recursive analyzer runs.
Here's a simplification using modern Java features more fully:
import static java.lang.Double.compare;
import static java.lang.Double.isFinite;
import static java.lang.Math.PI;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.DoubleUnaryOperator;
import static java.util.stream.Collectors.toList;
import java.util.stream.DoubleStream;
public class AdaptivePlot {
private final DoubleUnaryOperator f;
private final double a;
private final double c;
private final SortedSet<Point> plot = new TreeSet<>((s, t) -> compare(s.x, t.x));
public AdaptivePlot(DoubleUnaryOperator f, double a, double c) {
this.f = f;
this.a = a;
this.c = c;
}
public static class Point {
final double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
public AdaptivePlot computePlot(int depth, double eps) {
plot.clear();
Point pa = pointAt(a);
Point pc = pointAt(c);
plot.add(pa);
plot.add(pc);
computePlot(pa, pc, depth, eps);
return this;
}
public List<Point> getPlot() {
return plot.stream().collect(toList());
}
private Point pointAt(double x) {
return new Point(x, f.applyAsDouble(x));
}
private void computePlot(Point pa, Point pc, int depth, double eps) {
Point pb = pointAt(0.5 * (pa.x + pc.x));
Point pa1 = pointAt(0.5 * (pa.x + pb.x));
Point pb1 = pointAt(0.5 * (pb.x + pc.x));
plot.add(pb);
if (depth > 0 &&
(oscillates(pa.y, pa1.y, pb.y, pb1.y, pc.y)
|| unsmooth(pa.y, pa1.y, pb.y, pb1.y, pc.y, eps))) {
computePlot(pa, pb, depth - 1, 2 * eps);
computePlot(pb, pc, depth - 1, 2 * eps);
}
plot.add(pa1);
plot.add(pb1);
}
private static boolean oscillates(
double ya, double ya1, double yb, double yb1, double yc) {
return isOscillation(ya, ya1, yb)
&& isOscillation(ya1, yb, yb1)
&& isOscillation(yb, yb1, yc);
}
private static boolean isOscillation(double ya, double yb, double yc) {
return !isFinite(ya) || !isFinite(yb) || !isFinite(yc)
|| (yb > ya && yb > yc) || (yb < ya && yb < yc);
}
private static boolean unsmooth(
double ya, double ya1, double yb, double yb1,double yc, double eps) {
double y0 = DoubleStream.of(ya, ya1, yb, yb1, yc).min().getAsDouble();
double [] yg = DoubleStream.of(ya, ya1, yb, yb1, yc).map(y -> y - y0).toArray();
double q4 = quadrature(yg[0], yg[1], yg[2], yg[3]);
double q3 = quadrature(yg[2], yg[3], yg[4]);
return Math.abs(q4 - q3) > eps * q3;
}
private static double quadrature(double y0, double y1, double y2, double y3) {
return 3d/8d * y0 + 19d/24d * y1 - 5d/24d * y2 + 1d/24d * y3;
}
private static double quadrature(double y0, double y1, double y2) {
return 5d/12d * y0 + 2d/3d * y1 - 1d/12d * y2;
}
public static void main(String [] args) {
List<Point> plot = new AdaptivePlot(x -> x * Math.sin(x), -2d * PI, 2d * PI)
.computePlot(6, 0.005).getPlot();
for (Point p : plot) {
System.out.println(p.x + "\t" + p.y);
}
}
}
I personally find this method horrible.
You can get nice results with a simple rule: subdivide the interval as long as the distance of the middle point to the chord exceeds a small threshold.
Caution: to avoid scaling effects, you should express all geometric quantities in mm (with appropriate scale factors on both axes).
Subdivide left, middle, right:
if DistancePointLine (middle, f(middle)), (left, f(left)), (right, f(right)) < Tolerance:
DrawLine (left, f(left), (right, f(right))
else
Subdivide left, (left + middle) / 2, middle
Subdivide middle, (middle + right) / 2, right
The method can fail in symmetric situations, around inflection points. To cope, you can force one extra subdivision even if the tolerance has been met.

How to get the nearest Vector to a given target from a list

So imagine I've created a Vector class with two variables x and y in Java:
public class Vector {
private int x;
private int y;
public Vector(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return this.x;
}
public int getY(){
return this.y;
}
}
Then I've craeted an ArrayList of vectors:
private List<Vector> vecs = new ArrayList<Vector>();
I've created in that list:
8,9
10,5
83473834,938849584985
etc ...
Now I want to get the closest vector to another vector.
Example:
private List<Vector> vecs = new ArrayList<Vector>();
private Vector vec = new Vector(1,1);
for(Vector vector:vecs) {
//What do i put here??
}
So what do i put in the for loop to make it select the nearest vector from the vector list?
I would start by adding a method to the Vector class, distanceTo, that calculates the distance from this vector to another one:
public double distanceTo(Vector vec) {
double dx = x - vec.x; //calculate the diffrence in x-coordinate
double dy = y - vec.y; //calculate the diffrence in y-coordinate
return Math.sqrt(dx*dx + dy*dy); //use the distance formula to find the difference
}
And then you can write the following method that returns the closest vector in a list to a given vector:
public static Vector closest(Vector target, List<Vector> list) {
Vector closest = list.get(0); //this variable will kep track of the closest vector we have found yet. We simply start with the first one
for(int i = 1; i < list.size(); i++) { //loop over the list, skipping the first entry
Vector curr = list.get(i); //get the current vector from the list
if (target.distanceTo(curr) < target.distanceTo(closest)) //if the current vector is closer to target than the closest one yet
closest = curr; //keep the current vector as the new closest one
}
return closest; //return the resulting vector
}
This method can then be used like this:
Vector target = new Vector(1, 2);
List<Vector> vecs = new ArrayList<Vector>();
vecs.add(new Vector(-2, 6));
vecs.add(new Vector(1, 3));
vecs.add(new Vector(4, 0));
vecs.add(new Vector(8, -1));
Vector closest = findClosest(target, vecs);
As you can see I tried to explain the code as best as I could, but feel free to ask any further questions!
EDIT another method is:
public double distanceTo(Vector vec1,Vector vec2) {
double dx = vec2.x - vec1.x; //calculate the diffrence in x-coordinate
double dy = vec.y - vec1.y; //calculate the diffrence in y-coordinate
return Math.sqrt(dx*dx + dy*dy); //use the distance formula to find the difference
}
This is if you can't put it into the vector class
This is a basic programming question. It is not related to OpenGL. A simple linear search could look as follows:
private List<Vector> vecs = new ArrayList<Vector>();
private Vector vec = new Vector(1,1);
Vector minDistanceVector = null;
int minDistanceSquared = Integer.MAX_VALUE;
for(Vector vector : vecs) {
//Calculate the distance
//This could be a member function of Vector
int dx = vector.getX() - vec.getX();
int dy = vector.getY() - vec.getY();
int squaredDistance = dx * dx + dy * dy;
if(squaredDistance < minDistanceSquared) {
minDistanceSquared = squaredDistance;
minDistanceVector = vector;
}
}
After that, you will have the closest vector in minDistanceVector. I chose Euclidean distance because this is probably what you want. But you could use any other distance, of course.
If you want something more efficient, you may want to build some acceleration data structure over the points and query that one (e.g. grid, kd-tree, quadtree...).

Iteratation through variables of an objects (java)

I have a class Room with the following constructor:
public Room (int x, int y, int z, int Stockwerk) {
this.x = x;
this.y = y;
this.z = z;
this.Stockwerk = Stockwerk;
}
In another class I want to calculate the similarity of two rooms by using the Euclidean distance. Therefore I want to iterate trough the object room to compare the first value(x) of room1 with the first value(x) of room2, the second(y) with the second(y) and so on. (The code below describes how I tried to get a result). Is there any option to iterate through the object room?
Iterator itrRoom1 = room1.iterator();
Iterator itrRoom2 = room2.iterator();
while (itrRoom1.hasNext()) {
while (itrRoom2.hasNext()) {
int value1 = itrRoom1.next();
int value2 = itrRoom2.next();
}
//this is the function how I get the similarity of two rooms (euclidean distance)
squareSubtraction = squareSubtraction + Math.pow(Math.abs(value1 - value2)), 2);
}
distance = Math.sqrt(squareSubtraction);
You do it with a very complicated way and Iterator is not needed.
If you want to get the euclidean distance, you only have to parse these two instances of Room to be compared. I assume your Room class has the getters.
You can use the following method with the arbitrary number of arguments based on looping through of all instances of Room given.
public static void main(String[] args) {
Room room1 = new Room(10,12,2,0);
Room room2 = new Room(4,8,2,0);
Room room3 = new Room(7,5,2,0);
Room room4 = new Room(10,2,2,0);
double result = similarity(room1, room2, room3, room4);
}
public static double similarity(Room ... r) {
double sum = 0;
for (int i=0; i<r.length-1; i++) {
double a = Math.pow(r[i].getX() - r[i+1].getX(), 2);
double b = Math.pow(r[i].getY() - r[i+1].getY(), 2);
sum += a + b;
}
return Math.sqrt(sum);
}
This is giving you the result:
9.38083151964686
You can't iterate fields like that. You could iterate fields using reflection, but don't.
You only have 3 values. Just use them.
long dx = room1.getX() - room2.getX();
long dy = room1.getY() - room2.getY();
long dz = room1.getZ() - room2.getZ();
double distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
Note that dx and the others are declared long, so dx * dx won't overflow, and dx * dx is much faster than Math.pow(dx, 2).
It's actually even less code than what you were trying to do.

Change iteration method to recursion to calculate Polygon area

I did some coding in Java to calculate the area of Polygon. In my code, i'm using iteration "for" to get the input of coordinate value of X and Y in ArrayList. My code calculation run successfully and I did get the correct output.
My problem is that I want to change my code to use recursion so that computeArea(ArrayList c, int size) can be call recursively and I don't want to use an iteration "for" in this method.
I would be really appreciate to those who can show me how to change this method from iteration to recursive.
Thanks.
Below is my code for Polygon.java
import java.awt.geom.Point2D;
import java.util.ArrayList;
public class Polygon
{
private ArrayList<Point2D.Double> corners;
private double area;
private double adding;
private double minus;
private double exAdding;
private double exMinus;
public Polygon()
{
corners = new ArrayList<Point2D.Double>();
area = 0;
adding = 0;
minus = 0;
exAdding = 0;
exMinus = 0;
}
//add point to the array
public void add (Point2D.Double p)
{
corners.add(p);
}
//computes area of polygon
public double getArea()
{
return computeArea(corners, corners.size());
}
public double computeArea(ArrayList<Point2D.Double> c, int size)
{
if (size < 0)
{
return 0;
}
else
{
for (int i = 0; i < size-1; i++)
{
double xA = c.get(i).getX();
double yA = c.get(i).getY();
double xB = c.get(i+1).getX();
double yB = c.get(i+1).getY();
exAdding = c.get(size-1).getX()*c.get(size-size).getY();
adding = adding + xA*yB;
exMinus = c.get(size-1).getY()*c.get(size-size).getX();
minus = minus + yA*xB;
//System.out.println("test adding : " + adding);
}
//System.out.println("extra adding : " + exAdding);
area = Math.abs((adding+exAdding) - (minus+exMinus));
return area/2;
}
}
}
This is my code for PolygonTester.java
import java.awt.geom.Point2D;
public class PolygonTester
{
public static void main (String[] args)
{
//create square
Polygon p = new Polygon();
p.add(new Point2D.Double(10, 20));
p.add(new Point2D.Double(20, 20));
p.add(new Point2D.Double(20, 10));
p.add(new Point2D.Double(10, 10));
System.out.println("Area : " + p.getArea());
System.out.println("Expected : 100");
//create square
Polygon p1 = new Polygon();
p1.add(new Point2D.Double(3, 4));
p1.add(new Point2D.Double(5, 11));
p1.add(new Point2D.Double(12, 8));
p1.add(new Point2D.Double(9, 5));
p1.add(new Point2D.Double(5, 6));
System.out.println("Area : " + p1.getArea());
System.out.println("Expected : 30");
//regular hexagon with radius 1
p = new Polygon();
for (int i = 0; i < 6; i++)
{
p.add(new Point2D.Double(Math.sin(i*Math.PI/3), Math.cos(i*Math.PI/3)));
}
System.out.println("Area : " + p.getArea());
System.out.println("Expected : " + 3*Math.sqrt(3)/2);
}
}
Add this method to your program
public void recursionMethod(ArrayList<Point2D.Double> c, int size, int count){
if(count<size-1){
double xA = c.get(count).getX();
double yA = c.get(count).getY();
double xB = c.get(count+1).getX();
double yB = c.get(count+1).getY();
exAdding = c.get(size-1).getX()*c.get(size-size).getY();
adding = adding + xA*yB;
exMinus = c.get(size-1).getY()*c.get(size-size).getX();
minus = minus + yA*xB;
recursionMethod(c, size, count+1);
}
}
and call this method instead of your for loop
recursionMethod(c, size, 0);
I suspect you might be missing the point of recursion. While it is true that most iterative methods can be replaced with recursion and vice versa, in many cases one technique or the other is a natural fit for your problem. In your case this is naturally an iterative problem: the polygon is defined by its vertices and calculating the area involves visiting each one consecutively.
I would suggest picking a more naturally recursive problem if you wish to practice recursion. In general the form of recursion is:
result recursiveproblem(context)
if context is simple enough to have obvious answer
return obvious answer
else
break context into smaller pieces
call recursive problem on each of the smaller pieces
combine the answers
So this fits situations where there is a natural simple state with an obvious answer as well as ways of breaking down and combining answers.
The canonical example is factorial. By definition factorial(0) = 1 and factorial(n) = n * factorial(n - 1)
Already that looks very naturally recursive in a way that calculating area doesn't.
I have just been looking at using recursion to calculate centroids. I have written the following recursive solution for the equations listed in the Wikipedia page for Centroids of Polygons. You will see that you need to use the first two 2D points on the list to calculate areas etc and then add to the same function applied to the remainder of the list of 2D points. This seems to me to meet the criterion set by "sprinter" (but perhaps I am missing something).
The following F# code generates a tuple containing the area of the polygon and the two centroids:
let AddTuple3 (a, b, c) (d, e, f) = (a+d,b+e,c+f)
let Comb3 (X1, Y1) (X2, Y2) =
let A = (X1 * Y2 - X2 * Y1) / 2.0
(A, (X1 + X2) * A / 3.0, (Y1 + Y2) * A / 3.0)
let SecProp2D PtLst =
let rec RecSecProp2D PtLst =
match PtLst with
| [A] -> (0.0, 0.0, 0.0)
| B::C -> AddTuple3 (Comb3 B C.Head) (RecSecProp2D C)
| _ -> (0.0, 0.0, 0.0)
let (A, B, C) = RecSecProp2D PtLst
(A, B/A, C/A)
// Testing...
let PtLst1 = [(0.0,0.0);(2.0,0.0);(2.0,5.0);(0.0,5.0);(0.0,0.0)] // 2x5 rectangle
printfn "Recursive Area, Cx & Cy are %A" (SecProp2D PtLst1)
You can play with it on dotnetFiddle.

How do I fix this heart?

Seeing as Valentine's Day is fast approaching, I decided to create a heart. So I found this heart from mathematica.se:
I played around in Mathematica (solved for z, switching some variables around) to get this equation for the z-value of the heart, given the x and y values (click for full-size):
I faithfully ported this equation to Java, dealing with a couple out-of-bounds cases:
import static java.lang.Math.cbrt;
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
...
public static double heart(double xi, double yi) {
double x = xi;
double y = -yi;
double temp = 5739562800L * pow(y, 3) + 109051693200L * pow(x, 2) * pow(y, 3)
- 5739562800L * pow(y, 5);
double temp1 = -244019119519584000L * pow(y, 9) + pow(temp, 2);
//
if (temp1 < 0) {
return -1; // this is one possible out of bounds location
// this spot is the location of the problem
}
//
double temp2 = sqrt(temp1);
double temp3 = cbrt(temp + temp2);
if (temp3 != 0) {
double part1 = (36 * cbrt(2) * pow(y, 3)) / temp3;
double part2 = 1 / (10935 * cbrt(2)) * temp3;
double looseparts = 4.0 / 9 - 4.0 / 9 * pow(x, 2) - 4.0 / 9 * pow(y, 2);
double sqrt_body = looseparts + part1 + part2;
if (sqrt_body >= 0) {
return sqrt(sqrt_body);
} else {
return -1; // this works; returns -1 if we are outside the heart
}
} else {
// through trial and error, I discovered that this should
// be an ellipse (or that it is close enough)
return Math.sqrt(Math.pow(2.0 / 3, 2) * (1 - Math.pow(x, 2)));
}
}
The only problem is that when temp1 < 0, I cannot simply return -1, like I do:
if (temp1 < 0) {
return -1; // this is one possible out of bounds location
// this spot is the location of the problem
}
That's not the behavior of the heart at that point. As it is, when I try to make my image:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import static java.lang.Math.cbrt;
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
public class Heart {
public static double scale(int x, int range, double l, double r) {
double width = r - l;
return (double) x / (range - 1) * width + l;
}
public static void main(String[] args) throws IOException {
BufferedImage img = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB);
// this is actually larger than the max heart value
final double max_heart = 0.679;
double max = 0.0;
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
double xv = scale(x, img.getWidth(), -1.2, 1.2);
double yv = scale(y, img.getHeight(), -1.3, 1);
double heart = heart(xv, yv); //this isn't an accident
// yes I don't check for the return of -1, but still
// the -1 values return a nice shade of pink: 0xFFADAD
// None of the other values should be negative, as I did
// step through from -1000 to 1000 in python, and there
// were no negatives that were not -1
int r = 0xFF;
int gb = (int) (0xFF * (max_heart - heart));
int rgb = (r << 16) | (gb << 8) | gb;
img.setRGB(x, y, rgb);
}
}
ImageIO.write(img, "png", new File("location"));
}
// heart function clipped; it belongs here
}
I get this:
Look at that dip at the top! I tried changing that problematic -1 to a .5, resulting in this:
Now the heart has horns. But it becomes clear where that problematic if's condition is met.
How can I fix this problem? I don't want a hole in my heart at the top, and I don't want a horned heart. If I could clip the horns to the shape of a heart, and color the rest appropriately, that would be perfectly fine. Ideally, the two sides of the heart would come together as a point (hearts have a little point at the join), but if they curve together like shown in the horns, that would be fine too. How can I achieve this?
The problem is simple. If we look at that horseshoe region, we get imaginary numbers. For part of it, it should belong to our heart. In that region, if we were to evaluate our function (by math, not by programming), the imaginary parts of the function cancel. So it should look like this (generated in Mathematica):
Basically, the function for that part is almost identical; we just have to do arithmetic with complex numbers instead of real numbers. Here's a function that does exactly that:
private static double topOfHeart(double x, double y, double temp, double temp1) {
//complex arithmetic; each double[] is a single number
double[] temp3 = cbrt_complex(temp, sqrt(-temp1));
double[] part1 = polar_reciprocal(temp3);
part1[0] *= 36 * cbrt(2) * pow(y, 3);
double[] part2 = temp3;
part2[0] /= (10935 * cbrt(2));
toRect(part1, part2);
double looseparts = 4.0 / 9 - 4.0 / 9 * pow(x, 2) - 4.0 / 9 * pow(y, 2);
double real_part = looseparts + part1[0] + part2[0];
double imag_part = part1[1] + part2[1];
double[] result = sqrt_complex(real_part, imag_part);
toRect(result);
// theoretically, result[1] == 0 should work, but floating point says otherwise
if (Math.abs(result[1]) < 1e-5) {
return result[0];
}
return -1;
}
/**
* returns a specific cuberoot of this complex number, in polar form
*/
public static double[] cbrt_complex(double a, double b) {
double r = Math.hypot(a, b);
double theta = Math.atan2(b, a);
double cbrt_r = cbrt(r);
double cbrt_theta = 1.0 / 3 * (2 * PI * Math.floor((PI - theta) / (2 * PI)) + theta);
return new double[]{cbrt_r, cbrt_theta};
}
/**
* returns a specific squareroot of this complex number, in polar form
*/
public static double[] sqrt_complex(double a, double b) {
double r = Math.hypot(a, b);
double theta = Math.atan2(b, a);
double sqrt_r = Math.sqrt(r);
double sqrt_theta = 1.0 / 2 * (2 * PI * Math.floor((PI - theta) / (2 * PI)) + theta);
return new double[]{sqrt_r, sqrt_theta};
}
public static double[] polar_reciprocal(double[] polar) {
return new double[]{1 / polar[0], -polar[1]};
}
public static void toRect(double[]... polars) {
for (double[] polar: polars) {
double a = Math.cos(polar[1]) * polar[0];
double b = Math.sin(polar[1]) * polar[0];
polar[0] = a;
polar[1] = b;
}
}
To join this with your program, simply change your function to reflect this:
if (temp1 < 0) {
return topOfHeart(x, y, temp, temp1);
}
And running it, we get the desired result:
It should be pretty clear that this new function implements exactly the same formula. But how does each part work?
double[] temp3 = cbrt_complex(temp, sqrt(-temp1));
cbrt_complex takes a complex number in the form of a + b i. That's why the second argument is simply sqrt(-temp1) (notice that temp1 < 0, so I use - instead of Math.abs; Math.abs is probably a better idea). cbrt_complex returns the cube root of the complex number, in polar form: r eiθ. We can see from wolframalpha that with positive r and θ, we can write an n-th root of a complex numbers as follows:
And that's exactly how the code for the cbrt_complex and sqrt_complex work. Note that both take a complex number in rectangular coordinates (a + b i) and return a complex number in polar coordinates (r eiθ)
double[] part1 = polar_reciprocal(temp3);
It is easier to take the reciprocal of a polar complex number than a rectangular complex number. If we have r eiθ, its reciprocal (this follows standard power rules, luckily) is simply 1/r e-iθ. This is actually why we are staying in polar form; polar form makes multiplication-type operations easier, and addition type operations harder, while rectangular form does the opposite.
Notice that if we have a polar complex number r eiθ and we want to multiply by a real number d, the answer is as simple as d r eiθ.
The toRect function does exactly what it seems like it does: it converts polar coordinate complex numbers to rectangular coordinate complex numbers.
You may have noticed that the if statement doesn't check that there is no imaginary part, but only if the imaginary part is really small. This is because we are using floating point numbers, so checking result[1] == 0 will likely fail.
And there you are! Notice that we could actually implement the entire heart function with this complex number arithmetic, but it's probably faster to avoid this.

Categories