Calculating the term for pi using Taylor series using Java - java

I am trying calculating the term for Pi using the Taylor series. I want to keep adding terms until the last value of term is less than 1e-17. I have set the program right now at term = 31 because after that there is no change Pi = 3.141592653589794 error = 8.88178e - 16.
public static double compPi()
{
int terms1 = 31;
int sg = 1, denom1 = 1;
double sum = 1.0, denom2 = 1.0;
for (int t = 2; t <= terms1; t++){
denom1 += 2; denom2 *= 3;
double term = 1.0/ (denom1 * denom2);
sg *= -1;
sum += sg * term;
}
double pi = Math.sqrt(12) * sum;
return pi;
}

As Louis Wasserman suggested you are running into precision limitations with doubles in Java. Consider using BigDecimals for your calculations for more precision.

The floating-point errors in your sum are adding up to that difference, simply reverse your iteration (t = 31..2, i.e. start by adding up the very small summands first) and the error goes away:
public static double compPiReversed()
{
int terms1 = 31;
int sg = -1;
double sum = 0;
for (int t = terms1; t >= 2; --t) {
int denom1 = 1 + (t-1) * 2;
double denom2 = Math.pow(3, t-1);
double term = 1.0 / (denom1 * denom2);
sg *= -1;
sum += sg * term;
}
sum += 1;
double pi = Math.sqrt(12) * sum;
return pi;
}
again the 31st summand won't actually contribute (try starting at terms1 = 30, don't forget to change the sign sg = 1, too.)

Related

Sin approximation using Maclaurin expansion

I'm trying to write own code for calculating approximation of rad angle. I works so far for only a specific range of numbers, but fails for large ones like 500 or so.
Additional subquestion: which is more efficient - calculating powers by Math.pow() or the current way of doing it - only multiplication operators.
private static double sin_range(double rad) {
double sin_rad = rad;
while (sin_rad > 2 * Math.PI) {
sin_rad -= 2 * Math.PI;
}
return sin_rad;
}
private static double approx(double rad, int err) {
double rad_in_range = sin_range(rad);
double sin = rad_in_range, r = rad_in_range;
int previous = 1, factorial = 1;
for (int i = 0; i < err; i++) {
factorial = (factorial * (previous + 1) * (previous + 2));
r *= rad * rad;
if ((i & 1) == 0) { //even
sin -= r / factorial;
} else { //odd
sin += r / factorial;
}
previous += 2;
}
return sin;
}
public static void main(String[] args) {
double approximated = approx(15, 5);
System.out.println(approximated + " = " + Math.sin(15));
}

Return how many terms are necessary entering a level of precision, e.g .001, to come within the specified precision of the value of PI?

