I am trying to take a set of angles from 0 to 359 and get the average direction of the angles. I have searched everywhere and some of the examples work but for some reason my code isn't working.
For example the average of the set of {355,355,15,15} should be 5 degrees but they I get a bunch of varying answers that don't make much sense.
I'm using this equation courtesy of wiki:
https://en.wikipedia.org/wiki/Mean_of_circular_quantities
public static void main(String[] args) {
//ATAN2(sum_of_sin(theta), sum_of_cos(theta))
double[] numbers = {355,355,15,15};
double sin=0.0, cos=0.0, theta=0.0;
for(double d : numbers) {
sin += Math.sin(d);
cos += Math.cos(d);
}
sin = sin / ((double)numbers.length);
cos = cos / ((double)numbers.length);
// Using only atan2
System.out.println("Atan2 Only: " + Math.toDegrees(Math.atan2(sin, cos)));
// Atan2 Only: 159.71920992022936
// Using the wiki solution
if (sin > 0 && cos > 0) {
theta = Math.atan(sin/cos);
} else if(cos < 0) {
theta = Math.atan(sin/cos) + 180;
} else if(sin < 0 && cos > 0) {
theta = Math.atan(sin/cos) + 360;
}
System.out.println("Wiki Answer: " + theta);
// Wiki Answer: 179.6460334382022
}
You need to convert from degrees to radians for the input to sin and cos then back again for the result:
double[] numbers = {355, 5, 15 };
double sin=0.0, cos=0.0, theta=0.0;
for(double d : numbers) {
double s = Math.sin(Math.toRadians(d));
sin += s;
double c = Math.cos(Math.toRadians(d));
cos += c;
}
sin = sin / ((double)numbers.length);
cos = cos / ((double)numbers.length);
// Using only atan2
System.out.println("Atan2 Only: " + Math.toDegrees(Math.atan2(sin, cos)));
// Atan2 Only: 159.71920992022936
// Using the wiki solution
if (sin > 0 && cos > 0) {
theta = Math.atan(sin/cos);
} else if(cos < 0) {
theta = Math.atan(sin/cos) + 180;
} else if(sin < 0 && cos > 0) {
theta = Math.atan(sin/cos) + 360;
}
System.out.println("Wiki Answer: " + theta);
System.out.println("Wiki Answer in degrees: " + Math.toDegrees(theta));
output:
Atan2 Only: 4.9999999999999964
Wiki Answer: 0.08726646259971642
Wiki Answer in degrees: 4.9999999999999964
The math methods in Java assume that you're working in radians, not degrees. Try converting all your values to radians by multiplying them by π / 180 and see if that fixes things.
NOTE: There are considerable flaws to this approach; leaving this answer here so that others understand these flaws. Please see comments between #LutzL and me (#Nadesri) for details.
Perhaps I am missing something...
I think you should be able to add all the numbers, take the sum modulo 360 (assuming degrees), and then divide by n
private double avgOfAngles(List<int> numbers) {
int n = numbers.size();
int sum = 0;
for (int i=0; i<numbers; i++) {
sum += numbers.get(i);
}
return (double) (sum % 360) / n;
}
Of course, the above assumes that acceptable answers range between 0 to 359, inclusive; if you prefer a different range (such as -180 to 179), then the above solution would need to be offset by the appropriate amount.
The wiki notes [0, 360] as a possibly counterintuitive example (since the arithmetic mean is 180 despite that 360 degrees is for most purposes the same thing as 0 degrees); I think the above solution still handles at least this example.
Related
I was asked to find the cos following this equation:
I was able to find the sin of the angle, however when finding the cos, the number I would get is quite different from the correct value:
I used the following code for finding the cos.
ps: I can't use math.cos
public static double cos(double x, int n){
// declaring cos and factorial
double cos = 0.0;
// this loop determines how long does the loop go so the answer is more accurate
for (long howlong = 1 ; howlong <=n; howlong++){
double factorial =1;
// this will calculate the factorial for even numbers ex/ 2*2 = 4 , 4-2 = 2
// for the denominator
for (int factorialnumber=1; factorialnumber<=2*howlong-2; factorialnumber++){
factorial = factorial * howlong;
}
// now we need to create the pattern for the + and -
// so we use % that switches the sign everytime i increments by 1
if (howlong%2==1){
cos = cos + (double) (Math.pow(x, 2*howlong-2)/factorial);
}
else{
cos = cos - (double) (Math.pow(x, 2*howlong-2)/factorial);
}
}
return cos;
}
edit: I figured out my mistake as it was multiplying the factorial by how long instead of factorial number.
You have two bugs.
(Bug 1) Where you wrote
factorial = factorial * howlong;
it should have been
factorial = factorial * factorialnumber;
(Bug 2) You're not resetting your factorials on each iteration through the outer loop. So you need to move the line
double factorial =1;
down a couple of lines, so that it's inside the outer loop.
If you make those two changes, then the result of cos(Math.PI / 6, 10) is 0.8660254037844386 which seems correct to me.
The computation of your factorial was wrong.
Try it with this code:
public static double cos(double x, int n) {
// declaring cos and factorial
double cos = 0.0;
// this loop determines how long does the loop go so the answer is more
// accurate
for (long howlong = 1; howlong <= n; howlong++) {
// now we need to create the pattern for the + and -
// so we use % that switches the sign everytime i increments by 1
if (howlong % 2 == 1) {
cos = cos + Math.pow(x, 2 * howlong - 2) / factorial(2 * howlong - 2);
}
else {
cos = cos - Math.pow(x, 2 * howlong - 2) / factorial(2 * howlong - 2);
}
}
return cos;
}
public static long factorial(long n) {
long result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
Your calculation is not correct, please change to
double value = 1;
for (int factorialnumber = 1; factorialnumber <= 2 * howlong - 2; factorialnumber++) {
value = factorialnumber * value;
}
factorial = value;
System.out.println(value + " " + (2 * howlong - 2));
I am attempting to calculate the Sine function of an angle entered by the user in radians. I need to print the value within a given tolerance as well. I have spent a lot of time and am making very little progress. Any help would be greatly appreciated! Thanks! My main issue is a returning a value within the given tolerance.
// Tolerance is an epsilon value
ex. .00001
public static void sineCalc(double angle, double tolerance) {
int power = 1;
double currentAnswer = 0.0;
int count = 0;
double answer = 0.0;
double difference = 0.0;
int i = 1;
while (difference > tolerance || difference == 0) {
if (i % 2 == 0) {
currentAnswer = -Math.pow(angle, power) / getfactorial(power);
} else {
currentAnswer = Math.pow(angle, power) / getfactorial(power);
}
answer = answer + currentAnswer;
power = power + 2;
difference = Math.sin(angle) - answer;
difference = Math.abs(difference);
count++;
i++;
}
System.out.println(answer);
}
public double getDamage(double distance){
int damage1 = 30; // (0 - 38.1)
int damage2 = 20; // (50.8 - *)
double range1 = 38.1;
double range2 = 50.8;
double damage = 0; // FORMULA
return damage;
}
I try to create a formula to calculate the amount of damage that has been effected by the distance.
(Variable Distance =)
0 till 38.1 metre It will return 30 damage.
50.8 till Inifite it will return 20 damage.
38.1 till 50.8 it will decrease linear 30 -> 20.
How can I make this method work?
Thanks in advance.
Sounds like this:
double x = (distance - range1) / (range2 - range1);
if (x < 0)
x = 0;
if (x > 1)
x = 1;
return damage1 + x * (damage2 - damage1);
Basically you follow a linear rule and also adjust to stay in your linear interval.
Looks like you want a step formula, not a linear formula. Step formula is basically a bunch of if-else if comparisons in code. Something like this:
public double getDamage(double dist){
if (0 < dist & dist < 38.1)
return 30;
else if ( 38.1 < dist & dist < 50.8 )
return 30 - dist/10;
else
return
}
Edit: just saw you do want it linearly between 38.1 and 50.8.
Use something like this return 30 - dist/10; dist/10 would give you damage of 27 to 23, you'd need to find an appropriate constant (instead of 10) yourself. (Which is easy since its y = mx + b and you have two points by your conditions (38.1, 30) and (50.8, 20). So sub those into y = mx+b and you'll get the formula to use in the 2nd else-if.
The formula you are looking for is a simple variation of the point-slop equation y = m(x-x1) + y1 equation, where m = (damage1 - damage2)/(range1 - range2), x1 = range1, y1 = damage1, and x is the variable distance.
public double getDamage(double distance){
int damage1 = 30;
int damage2 = 20;
double range1 = 38.1;
double range2 = 50.8;
double damage = 0;
if(0 <= distance && distance <= range1)
damage = damage1;
else if (range1 < distance && distance < range2)
damage = (damage1 - damage2)/(range1 - range2) * (distance - range1) + damage1;
else if (distance >= range2)
damage = damage2;
return damage;
}
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);
}
I know Math.sin() can work but I need to implement it myself using factorial(int) I have a factorial method already below are my sin method but I can't get the same result as Math.sin():
public static double factorial(double n) {
if (n <= 1) // base case
return 1;
else
return n * factorial(n - 1);
}
public static double sin(int n) {
double sum = 0.0;
for (int i = 1; i <= n; i++) {
if (i % 2 == 0) {
sum += Math.pow(1, i) / factorial(2 * i + 1);
} else {
sum += Math.pow(-1, i) / factorial(2 * i + 1);
}
}
return sum;
}
You should use the Taylor series. A great tutorial here
I can see that you've tried but your sin method is incorrect
public static sin(int n) {
// angle to radians
double rad = n*1./180.*Math.PI;
// the first element of the taylor series
double sum = rad;
// add them up until a certain precision (eg. 10)
for (int i = 1; i <= PRECISION; i++) {
if (i % 2 == 0)
sum += Math.pow(rad, 2*i+1) / factorial(2 * i + 1);
else
sum -= Math.pow(rad, 2*i+1) / factorial(2 * i + 1);
}
return sum;
}
A working example of calculating the sin function. Sorry I've jotted it down in C++, but hope you get the picture. It's not that different :)
Your formula is wrong and you are getting a rough result of sin(1) and all you're doing by changing n is changing the accuracy of this calculation. You should look the formula up in Wikipedia and there you'll see that your n is in the wrong place and shouldn't be used as the limit of the for loop but rather in the numerator of the fraction, in the Math.pow(...) method. Check out Taylor Series
It looks like you are trying to use the taylor series expansion for sin, but have not included the term for x. Therefore, your method will always attempt to approximate sin(1) regardless of argument.
The method parameter only controls accuracy. In a good implementation, a reasonable value for that parameter is auto-detected, preventing the caller from passing to low a value, which can result in highly inaccurate results for large x. Moreover, to assist fast convergence (and prevent unnecessary loss of significance) of the series, implementations usually use that sin(x + k * 2 * PI) = sin(x) to first move x into the range [-PI, PI].
Also, your method is not very efficient, due to the repeated evaluations of factorials. (To evaluate factorial(5) you compute factorial(3), which you have already computed in the previous iteration of the for-loop).
Finally, note that your factorial implementation accepts an argument of type double, but is only correct for integers, and your sin method should probably receive the angle as double.
Sin (x) can be represented as Taylor series:
Sin (x) = (x/1!) – (x3/3!) + (x5/5!) - (x7/7!) + …
So you can write your code like this:
public static double getSine(double x) {
double result = 0;
for (int i = 0, j = 1, k = 1; i < 100; i++, j = j + 2, k = k * -1) {
result = result + ((Math.pow(x, j) / factorial (j)) * k);
}
return result;
}
Here we have run our loop only 100 times. If you want to run more than that you need to change your base equation (otherwise infinity value will occur).
I have learned a very good trick from the book “How to solve it by computer” by R.G.Dromey. He explain it like this way:
(x3/3! ) = (x X x X x)/(3 X 2 X 1) = (x2/(3 X 2)) X (x1/1!) i = 3
(x5/5! ) = (x X x X x X x X x)/(5 X 4 X 3 X 2 X 1) = (x2/(5 X 4)) X (x3/3!) i = 5
(x7/7! ) = (x X x X x X x X x X x X x)/(7 X 6 X 5 X 4 X 3 X 2 X 1) = (x2/(7 X 6)) X (x5/5!) i = 7
So the terms (x2/(3 X 2)) , (x2/(5 X 4)), (x2/(7 X 6)) can be expressed as x2/(i X (i - 1)) for i = 3,5,7,…
Therefore to generate consecutive terms of the sine series we can write:
current ith term = (x2 / ( i X (i - 1)) ) X (previous term)
The code is following:
public static double getSine(double x) {
double result = 0;
double term = x;
result = x;
for (int i = 3, j = -1; i < 100000000; i = i + 2, j = j * -1) {
term = x * x * term / (i * (i - 1));
result = result + term * j;
}
return result;
}
Note that j variable used to alternate the sign of the term .