Weird floating point error? - java

I've been trying to generate a pattern of circles using a for loop. However, when it runs everything looks fine except for the 9th ring of circles which is ever so slightly. Having looked at a print out of numbers for that circle everything looks fine, so I can't work out what is going wrong. However, when I add one to the angle value of that ring. i.e. j (the commented out code) it pretty much corrects.
Any idea why this might happen. having looked at all the numbers I can only think it is some math error that I haven't factored in or am I missing something obvious.
Thanks!
ellipse(325,325,15,15);
float div = 1;
for (int i = i; i < 25; i++)
{
div = i*6
float segment = 360/div;
float radius = (i*20);
for (int j = 0; j < 360; j+=segment)
{
//if (i==8)
//{
//println("before " + j);
//j+=1;
//println("after " + j);
//}
float x = 325 + (radius*cos(radians(j)));
float y = 325 + (radius*sin(radians(j)));
ellipse(x, y, 15, 15);
}
}

Ok, three things, in order of importance. Two of these have already been mentioned.
1) Clean out the ints. Variable i can be an int, but none of the others (especially j since it is an angle, not a counter or index), and you want to make sure that all math operations treat numbers as doubles. Go so far as to designate your constants as doubles (e.g. use 1d instead of 1).
2) Avoid cumulative errors. In your inner for loop, you repeatedly add to j. Bad. Instead, compute your angle directly based on which circle you're computing.
3) Use double, not float. You'll get better precision.
I'd do it like this...
ellipse(325,325,15,15);
for (int i = i; i < 25; i++)
{
double div = i*6d;
double radius = (i*20d);
for (int j = 0; j < div; j++)
{
double theta = j * 360d / div;
double x = 325d + (radius*cos(radians(theta)));
double y = 325d + (radius*sin(radians(theta)));
ellipse(x, y, 15, 15);
}
}

You get the segment as a float, but then you use an int to calculate the degrees.
for (int j=0; j < 360; j+=segment)
and
float x = 325 + (radius*cos(radians(j)));
This is what is causing the rounding errors.
And if you make i to get a value bigger than 60 the program will never end.