Problem: Interactively enter a level of precision, e.g., .001, and then report how many terms are necessary for each of these estimates to come within the specified precision of the value of pi.
My Solution So Far:
Current result does not terminate. The PIEstimator class driver is given. The problem lies inside the PIEstimates class. Some specific questions that I have:
How do you calculate Wallis PI and Leibniz PI in code? The ways for calculating each arithmetically for Wallis is:
pi/4 = (2/3)(4/3)(4/5)(6/5)(6/7)(8/7)(8/9)*...
and for Leibniz:
pi/4 = (1 - 1/3 + 1/5 - 1/7 + 1/9 ...)
Is the current logic of doing this code correct so far? Using a while loop to check if the respective PI calculation is within the limits, with a for loop inside continuing to run until the while requirements are met. Then there is a count that returns the number of times the loop repeated.
For .001, for example, how many terms does it take for each of these formulas to reach a value between 3.14059.. and 3.14259...
import java.util.Scanner;
public class PiEstimator {
public static void main(String[] args) {
System.out.println("Wallis vs Leibnitz:");
System.out.println("Terms needed to estimate pi");
System.out.println("Enter precision sought");
Scanner scan = new Scanner(System.in);
double tolerance = scan.nextDouble();
PiEstimates e = new PiEstimates(tolerance);
System.out.println("Wallis: " + e.wallisEstimate());
System.out.println("Leibnitz: " + e.leibnitzEstimate());
}
}
public class PiEstimates {
double n = 0.0;
double upperLim = 0;
double lowerLim = 0;
public PiEstimates(double tolerance) {
n = tolerance;
upperLim = Math.PI+n;
lowerLim = Math.PI-n;
}
public double wallisEstimate() {
double wallisPi = 4;
int count = 0;
while(wallisPi > upperLim || wallisPi < lowerLim) {
for (int i = 3; i <= n + 2; i += 2) {
wallisPi = wallisPi * ((i - 1) / i) * ((i + 1) / i);
}
count++;
}
return count;
}
public double leibnitzEstimate() {
int b = 1;
double leibnizPi = 0;
int count = 0;
while(leibnizPi > upperLim || leibnizPi < lowerLim) {
for (int i = 1; i < 1000; i += 2) {
leibnizPi += (4/i - 4/i+2);
}
b = -b;
count++;
}
return count;
}
}
At least one mistake in wallisEstimate
for (int i = 3; i <= n + 2; i += 2) {
should be count instead of n:
for (int i = 3; i <= count + 2; i += 2) {
... but still then the algorithm is wrong. This would be better:
public double wallisEstimate() {
double wallisPi = 4;
int count = 0;
int i = 3;
while(wallisPi > upperLim || wallisPi < lowerLim) {
wallisPi = wallisPi * ((i - 1) / i) * ((i + 1) / i);
count++;
i += 2;
}
return count;
}
Similarly, for the Leibniz function:
public double leibnitzEstimate() {
double leibnizPi = 0;
int count = 0;
int i = 1;
while(leibnizPi > upperLim || leibnizPi < lowerLim) {
leibnizPi += (4/i - 4/i+2);
count++;
i += 4;
}
return count;
}
For Leibnitz, I think the inner loop should only run count number of times:
for (int i = 1; i < count; i += 2) {
leibnizPi += (4/i - 4/i+2);
}
If it runs 1000 times every time you can't know when pi was in range.
If the while loops don't terminate, then they don't approach PI.
Wallis: pi/4 = (2/3)(4/3)(4/5)(6/5)(6/7)(8/7)(8/9)*...
The while loop would restart the for loop at i = 3 which would keep adding the same sequence of terms, rather than progressing along with the pattern.
Here's a draft of an alternative solution (no pun intended) based on the pattern of terms.
As you see, the numerators repeat, except for the 2. The divisors repeat aswell, but offset from the numerators: they increment in an alternating fashion.
This way you can actually count each individual terms, instead of pairs of them.
public double wallisEstimate() {
double wallisPi = 1;
int count = 0;
double numerator = 2;
double divisor = 3;
while(wallisPi > upperLim || wallisPi < lowerLim) {
wallisPi *= numerator / divisor;
if ( count++ & 1 == 0 )
numerator+=2;
else
divisor+=2;
}
return count;
}
There is one more change to make, and that is in the initialisation of upperLim and lowerLim. The algorithm approaches PI/2, so:
upperLim = (Math.PI + tolerance)/2;
lowerLim = (Math.PI - tolerance)/2;
Leibniz: pi/4 = (1 - 1/3 + 1/5 - 1/7 + 1/9 ...)
Here the for loop is also unwanted: at best, you will be counting increments of 2000 terms at a time.
The pattern becomes obvious if you write it like this:
1/1 - 1/3 + 1/5 - 1/7 ...
The divisor increments by 2 for every next term.
public double leibnitzEstimate() {
double leibnizPi = 0;
int divisor = 1;
int count = 0;
while(leibnizPi > upperLim || leibnizPi < lowerLim) {
leibnizPi += 1.0 / divisor;
divisor = -( divisor + 2 );
count++;
}
return count;
}
The update of leibnizPi can also be written like this:
leibnizPi += sign * 1.0 / divisor;
divisor += 2;
sign = -sign;
which is more clear, takes one more variable and one more instruction.
An update to the upperLim and lowerLim must be made here aswell: the algorithm approaches PI/4 (different from Leibniz!), so:
upperLim = (Math.PI + tolerance)/4;
lowerLim = (Math.PI - tolerance)/4;

Adding last average value

I'm having trouble adding the last number of the loop that I have. I don't have any ideas how to include the last number and then add it to the double variable and then divide it to make the average. I would use my IDE to solve the problem but the input has to be approximately 1000 trials in order to be accurate. You can plainly see that 10 trials of 3.0 or higher does not equal approximately 2.8. I just need to have the missing trial added and then calculated into the average.
Code:
import java.util.*;
public class CalculatePI2
{
public static boolean ifitisInside (double xPosion, double yPosion)
{
double distance = Math.sqrt((xPosion * xPosion) + (yPosion * yPosion));
return (distance < 1.0);
}
public static double calculatePI (int numThrows)
{
Random randomGen = new Random();
int hits = 0;
double PI = 0;
double Alpha=0;
double average= 0;
for( int m=0; m<10; m++)
{
Alpha=+PI;
average= m/Alpha;
if(m>=0)
{
hits=0;
PI=0;
for (int i = 0; i <= numThrows; i++)
{
double xPosion = (randomGen.nextDouble()) * 2 - 1.0;
double yPosion = (randomGen.nextDouble()) * 2 - 1.0;
if (ifitisInside(xPosion, yPosion))
{
hits++;
double dthrows = numThrows;
PI =+ (4.0 * (hits/dthrows));
}
}
System.out.println("Trial["+m+"]: ="+ PI);
}
}
System.out.println("Estimate:"+average);
return PI;
}
public static void main (String[] args)
{
Scanner pie = new Scanner (System.in);
System.out.println("This program approximates PI using the Monte Carlo method. By simulating throwing darts at a dartboard. ");
System.out.print("Please enter number of throws: ");
int numThrows = pie.nextInt();
double PI = calculatePI(numThrows);
}
}
Just take the average computation out of the inner loop. Also, the loop invariant is from 0 to < numthrows, not <= numthrows. Also, your initialization of the variables needs to happen for each of your 10 trials, so it needs to be moved inside the loop.
double sumPiOverMTrials = 0;
for( int m=0; m<10; m++)
{
double hits = 0;
for (int i = 0; i < numThrows; i++) {
double xPosition = (randomGen.nextDouble()) * 2 - 1.0;
double yPosition = (randomGen.nextDouble()) * 2 - 1.0;
if (ifitisInside(xPosition, yPosition))
{
hits++;
}
}
double pi = 4.0 * hits/numthrows;
System.out.println("Trial["+m+"]: ="+ pi);
sumPiOverMTrials += pi;
}
System.out.println("Average over "+m+" trials ="+ sumPiOverMTrials/10);
I think you can solve this just by rearranging your code:
public static double calculatePI (int numThrows)
{
Random randomGen = new Random();
int hits = 0;
double PI = 0;
double Alpha=0;
double average= 0;
for( int m=0; m<10; m++)
{
for (int i = 0; i <= numThrows; i++)
{
double xPosion = (randomGen.nextDouble()) * 2 - 1.0;
double yPosion = (randomGen.nextDouble()) * 2 - 1.0;
if (ifitisInside(xPosion, yPosion))
{
hits++;
double dthrows = numThrows;
PI += (4.0 * (hits/dthrows)); // NOTE: += not =+
}
}
Alpha+=PI; // NOTE += not =+
average= m/Alpha;
if(m>=0)
{
hits=0;
PI=0;
}
System.out.println("Trial["+m+"]: ="+ PI);
}
}
This way the average is calculated after each trial rather than before. Also, I don't know much about this particular algorithm, but I think that this line:
average=m/Alpha;
should be:
average=Alpha/m

Summing array values java

I'm feeding in an array of length 8 say if trials is 100 it might be of the form 93 5 2 0 0 0 0 0, but whatever the values I have in the array I only get 0.6 back. If anyone can see if I'm making a stupid error that would be great. I've tried it with a for loop but keep getting 0.6.
static void getMetric(int[]a, int trials){
double metric = 0;
int i =0;
while(i<8){
if(i==0){
double x = (a[0] / trials) - (2 / 15);
metric += Math.abs(x);
i++;
}
else if(i>0 && i<7){
double x = (a[i] / trials) - 0.1;
metric += Math.abs(x);
i++;
}
else{
double x = (a[7] / trials) - (2 / 15);
metric += Math.abs(x);
System.out.println(""+metric);
i++;
}
}
}
You use integer division ( 5 / 3 = 1; 2 / 15 = 0).
So instead of a[0] / trials, you should have a[0] / (double) trials;
Instead of 2 / 15 you should have 2 / 15.0 etc.
It looks like you need double-division and not int-division. Remember:
int a = 96;
int b = 100;
double c = a / b; //will be 0.0!
so the following program should do the same, but more correct, I think (and shorter):
static void getMetric(int[] a, int trials){
double metric = Math.abs((((double)a[0]) / trials) - (2 / 15));
for (int i = 1; i < 7; i++) {
metric += Math.abs((((double)a[i]) / trials) - 0.1);
}
metric += Math.abs((((double)a[7]) / trials) - (2 / 15));
System.out.println(""+metric);
}
and that one is even more reable and robust:
static void getMetric(int[] a, int trials){
double metric = calcMetricDiff(a[0], trials, 2.0 / 15.0);
for (int i = 1; i < a.length - 1; i++) {
metric += calcMetricDiff(a[i], trials, 0.1);
}
metric += calcMetricDiff(a[a.length-1], trials, 2.0 / 15.0);
System.out.println(""+metric);
}
private static double calcMetricDiff(double val, int trials, double diff) {
return Math.abs((val / trials) - diff);
}

for-loop, increment by double

I want to use the for loop for my problem, not while. Is it possible to do the following?:
for(double i = 0; i < 10.0; i+0.25)
I want to add double values.
To prevent being bitten by artifacts of floating point arithmetic, you might want to use an integer loop variable and derive the floating point value you need inside your loop:
for (int n = 0; n <= 40; n++) {
double i = 0.25 * n;
// ...
}
You can use i += 0.25 instead.
James's answer caught the most obvious error. But there is a subtler (and IMO more instructive) issue, in that floating point values should not be compared for (un)equality.
That loop is prone to problems, use just a integer value and compute the double value inside the loop; or, less elegant, give yourself some margin: for(double i = 0; i < 9.99; i+=0.25)
Edit: the original comparison happens to work ok, because 0.25=1/4 is a power of 2. In any other case, it might not be exactly representable as a floating point number. An example of the (potential) problem:
for(double i = 0; i < 1.0; i += 0.1)
System.out.println(i);
prints 11 values:
0.0
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
for(double i = 0; i < 10.0; i+=0.25) {
//...
}
The added = indicates a shortcut for i = i + 0.25;
In
for (double i = 0f; i < 10.0f; i +=0.25f) {
System.out.println(i);
f indicates float
The added = indicates a shortcut for i = i + 0.25;
For integer.
We can use : for (int i = 0; i < a.length; i += 2)
for (int i = 0; i < a.length; i += 2) {
if (a[i] == a[i + 1]) {
continue;
}
num = a[i];
}
Same way we can do for other data types also.
private int getExponentNumber(double value){
String[] arr;
String strValue = String.valueOf(value);
if (strValue.contains("E")){
arr = strValue.split("E");
return Math.abs(Integer.parseInt(arr[1]));
}
else if (strValue.contains(".")){
arr = strValue.split("\\.");
return arr[1].length();
}
return 0;
}
private int getMinExponent(int start, int stop, int step){
int minExponent = Math.max(Math.abs(start), Math.abs(stop));
minExponent = Math.max(minExponent, Math.abs(step));
return minExponent;
}
double start = 0;
double stop = 1.362;
double step = 2E-2;
int startExp = getExponentNumber(start);
int stopExp = getExponentNumber(stop);
int stepExp = getExponentNumber(step);
int min = getMinExponent(startExp, stopExp, stepExp);
start *= Math.pow(10, min);
stop *= Math.pow(10, min);
step *= Math.pow(10, min);
for(int i = (int)start; i <= (int)stop; i += (int)step)
System.out.println(i/Math.pow(10, min));

Categories