Use double instead of float to minimise representation error.
Change the for loop to reduce error.
for (int k = 0; k < div; k++) {
int j = k * 360 / div;
This will give you different values for j which are likely to be more correct.

Related

Finding the smallest distance between origin and coordinates (larger than radius) in Java

Given a radius, find the coordinates (x, y) such that the distance between (x, y) to the origin is greater than the radius. However, I want to find the distance that is the smallest distance that is greater than the radius. Problem is seen here: open.kattis.com/problems/discdistrict. My code works well for radii that are less than or equal to 5000. However, for large radii, my code starts to break and takes exponentially longer to finish!! Are there any ideas?
Examples: 1 yields (1, 1). 5 yields (2, 5). 10 yields (5, 9). (radius | radius >= 10,000) takes an exponentially long period of time.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class disc_district {
public static void main(String[] args) {
Scanner new_scanner = new Scanner(System.in);
int radius = new_scanner.nextInt();
new_scanner.close();
//ArrayList<Double> new_distance = new ArrayList<>();
double min_max_dist = Double.MAX_VALUE - 1;
int[] new_min_pair = new int[2];
for (int i = (radius / 2); i <= radius; i++) {
int start = (int)Math.floor(Math.sqrt(Math.pow(radius, 2) - Math.pow(i, 2))) + 1;
for (int j = Math.max(i, start); j <= radius; j++) {
//for (int j = i; j <= radius; j++) {
double new_dist = Math.sqrt(Math.pow(i, 2) + Math.pow(j, 2));
if (new_dist > radius) {
if (min_max_dist > new_dist) {
min_max_dist = new_dist;
new_min_pair[0] = i;
new_min_pair[1] = j;
}
}
}
}
System.out.println(new_min_pair[0] + " " + new_min_pair[1]);
}
}
Thanks again!
It does not takes an exponential time to finish, just a quadratic time, that is O(radius² / 4). This is because you use 2 nested loops. You can speed up the inner-most loop by just solving a basic conic inequation.
Indeed, you are searching for j values where sqrt(i² + j²) > r with r = radius. This means i² + j² > r² since all values are expected to be positives ones. This means j² > r² - i² and so j > sqrt(r² - i²). As a result, you can start the second inner loop to the value Math.sqrt(Math.pow(r, 2) - Math.pow(i, 2)) if it is bigger than i (note that the expression in the square root is always positive since i <= r).
The resulting code is:
for (int i = (radius / 2); i <= radius; i++) {
int start = (int)Math.floor(Math.sqrt(Math.pow(radius, 2) - Math.pow(i, 2))) + 1;
for (int j = Math.max(i, start); j <= radius; j++) {
System.out.println(i + " " + j);
}
}
Note that if you want to get one value, you can just use a break instruction so to avoid many unneeded computations.
Assuming the initial code is correct and you really need all the values to be printed, the complexity of the new code is optimal (each iteration is done in a constant time and is strictly useful since it results in a printed line), but it is still quadratic. A deeper analysis of the code shows its (optimal) complexity is O(radius² / 11.68). Thus the new code is only about ~3 times faster in theory. It can still be slow since there are a lot of values to be printed. It may be interesting to better control when the output is flushed.
Update:
Based on the additional information (about the distance being minimized and only one solution required to be printed), here is the resulting code:
double min_max_dist = Double.MAX_VALUE - 1;
int[] new_min_pair = new int[2];
for (int i = (radius / 2); i <= radius; i++) {
int start = (int)Math.floor(Math.sqrt(Math.pow(radius, 2) - Math.pow(i, 2))) + 1;
int j = Math.max(i, start);
double new_dist = Math.sqrt(Math.pow(i, 2) + Math.pow(j, 2));
if (new_dist > radius) {
if (min_max_dist > new_dist) {
min_max_dist = new_dist;
new_min_pair[0] = i;
new_min_pair[1] = j;
}
}
}
System.out.println(new_min_pair[0] + " " + new_min_pair[1]);
This code find the solution that minimize the distance. It runs in O(n/2) time, that is, a linear time.

Find longest distance from a certain point (java, 2d diagram)

I'm working on a clustering program in Java. I'm trying to find the point that has the longest distance from another point in an 2-dimentional diagram with x and y axis.
I though I could use pytagoras:
Where the square of the Y-axis of the starting point + the square of the X-axis of the of the other points will determine the distance between them.
What my code does is for this certain point, check all other points to see if it finds a point with a higher distance. The code I have at the moment is the following:
// The points to find longest distance from
x_coord = 2;
y_coord = 4;
// Need to find right size
double var1 = Math.pow(y_coord, 2); // square of Y
double var2 = 0;
double var3 = 0;
int sum = 0;
/* For all coords ( of the cluster clusters)
* coordsX is an array that holds all the X coordinates
* of all other points
*/
for (int k = 0; k < coordsX.length; k++){
// Check which is furthest away from
var2 = Math.pow(coordsX[k], 2); // square of X
var3 = var1 + var2; // Sum of var1 and var2
sum = (int)Math.sqrt(var3); // Square root to find distance
if (sum > longestDistance){
longestDistance = sum;
}
}
Does anyone have any suggestions what might be wrong? Or is this an unsuited method to calculate distances?
So, to find the distance between two points, say A and B located on a xy plane, where A and B are indices, here is what you need to do:
double distanceSquared = Math.pow(coordsX[A] - coordsX(B), 2) + Math.pow(coordsY[A] - coordsY(B), 2);
And if you just want to find the most distant point, there is no need to take a square root, because it is a monotonic function, so it does not change the result of comparison at all.
Simply compare the distance squares
EDIT: code for you.
double longestDistanceSquared = 0;
int mostDistantPointIndex = -1;
for (int k = 0; k < coordsX.length; k++){
double distanceSquared = Math.pow(coordsX[k] - x_coord, 2) + Math.pow(coordsY[k] - y_coord, 2);
if (distanceSquared > longestDistanceSquared){
longestDistanceSquared = distanceSquared;
mostDistantPointIndex = k;
}
}
The first thing that jumps out of the code to me is that you are casting the result of the sqrt() call to an int. That seems like it's going to cause problems.

can someone explain the steps to compute this equation? Java

Write a program that computes the following equation.
100/1+99/2+98/3+97/4+96/5...3/98+2/99+1/100
I am not asking for a solution. Yes this is a homework problem, but I am not here to copy paste the answers. I asked my professor to explain the problem or how should I approach this problem? She said "I can't tell you anything."
public static void main(String[] args){
int i;
for(i = 100; i >= 1; i--)
{
int result = i/j;
j = j+1;
System.out.println(result);
}
}
You can try to observe a "trend" or "pattern" when solving questions of this type.
Given: 100/1+99/2+98/3+97/4+96/5...3/98+2/99+1/100
We derived: Numerator/Denominator, let's call it n divide by d (n/d)
Pattern Observed:
n - 1 after every loop
d + 1 after every loop
So, if you have 100 numbers, you need to loop 100 times. Thus using a for-loop which loops 100 times will seemed appropriate:
for(int n=0; n<100; n++) //A loop which loops 100 times from 0 - 100
To let n start with 100, we change the loop a little to let n start from 100 instead of 0:
for(int n=100; n>0; n--) //A loop which loops 100 times from 100 - 0
You settled n, now d needs to start from 1.
int d = 1; //declare outside the loop
Putting everything together, you get:
int d = 1;
double result = 0.0;
for (int n=100; n>0; x--)
{
result += (double)n/d; //Cast either n or d to double, to prevent loss of precision
d ++; //add 1 to d after every loop
}
You are on the right track. You need to loop like you've done, but then you need to SUM up all the results. In your example you can try:
result = result + i/j;
or
result += i/j;
Note that the declaration of result needs to be outside the loop otherwise you are always initializing it.
Also think about the division (hint), you are dividing integers...
What you have is a series.
There is more than one way to define a series, but all things being the same it's more intuitive to have the index of a series increase rather than decrease.
In this case, you could use i from 0 to 99.
Which in java can be:
double sum = 0;
for (int i = 0; i < 100; i++) {
sum += (100 - i) / (double) (1 + i);
}
if you want the result in the same format then do :
int j = 100;
double sum=0;
for (int i = 1; i <= 100; i++) {
sum += ((double) j / i); // typecast as least one of i or j to double.
System.out.print(j + "/" + i+"+");
j--;
}
// here print the sum

Calculating Standard Deviation of Angles?

So I'm working on an application using compass angles (in degrees). I've managed to determine the calculation of the mean of angles, by using the following (found at http://en.wikipedia.org/wiki/Directional_statistics#The_fundamental_difference_between_linear_and_circular_statistics) :
double calcMean(ArrayList<Double> angles){
double sin = 0;
double cos = 0;
for(int i = 0; i < angles.size(); i++){
sin += Math.sin(angles.get(i) * (Math.PI/180.0));
cos += Math.cos(angles.get(i) * (Math.PI/180.0));
}
sin /= angles.size();
cos /= angles.size();
double result =Math.atan2(sin,cos)*(180/Math.PI);
if(cos > 0 && sin < 0) result += 360;
else if(cos < 0) result += 180;
return result;
}
So I get my mean/average values correctly, but I can't get proper variance/stddev values. I'm fairly certain I'm calculating my variance incorrectly, but can't think of a correct way to do it.
Here's how I'm calculating variance:
double calcVariance(ArrayList<Double> angles){
//THIS IS WHERE I DON'T KNOW WHAT TO PUT
ArrayList<Double> normalizedList = new ArrayList<Double>();
for(int i = 0; i < angles.size(); i++){
double sin = Math.sin(angles.get(i) * (Math.PI/180));
double cos = Math.cos(angles.get(i) * (Math.PI/180));
normalizedList.add(Math.atan2(sin,cos)*(180/Math.PI));
}
double mean = calcMean(angles);
ArrayList<Double> squaredDifference = new ArrayList<Double>();
for(int i = 0; i < normalizedList.size(); i++){
squaredDifference.add(Math.pow(normalizedList.get(i) - mean,2));
}
double result = 0;
for(int i = 0; i < squaredDifference.size(); i++){
result+=squaredDifference.get(i);
}
return result/squaredDifference.size();
}
While it's the proper way to calculate variance, I'm not what I'm supposed to use. I presume that I'm supposed to use arctangent, but the standard deviation/variance values seem off. Help?
EDIT:
Example: Inputting the values 0,350,1,0,0,0,1,358,9,1 results with the average angle of 0.0014 (since the angles are so close to zero), but if you just do a non-angle average, you'll get 72...which is way off. Since I don't know how to manipulate individual values to be what they should be, the variance calculated is 25074, resulting in a standard deviation of 158 degrees, which is insane!! (It should only be a few degrees) What I think I need to do is properly normalize individual values so I can get correct variance/stddev values.
By the Wikipedia page you link to the circular standard deviation is sqrt(-log R²), where R = |mean of samples|, if you consider the samples as complex numbers on the unit circle. So the calculation of standard deviation is very similar to the calculation of the mean angle:
double calcStddev(ArrayList<Double> angles){
double sin = 0;
double cos = 0;
for(int i = 0; i < angles.size(); i++){
sin += Math.sin(angles.get(i) * (Math.PI/180.0));
cos += Math.cos(angles.get(i) * (Math.PI/180.0));
}
sin /= angles.size();
cos /= angles.size();
double stddev = Math.sqrt(-Math.log(sin*sin+cos*cos));
return stddev;
}
And if you think about it for a minute it makes sense: When you average a bunch of points close to each other on the unit circle the result is not too far off from the circle, so R will be close to 1 and the stddev near 0. If the points are distributed evenly along the circle their average will be close to 0, so R will be close to 0 and the stddev very large.
When you use Math.atan(sin/cosine) you get an angle between -90 and 90 degrees. If you have 120 degrees angle, you get cos=-0.5 and sin=0.866, then you get atan(-1.7)=-60 degrees. Thus you put wrong angles in your normalized list.
Assuming that variance is a linear deviation, I'd recommend you to rotate your angles array by the -calcMean(angles) and add/subtract 360 to/from angles above/below 180/-180 (damn my writing!)) while finding maximum and minimum angle. It will give you desired deviations. Like this:
Double meanAngle = calcMean(angles)
Double positiveDeviation = new Double(0);
Double negativeDeviation = new Double(0);
Iterator<Double> it = angles.iterator();
while (it.hasNext())
{
Double deviation = it.next() - meanAngle;
if (deviation > 180) deviation -= 180;
if (deviation <= -180) deviation += 180;
if (deviation > positiveDeviation) positiveDeviation = deviation;
if (deviation > negativeDeviation) negativeDeviation = deviation;
}
return positiveDeviation - negativeDeviation;
For average squared deviations you should use your method (with angles, not "normalized" ones), and keep looking for (-180, 180) range!
The math library function remainder is handy for dealing with angles.
A simple change would be to replace
normalizedList.get(i) - mean
with
remainder( normalizedList.get(i) - mean, 360.0)
However your first loop is then redundant, as the call to remainder will take care of all the normalisation. Moreover it's simpler just to sum up the squared differences, rather than store them. Personally I like to avoid pow() when arithmetic will do. So your function could be:
double calcVariance(ArrayList<Double> angles){
double mean = calcMean(angles);
double result = 0;
for(int i = 0; i < angles.size(); i++){
double diff = remainder( angles.get(i) - mean, 360.0);
result += diff*diff;
}
return result/angles.size();
}
The current good way to deal with this is now the two functions already implemented in scipy :
circmean : https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.circmean.html
circstd : https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.circstd.html
Couple of great things included :
vectorization for fast computing
nan dealing
high, low thresholds, typically for angles between 0 and 360 degrees vs between 0 and 2 Pi.
The accepted answer by Joni does an excellent job at answering this question, but as Brian Hawkins noted:
Mind the units. The function as written takes angles in degrees as input and returns the standard deviation in radians.
Here's a version that fixes that issue by using degrees for both its arguments and its return value. It also has more flexibility, as it allows for a variable number of arguments.
public static double calcStdDevDegrees(double... angles) {
double sin = 0;
double cos = 0;
for (int i = 0; i < angles.length; i++) {
sin += Math.sin(angles[i] * (Math.PI/180.0));
cos += Math.cos(angles[i] * (Math.PI/180.0));
}
sin /= angles.length;
cos /= angles.length;
double stddev = Math.sqrt(-Math.log(sin*sin+cos*cos));
return Math.toDegrees(stddev);
}

Strange double rounding issue

I came across the following silly function here:
public static String findOutWhatLifeIsAllAbout() {
int meaning = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 20; j++) {
for (int k = 0; k < 300; k++) {
for (int m = 0; m < 7000; m++) {
meaning += Math.random() + 1;
}
}
}
}
return String.valueOf(meaning).replaceAll("0*$", "");
}
In summary, the expected result is a string "42", since Math.random() returns doubles "greater than or equal to 0.0 and less than 1.0". In practice, running on an i5 under Ubuntu the resulting strings are similar to "420000011", "420000008"! (meaning sometimes Math.random()'s result is getting rounded up!
To get a grip on what sorts of double values would cause Math.random()'s result to somehow round to 1, I tried this function instead, expecting to see some examples.
public static String findOutWhatLifeIsAllAboutAltered() {
int meaning = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 20; j++) {
for (int k = 0; k < 300; k++) {
for (int m = 0; m < 7000; m++) {
double randomResult = Math.random();
int converted = randomResult;
if (converted > 0) {
System.out.println("Misbehaving double = " + randomResult);
}
meaning += converted + 1;
}
}
}
}
return String.valueOf(meaning).replaceAll("0*$", "");
}
However this formulation always returns "42"! Can anyone offer insight about why the behavior changes in the altered function? Thanks.
Furthermore why does Java let the original function compile at all? Shouldn't there be a loss-of-precision error at the += call?
edit posted the code I wanted you all to see - the "original" version wasn't supposed to have a cast.
There's a small but important difference between the code in the link and the code originally posted here,
meaning += Math.random() + 1; // link
vs.
meaning += (int)Math.random() + 1; // StackOverflow
If the code posted here prints out anything but 42, it's a serious bug.
Here, the result of Math.random() is explicitly cast to int, that must result in 0, then 1 is added, resulting in 1, which then is added to meaning.
The code in the linked post, however performs an implicit cast to int after adding the result of Math.random() to 1 and that to meaning, basically
meaning = (int)(Math.random() + (double)1 + (double)meaning);
Now, if the result of Math.random() is close enough to 1.0, it occasionally happens that the result of the double addition is rounded up, so producing a final result slightly larger than immediately expected.
Shouldn't there be a loss-of-precision error at the += call?
No. In the first case, you're explicitly casting the value returned by Math.random() to an int. In the second case, meaning, converted, and 1 are all integers.
There is, however, a possible loss-of-precision at this line:
int converted = randomResult;
http://ideone.com/1ZTDi
There won't be a loss of precision error at all because they're all ints in this example - you're not actually adding any doubles!
The only line where you could be you're casting the result of Math.random() to an int - so you're still just adding two ints together.
However, even if you were adding doubles to an int then there still wouldn't be because the JLS defines an implicit cast for these types of operators:
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.
Source:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.26.2
Loss of precision could only come on these lines as you're doing type conversion:
double randomResult = Math.random();
int converted = randomResult;
As Math.random() returns an double which is between 0.0 inclusive and 1.0 exclusive, the variable converted will only ever contain 0 as it's converted to an int.
For more on the casting from a double > int, see here: Different answer when converting a Double to an Int - Java vs .Net
I've tried your original program on a Java 6 system running on Mac OS X (Lion) and it only outputs 42. Which JRE/JDK are you using?

Categories