A few Programs to Demonstrate Control Structures

Program 1: Exponential Function Series Expansion

Write a Java program to calculate the series expansion of the exponential function e^x. The exponential function can be expressed as a series:

\[e^x = 1 + \frac{x}{1!} + \frac{x^2}{2!} + \frac{x^3}{3!} + \ldots\]

The program should prompt the user to enter the value of x and terminate the calculation when the absolute difference between two successive terms is less than 0.0001. Implement a private method to calculate the factorial of a number.

  • Class Name: ExponentialSeries
  • Methods:
    • calculateExponential(double x)
    • private static double factorial(int n)
  • Main Class Name: App

Solution for Question 1: Calculate Exponential Function (e^x) Series Expansion

We will represent the class that is expected to be created diagrammatically. The - indicates private methods and the + indicates public methods.

classDiagram
    class ExponentialSeries {
      -double factorial(int n)
      +double calculateExponential(double x)
    }
    class App {
      +void main(String[] args)
    }

    App --> ExponentialSeries : uses

Flowchart of the proposed solution.

flowchart TD
    A([Start]) --> B[Initialize sum = 1.0, 
    term = 1.0, 
    numerator = 1.0, 
    nFactorial = 1.0, 
    previousSum = 0.0, 
    n = 1]
    B --> C{"Check (sum - previousSum) > 0.0001"}
    C -->|No| J[Return sum]
    C -->|Yes| D[previousSum = sum]
    D --> E[numerator *= x]
    E --> F["nFactorial = factorial(n)"]
    F --> G[term = numerator / nFactorial]
    G --> H[sum += term]
    H --> I[n++]
    I ---> C
    J --> K([End])

File: ExponentialSeries.java

public class ExponentialSeries {
    private static double factorial(int n) {
        if (n == 0 || n == 1) {
            return 1;
        }
        double result = 1;
        for (int i = 2; i <= n; i++) {
            result *= i;
        }
        return result;
    }

    public static double calculateExponential(double x) {
        double sum = 1.0;
        double term = 1.0;
        double numerator = 1.0;
        double nFactorial = 1.0;
        double previousSum = 0.0;
        int n = 1;

        do {
            previousSum = sum;
            numerator = numerator * x;
            nFactorial = factorial(n); 
            term = numerator / nFactorial;
            sum += term;
            n++;
        } while ((sum - previousSum) > 0.0001);

        return sum;
    }
}

File: App.java

public class App {
    public static void main(String[] args) {
        java.util.Scanner scanner = new java.util.Scanner(System.in);
        System.out.print("Enter the value of x: ");
        double x = scanner.nextDouble();
        double result = ExponentialSeries.calculateExponential(x);
        System.out.println("e^" + x + " = " + result);
    }
}

Explanation:

  • The factorial method is a private static method that calculates the factorial of a number n. It handles the special cases where n is 0 or 1, returning 1 for these cases, and computes the factorial for other values of n.
  • The calculateExponential method calculates e^x using the series expansion formula. The method initializes sum to 1.0, term to 1.0, numerator to 1.0, nFactorial to 1.0, and previousSum to 0.0.
  • The method uses a do-while loop to calculate and add successive terms to sum. The loop runs until the absolute difference between sum and previousSum is greater than 0.0001.
  • Inside the loop, previousSum is updated to the current sum. The numerator is multiplied by x to get the next power of x, and nFactorial is calculated using the factorial method. The term is then calculated as numerator / nFactorial and added to sum. The loop counter n is incremented after each iteration.
  • Once the loop condition is no longer satisfied, the method returns the final value of sum, which is the approximate value of e^x.
Enter the value of x: 1
e^1.0 = 2.71827876984127

Enter the value of x: 0
e^0.0 = 1.0

Enter the value of x: -1
e^-1.0 = 0.3678819444444445

Enter the value of x: 2
e^2.0 = 7.389046015712681

Variable Changes for ExponentialSeries with x = 1 over 9 Iterations

Value of n numerator nFactorial term previousSum sum difference
1 1 1 1.0 0.0 2.0 1.0
2 1 2 0.5 2.0 2.5 0.5
3 1 6 0.16666666666666666 2.5 2.6666666666666665 0.16666666666666666
4 1 24 0.041666666666666664 2.6666666666666665 2.708333333333333 0.041666666666666664
5 1 120 0.008333333333333333 2.708333333333333 2.7166666666666663 0.008333333333333333
6 1 720 0.001388888888888889 2.7166666666666663 2.7180555555555554 0.001388888888888889
7 1 5040 0.0001984126984126984 2.7180555555555554 2.7182539682539684 0.0001984126984126984
8 1 40320 0.0000248015873015873 2.7182539682539684 2.71827876984127 0.0000248015873015873
9 1 362880 0.000002755731922398589 2.71827876984127 2.7182815255731922 0.000002755731922398589

After 9 iterations, the sum converges to approximately 2.7182815255731922, demonstrating the exponential function's calculation e^x for x = 1. The difference between successive sums decreases with each iteration, illustrating the convergence of the series.

Here is an alternate implementation. You can see that there are simpler ways to arrive at a solution. The question required that a method factorial is used and hence we used the same.

Please compare the values computed on your calculator. You will find the accuracy is till a certain digit.  Why is it so?

public class ExponentialSeries {

    public static double calculateExponential(double x) {
        double sum = 1.0;
        double term = 1.0;
        double previousSum;
        int n = 1;

        do {
            previousSum = sum;
            term *= x / n;
            sum += term;
            n++;
        } while (Math.abs(sum - previousSum) > 0.0001);

        return sum;
    }
}

Program 2: Sine Function Series Expansion

Write a Java program to calculate the series expansion of the sine function sin x. The sine function can be expressed as a series:

\[\sin x = x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \ldots\]

The program should prompt the user to enter the value of x (in radians) and terminate the calculation when the absolute difference between two successive terms is less than 0.0001. Implement a private method to calculate the factorial of a number.

  • Class Name: SineSeries
  • Methods:
    • calculateSine(double x)
    • private static double factorial(int n)
  • Main Class Name: App

Solution for Program 2: Sine Function Series Expansion

We will represent the class that is expected to be created diagrammatically. The - indicates private methods and the + indicates public methods.

classDiagram
    class SineSeries {
      -double factorial(int n)
      +double calculateSine(double x)
    }
    class App {
      +void main(String[] args)
    }

    App --> SineSeries : uses

Flowchart of the proposed solution.

flowchart TD
    A([Start]) --> B[Initialize sum = x, 
    term = 1.0, 
    numerator = x, 
    nFactorial = 1.0, 
    previousSum = 0.0, 
    difference = 0.0, 
    n = 1, 
    sign = 1]
    B --> C{Check difference > 0.0001}
    C -- No --> J[Return sum]
    C -- Yes --> D[previousSum = sum]
    D --> E[numerator *= x * x]
    E --> F[sign *= -1]
    F --> G[n += 2]
    G --> H["nFactorial = factorial(n)"]
    H --> I[term = sign * numerator / nFactorial]
    I --> K[sum += term]
    K --> L["difference = abs(sum - previousSum)"]
    L --> C
    J --> M([End])

File: SineSeries.java

public class SineSeries {

    private static double factorial(int n) {
        if (n == 0 || n == 1) {
            return 1;
        }
        double result = 1;
        for (int i = 2; i <= n; i++) {
            result *= i;
        }
        return result;
    }

    public static double calculateSine(double x) {
        double sum = x;
        double term = 1.0;
        double numerator = x;
        double nFactorial = 1.0;
        double previousSum = 0.0;
        double difference = 0.0;
        int n = 1;
        int sign = 1;

        do {
            previousSum = sum;
            numerator = numerator * x * x;
            n += 2;
            sign *= -1;
            nFactorial = factorial(n); 
            term = sign * numerator / nFactorial;
            sum += term;
            difference = sum - previousSum;
            if (difference < 0.0){
                difference = -1.0 * difference;
            }
        } while (difference > 0.0001);
        return sum;
    }
}

File: App.java

public class App {
    public static void main(String[] args) {
        java.util.Scanner scanner = new java.util.Scanner(System.in);
        System.out.print("Enter the value of x (in radians): ");
        double x = scanner.nextDouble();
        double result = SineSeries.calculateSine(x);
        System.out.println("sin(" + x + ") = " + result);
    }
}

Explanation for the SineSeries Class

The SineSeries class is designed to compute the sine of a given angle x (in radians) using the series expansion method. The class is comprised of two primary components:

Private Static Method: factorial(int n)

  • Purpose: Calculates the factorial of a given integer n.
  • Implementation: Employs a loop to multiply integers from 2 to n. Handles special cases for n = 0 and n = 1 by returning 1, as the factorial of both is 1.
  • Parameters: n - The integer for which the factorial is to be calculated.
  • Return Value: The factorial of n as a double.

Public Static Method: calculateSine(double x)

  • Purpose: Calculates the sine of a given angle x using its series expansion.
  • Implementation: Initiates with sum = x, iterating through the series expansion formula. It alternates the sign for each term, updates the numerator and factorial part, and continues until the difference between successive sums is minimal, indicating convergence.
  • Parameters: x - The angle in radians for which the sine is to be calculated.
  • Return Value: The approximate sine of x as a double.

Usage in the App Class

The App class's main method prompts the user for an angle in radians, calculates its sine using SineSeries.calculateSine(double x), and displays the result. This demonstrates the practical application of mathematical series in programming for solving trigonometric problems.

Note on Radians

The use of radians in trigonometric functions like sine is due to their natural appearance in mathematical analysis and calculus. Radians directly relate an angle's measure to the length of the arc it subtends on a unit circle, providing a simple and intuitive way to work with circular and oscillatory motion. This is why angles must be specified in radians when using the SineSeries class for accurate calculations consistent with mathematical conventions.

Console Output Display:

Run 1 - case 0°

Enter the value of x (in radians): 0.0
sin(0.0) = 0.0
 

Run 2 - case 45°

Enter the value of x (in radians): 0.7853981634
sin(0.7853981634) = 0.7071064695769824
 

Run 3 - case 90°

Enter the value of x (in radians): 1.570796327
sin(1.570796327) = 0.9999999437410506
 

Run 4 - case 180°

Enter the value of x (in radians): 3.141592654
sin(3.141592654) = -7.73196098675832E-7
 

Run 5 - case 270°

Enter the value of x (in radians): 4.71238898
sin(4.71238898) = -1.0000025759875684
 

Run 6 - case 360°

Enter the value of x (in radians): 6.283185307
sin(6.283185307) = -5.4945633662504585E-6
 

The above are values for sin 0°, sin 45°, sin 90°, sin 270°, sin 360°.

Variable Changes for SineSeries with x = 1.570796327 (90 degrees) over 5 Iterations

We will now see how the values of sine 90° will change. The first value is π/2 = 1.570796327. The subsequent values are now tabulated.

Value of n numerator nFactorial term previousSum sum difference sign
3 3.875785 6.000000 -0.645964 1.570796 0.924832 0.645964 -1
5 9.563115 120.000000 0.079693 0.924832 1.004525 0.079693 1
7 23.596041 5040.000000 -0.004682 1.004525 0.999843 0.004682 -1
9 58.220897 362880.000000 0.000160 0.999843 1.000004 0.000160 1
11 143.654306 39916800.000000 -0.000004 1.000004 1.000000 0.000004 -1

After 5 iterations, the sum converges to approximately 1.570796327 for x = 1.570796327 (90 degrees), demonstrating the sine function's calculation sin x using the series expansion method. The difference between successive sums decreases with each iteration, illustrating the convergence of the series. It may be noted that the table values and the console output do not match. This is because the table was created with string formatting functions, which round the values before displaying, while the console output gives the value as computed internally by the Java compiler.

It will be instructive to reproduce the code that was used to generate the console output and the table. Both are outputs of the program and reduced my effort in preparing those tables. They serve as a revision of many of the methods that we have used earlier. Although a detailed explanation of the exceptions have not been discussed, do familiarise yourself with different constructs. In the updated SineSeries class, files are being read/written, and it is a good habit to ensure that minimum error handling is done to ensure the program terminates gracefully.

Updated SineSeries Class to generate outputs

The updated SineSeries throws an exception, and the App class needs to have a method to handle the same. Both the modified codes are reproduced below. To make the file handling simpler, another class has been created. This class essentially uses the PrintWriter class.

Ideally, these codes should be run in an IDE like Visual Studio Code. Many of the online compilers are not handling files that are written well.

File: SineSeries.java

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class SineSeries {

    private static double factorial(int n) {
        if (n == 0 || n == 1) {
            return 1;
        }
        double result = 1.0;
        for (int i = 2; i <= n; i++) {
            result *= i;
        }
        return result;
    }

    public static double calculateSine(double x) throws IOException {
        String filePath = "SineSeriesTable.html";
        File file = new File(filePath);
        if (file.exists()) {
            file.delete(); // Delete the file if it exists
        }
        double sum = x;
        double term = 1.0;
        double numerator = x;
        double nFactorial = 1.0;
        double previousSum = 0.0;
        double difference = 0.0;
        int n = 1;
        int sign = 1;
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
            writer.write("<table border='1'><tr><th>Iteration (n)</th><th>numerator</th><th>nFactorial</th>" +
                         "<th>term</th><th>previousSum</th><th>sum</th><th>difference</th><th>sign</th></tr>" + '\n');
            do {
            previousSum = sum;
            numerator = numerator * x * x;
            n += 2;
            sign *= -1;
            nFactorial = factorial(n); 
            term = sign * numerator / nFactorial;
            sum += term;
            difference = sum - previousSum;
            if (difference < 0.0){
                difference = -1.0 * difference;
            }
                writer.write(String.format("<tr><td>%d</td><td>%f</td><td>%f</td><td>%f</td><td>%f</td><td>%f</td><td>%f</td><td>%d</td></tr>\n",
                                           n, numerator, nFactorial, term, previousSum, sum, difference, sign));

            } while (difference > 0.0001);

            writer.write("</table>");
        }

        return sum;
    }

    public static void main(String[] args) {
        try {
            double x = Math.PI / 2; // Example usage
            calculateSine(x);
            System.out.println("Sine series table has been written to SineSeriesTable.html");
        } catch (IOException e) {
            System.err.println("Error writing to the file: " + e.getMessage());
        }
    }
}

File: FileOutputManager.java

import java.io.*;

public class FileOutputManager {
    private static final String FILE_NAME = "consoleOutput.txt";
    private static int runCount = 0;
    
    public static void initialize() throws IOException {
        boolean fileExists = new File(FILE_NAME).exists();
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(FILE_NAME, true)));
        if (!fileExists) {
            // File is created for the first time; no need for a run comment
            out.println("<div class=\"consoleOutput\">");
        } else {
            runCount++;
            out.println("<!-- Run " + runCount + " --><span style=\"color: grey;\">Run " + runCount + "</span>");
        }
        out.close();
    }
    
    public static void print(String message) throws IOException {
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(FILE_NAME, true)));
        out.print(message);
        out.close(); // Close the writer to flush the contents
    }
    
    public static void println(String message) throws IOException {
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(FILE_NAME, true)));
        out.println(message);
        out.close(); // Close the writer to flush the contents
    }
}

File: App.java

import java.util.Scanner;

public class App {
    public static void main(String[] args) {
        try {
            FileOutputManager.initialize(); // Initialize the file output manager
            
            System.out.print("Enter the value of x (in radians): ");
            Scanner scanner = new Scanner(System.in);
            double x = scanner.nextDouble();
            FileOutputManager.print("<p>Enter the value of x (in radians): " + x); // Replace System.out with FileOutputManager.print
            double result = SineSeries.calculateSine(x);
            System.out.println("sin(" + x + ") = " + result);
            FileOutputManager.println("<br />sin(" + x + ") = " + result + "<br /><br /></p>"); // Replace System.out.println with FileOutputManager.println
            scanner.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Understanding the FileOutputManager Class

The FileOutputManager class is a Java utility designed to facilitate writing output to a file. It encapsulates file operations, providing an easy-to-use interface for persistently capturing output. This class is an example of encapsulation and reusability in software development.

Core Components and Functionalities

  • FILE_NAME: A static final variable holding the output file's name, ensuring consistent file operations across the application.
  • runCount: Tracks the number of application executions to differentiate output across runs.
  • initialize(): Prepares the file for writing, creating a new file or appending a run comment for existing files.
  • print(String message) and println(String message): Methods for writing strings to the file, mimicking System.out.print and System.out.println but directing output to a file.

Benefits of Using FileOutputManager

  • Encapsulation: Simplifies file handling by hiding the complexity from the application's core logic.
  • Reusability: Can be used across various projects or within different parts of the same project without duplicating code.
  • Maintainability: Centralizes file output logic, making it easier to update and maintain.

Example Scenario

In the current usage, FileOutputManager is employed within an application that calculates the sine of an angle using its series expansion and writes the results to an HTML file. This demonstrates the class's utility in capturing mathematical computation outputs, making it a valuable tool for educational purposes, where students can visually inspect the iterative process of series expansion calculations.

The FileOutputManager class exemplifies the principle of code reusability and encapsulation in software development. By providing a centralised mechanism for file output operations, it enhances code maintainability and reduces redundancy across projects.

Key Features

Includes methods for initialising output files, printing messages, appending new lines, and keeping track of execution runs for clear differentiation of output data.

Reusability in Different Programs

Its design allows for easy integration into any Java program requiring file output, streamlining file writing operations and promoting code reuse.

Typical Usage

Useful in batch processing applications and any scenario requiring structured logging or output to files.

Program 3: Cosine Function Series Expansion

Write a Java program to calculate the series expansion of the cosine function cos x. The cosine function can be expressed as a series:

\[\cos x = 1 - \frac{x^2}{2!} + \frac{x^4}{4!} - \frac{x^6}{6!} + \ldots\]

The program should prompt the user to enter the value of x (in radians) and terminate the calculation when the absolute difference between two successive terms is less than 0.0001. Implement a private method to calculate the factorial of a number.

  • Class Name: CosineSeries
  • Methods:
    • calculateCosine(double x)
    • private static double factorial(int n)
  • Main Class Name: App

We will represent the class that is expected to be created diagrammatically. The - indicates private methods and the + indicates public methods.

classDiagram
    class CosineSeries {
      -double factorial(int n)
      +double calculateCosine(double x)
    }
    class App {
      +void main(String[] args)
    }

    App --> CosineSeries : uses

Flowchart of the proposed solution.

flowchart TD
    A([Start]) --> B[Initialize sum = 1.0, 
    term = 1.0, 
    numerator = 1.0, 
    nFactorial = 1.0, 
    previousSum = 0.0, 
    difference = 0.0, 
    n = 0, 
    sign = 1]
    B --> C{Check difference > 0.0001}
    C -- No --> J[Return sum]
    C -- Yes --> D[previousSum = sum]
    D --> E[numerator *= x * x]
    E --> F[sign *= -1]
    F --> G[n += 2]
    G --> H["nFactorial = factorial(n)"]
    H --> I[term = sign * numerator / nFactorial]
    I --> K[sum += term]
    K --> L["difference = abs(sum - previousSum)"]
    L --> C
    J --> M([End])

Solution for Program 3: Cosine Function Series Expansion

File: CosineSeries.java

public class CosineSeries {
    private static double factorial(int n) {
        if (n == 0 || n == 1) {
            return 1;
        }
        double result = 1;
        for (int i = 2; i <= n; i++) {
            result *= i;
        }
        return result;
    }

    public static double calculateCosine(double x) {
        double sum = 1.0; // The start value of the series is 1, hence we use 1. Compare it with the Sine series solution.
        double term = 1.0;
        double numerator = 1.0; // We start the second term on x^2 here. Compare it with the Sine series solution.
        double nFactorial = 1.0;
        double previousSum = 0.0;
        double difference = 0.0;
        int n = 0; // The series for factorial is even, hence we start with 0. Compare it with the Sine series solution.
        int sign = 1;

        do {
            previousSum = sum;
            numerator = numerator * x * x;
            n += 2;
            sign *= -1;
            nFactorial = factorial(n); 
            term = sign * numerator / nFactorial;
            sum += term;
            difference = sum - previousSum;
            if (difference < 0.0){
                difference = -1.0 * difference;
            }
        } while (difference > 0.0001);
        return sum;
    }
}

File: App.java

public class App {
    public static void main(String[] args) {
        java.util.Scanner scanner = new java.util.Scanner(System.in);
        System.out.print("Enter the value of x (in radians): ");
        double x = scanner.nextDouble();
        double result = CosineSeries.calculateCosine(x);
        System.out.println("cos(" + x + ") = " + result);
    }
}

Console Output Display:

Run 1 - case 0°

Enter the value of x (in radians): 0.0
cos(0.0) = 1.0
 

Run 2 - case 45°

Enter the value of x (in radians): 0.7853981634
cos(0.7853981634) = 0.70710680568149
 

Run 3 - case 90°

Enter the value of x (in radians): 1.570796327
cos(1.570796327) = -4.6497111258537687E-7
 

Run 4 - case 180°

Enter the value of x (in radians): 3.141592654
cos(3.141592654) = -0.9999998647395547
 

Run 5 - case 270°

Enter the value of x (in radians): 4.71238898
cos(4.71238898) = 5.533598320419583E-7
 

Run 6 - case 360°

Enter the value of x (in radians): 6.283185307
cos(6.283185307) = 1.0000013329315738
 

Finding the values and variation of the variables in each iteration is left to the student as an exercise. Such an exercise will reinforce the concepts of how the internal values of variables will change in each iteration and help understand the way computers work, thereby making programming second nature.

Understanding the Differences: Sine and Cosine Series Calculations

When we explore mathematical concepts like Sine and Cosine series, we unravel the patterns that help us approximate complex calculations using simpler steps. These series are crucial in various scientific and engineering applications, offering insights into natural phenomena, waves, and circular motions. Below, we present a comparison of how these series are implemented in code, aiming to bridge understanding. Unravelling and identifying patterns is a key element to writing good programs.

Breaking Down the Code: SineSeries and CosineSeries Explained
Line Number SineSeries CosineSeries Explanation
13 double sum = x; double sum = 1.0; In the Sine series, we start adding from the angle itself (x). In the Cosine series, we start from 1 because that's how their mathematical formulas begin.
15 double numerator = x; double numerator = 1.0; For the Sine series, we use the angle (x) to start multiplying for the series. For the Cosine series, we begin with 1, reflecting the series' starting point based on their formulas.
18 int n = 1; int n = 0; This shows the position we start from in each series. Sine series starts at an odd position (1, 3, 5...), and Cosine at an even position (0, 2, 4...), following their mathematical patterns.

The comparison above shows how subtle differences in starting points and patterns can lead to distinct series calculations for Sine and Cosine. By understanding these foundational steps, we gain insight into the elegance and precision of mathematical series, even without delving into programming specifics.

When we talk about "series" in mathematics (like the Sine and Cosine series), we're referring to a way to approximate complex calculations using simpler, repeated additions or subtractions. The Sine and Cosine functions are fundamental in understanding circles, waves, and many natural phenomena, but they're not straightforward to calculate directly for non-trivial angles.

Starting Point (sum = x vs. sum = 1.0):

Imagine you're building two towers using blocks. For the Sine tower, you start with a specific number of blocks (x), which represents the angle in radians you're interested in. For the Cosine tower, you always start with just 1 block, regardless of the angle.

First Step (numerator = x vs. numerator = 1.0):

Think of the numerator as the current number of blocks you're adding or removing. For the Sine tower, the first move is determined directly by the angle (x). For the Cosine tower, your first move doesn't depend on the angle; you always start the same way without adding or removing any blocks.

Pattern of Steps (n = 1 vs. n = 0):

This indicates the rhythm or pattern you follow when adding or removing blocks. For the Sine tower, you skip the first step and start from the second, following an odd pattern. For the Cosine tower, you begin right away and follow an even pattern.

These differences align with the mathematical properties of the Sine and Cosine functions, allowing us to approximate their values through a series of steps, adding (or removing) blocks according to a specific pattern that represents the mathematical series for each function.

The concept of a series termination condition is crucial in determining when to stop adding or removing blocks in our towers. In the case of the Sine and Cosine series, we stop when the difference between successive steps becomes very small (less than 0.0001), indicating that further additions or subtractions won't significantly change the outcome. This is akin to reaching a point where adding or removing any more blocks doesn't noticeably increase the height or stability of our tower. For non-converging series, where the sum doesn't settle into a stable value, special handling is required to prevent endless building. In practical terms, setting a limit on the number of steps or blocks ensures that we can conclude our construction process even if perfect stability isn't achieved, preventing the task from becoming an infinite endeavour.

Program 4: Natural Logarithm (ln(1+x)) Series Expansion

Write a Java program to calculate the series expansion of the natural logarithm ln(1+x). The natural logarithm can be expressed as a series:

\[\ln(1+x) = x - \frac{x^2}{2} + \frac{x^3}{3} - \frac{x^4}{4} + \ldots\]

The program should prompt the user to enter the value of x (-1 < x ≤ 1) and terminate the calculation when the absolute difference between two successive terms is less than 0.0001.

  • Class Name: NaturalLogSeries
  • Method Name: calculateNaturalLog(double x)
  • Main Class Name: App

NaturalLogSeries Class Explanation

The NaturalLogSeries class is designed to calculate the natural logarithm (\(\ln(1+x)\)) of a number using its series expansion. This method is invaluable in scenarios where an approximation of the natural logarithm is necessary, especially in the absence of built-in mathematical libraries.

Key Components

  • factorial(int n): A private method for computing the factorial of a number n. Factorials are essential in the series expansion formula for calculating natural logarithms.
  • calculateNaturalLog(double x): A public method that approximates the natural logarithm of 1+x. It calculates the terms of the series and adds them until the series converges to an accurate value, determined by a small difference between successive sums.

We will represent the class that is expected to be created diagrammatically. The - indicates private methods and the + indicates public methods.

Here are the class diagram and the flowchart for the NaturalLogSeries calculation in the mermaid format:

classDiagram
    class NaturalLogSeries {
      -double factorial(int n)
      +double calculateNaturalLog(double x)
    }
    class App {
      +void main(String[] args)
    }

    App --> NaturalLogSeries : uses

This diagram represents the relationship between the App class, which serves as the entry point for user interaction, and the NaturalLogSeries class, responsible for calculating the natural logarithm series expansion. The NaturalLogSeries class contains a private method factorial(int n) for calculating factorials needed in the series computation and a public method calculateNaturalLog(double x) for computing the natural logarithm of 1+x.

Flowchart of the proposed solution.

flowchart TD
    A([Start]) --> B[Initialize sum = 0.0, term = x, numerator = x, nFactorial = 1.0, previousSum = 0.0, difference = 0.0, n = 1, sign = 1]
    B --> C{Check if difference > 0.0001}
    C -- No --> J[Return sum]
    C -- Yes --> D[previousSum = sum]
    D --> E[Calculate next term: term = term * x / n]
    E --> F[Update n: n++]
    F --> G[Adjust term sign every other term]
    G --> H[Calculate difference: difference = sum - previousSum]
    H --> I[Make difference positive if negative]
    I --> K[Add term to sum: sum += term]
    K --> C
    J --> L([End])

This flowchart details the logical steps taken by the NaturalLogSeries.calculateNaturalLog(double x) method to calculate the natural logarithm of 1+x using its series expansion. Starting with initial values, it enters a loop where it computes each term of the series based on the current value of x and n, adjusting the sign as necessary. The loop continues until the change (difference) between successive sums of terms is small enough, indicating that further terms will not significantly affect the result, achieving an accurate approximation of the natural logarithm.

Solution for Program 3: Natural Log Series Expansion

File: NaturalLogSeries.java

public class NaturalLogSeries {

    public static double calculateNaturalLog(double x) {
        if (x <= -1 || x > 1) {
            throw new IllegalArgumentException("x must be in the range -1 < x <= 1");
        }
        
        double sum = 0.0; // Initialize sum of the series
        double term = x; // First term is x itself
        double previousSum = 0.0;
        double difference = 0.0;
        int n = 1;

        do {
            previousSum = sum; // Store the sum from the previous iteration
            sum += term; // Add the current term to the sum
            n++; // Increment n for the next term
            term = term * x / n; // Calculate the next term
            
            if (n % 2 == 0) { // Adjust the sign for even terms
                term = -term;
            }
            
            difference = sum - previousSum; // Calculate the difference
            if (difference < 0) { // Make the difference positive if it's negative
                difference = -difference;
            }
        } while (difference > 0.0001); // Continue until the difference is small enough
        
        return sum; // Return the sum, which is the approximation of ln(1+x)
    }
}

File: App.java

import java.util.Scanner;

public class App {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter the value of x (-1 < x <= 1): ");
        double x = scanner.nextDouble();

        if (x <= -1 || x > 1) {
            System.out.println("The value of x must be in the range -1 < x <= 1.");
        } else {
            double result = NaturalLogSeries.calculateNaturalLog(x);
            System.out.println("Natural log of (1+" + x + ") is approximately: " + result);
        }
    }
}

Console Output Display:

Run 1 - Input Value 0.5

Enter the value of x (in radians): 0.5
ln(1+0.5) = 0.3570095486111111
 

Run 2 - Input Value -0.5

Enter the value of x (in radians): -0.5
ln(1+-0.5) = -0.6018446180555556
 

Run 3 - Input Value 1

Enter the value of x (in radians): 1
ln(1+1) = 0.38177083333333345
 

Run 4 - Input Value -0.9

Enter the value of x (in radians): -0.9
ln(1+-0.9) = Calculated value here
 

One key takeaway from this program is the method used to alternate the sign of terms in the series expansion for calculating the natural logarithm (\(\ln(1+x)\)). This technique is succinctly illustrated by the line of code:

if (n % 2 == 0) { // Adjust the sign for even terms
    term = -term;
}

The n % 2 == 0 condition checks if the term's position n is even by using the modulus operator (%), which returns the remainder of the division of n by 2. If the remainder is 0, the term is in an even position, prompting a sign change by negating the term's current value (term = -term). This simple yet effective strategy is crucial in series where the sign alternates between successive terms, such as in the calculation of logarithmic or trigonometric functions.

Understanding this approach not only deepens one's grasp of mathematical series but also enhances problem-solving skills in programming. By learning to manipulate the sign based on a term's position, learners can apply similar logic to various contexts where alternating patterns are needed, whether in mathematical calculations or in designing algorithms that require periodic behaviour changes.

Program 5: Arctan Function (arctan x) Series Expansion

Write a Java program to calculate the series expansion of the arctan function arctan x. The arctan function can be expressed as a series:

\[\arctan x = x - \frac{x^3}{3} + \frac{x^5}{5} - \frac{x^7}{7} + \ldots\]

The program should prompt the user to enter the value of x (-1 ≤ x ≤ 1) and terminate the calculation when the absolute difference between two successive terms is less than 0.0001. Implement a private method to calculate the factorial of a number.

  • Class Name: ArctanSeries
  • Methods:
    • calculateArctan(double x)
    • private static double factorial(int n)
  • Main Class Name: App

We will represent the class that is expected to be created diagrammatically. The - indicates private methods and the + indicates public methods.

classDiagram
    class ArctanSeries {
      -double factorial(int n)
      +double calculateArctan(double x)
    }
    class App {
      +void main(String[] args)
    }

    App --> ArctanSeries : uses

Flowchart of the proposed solution.

flowchart TD
    A([Start]) --> B[Initialize sum = x, 
    term = x, 
    n = 1, 
    sign = -1]
    B --> C{Check difference > 0.0001}
    C -- No --> J[Return sum]
    C -- Yes --> D["Calculate next term: term = term * x * x / (n + 2)"]
    D --> E[Update n: n += 2]
    E --> F[Update sign: sign *= -1]
    F --> G[Apply sign to term]
    G --> H[Add term to sum: sum += term]
    H --> I[Calculate difference]
    I --> C
    J --> K([End])

Solution for Program 5: ArcTan Function Series Expansion

File: ArctanSeries.java

public class ArctanSeries {

    public static double calculateArctan(double x) {
        if (x < -1 || x > 1) {
            throw new IllegalArgumentException("Value of x must be in the range -1 <= x <= 1.");
        }

        double sum = x; // Initialize sum with the first term of the series, which is x
        double term = x; // The first term of the series
        double difference;
        int n = 1;

        do {
            term *= x * x; // Square x for the next term's numerator
            term = -term / (n + 2); // Alternate the sign and adjust the denominator
            sum += term; // Add the new term to the sum
            difference = term > 0 ? term : -term; // The conditional operator ?: is used here
            n += 2; // Increment n by 2 for the next term
        } while (difference >= 0.0001);

        return sum;
    }
}


File: App.java

import java.util.Scanner;

public class App {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter the value of x (-1 <= x <= 1): ");
        double x = scanner.nextDouble();

        try {
            double result = ArctanSeries.calculateArctan(x);
            System.out.printf("arctan(%f) = %f\n", x, result);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }
    }
}

Understanding the ArctanSeries Class

The ArctanSeries class is specifically designed to calculate the arctan of a given number x using the arctan function's series expansion. This is particularly useful in scenarios where a precise calculation of arctan is required without relying on Java's built-in Math class.

Key Components of ArctanSeries

  • calculateArctan(double x): This public static method performs the series expansion calculation. It takes a double value x as input, which must be within the range -1 ≤ x ≤ 1. Through iterative calculations, it approximates the arctan of x by summing up the series' terms until the absolute difference between two successive terms is less than 0.0001.

The calculation starts with x itself and alternates the sign of each subsequent term while dividing by increasingly odd numbers, following the series' pattern: \[\arctan x = x - \frac{x^3}{3} + \frac{x^5}{5} - \frac{x^7}{7} + \ldots\]

This approach, especially the manual alternation of signs and avoidance of the Math class for operations like absolute value calculation, showcases an efficient method of implementing mathematical series in programming. It highlights how control structures and arithmetic operations can be combined to solve complex mathematical problems.

Console Output Display:

Run 1 - Input Value 0.5

Enter the value of x (-1 ≤ x ≤ 1): 0.5
arctan(0.5) = 0.460342
 

Run 2 - Input Value -0.5

Enter the value of x (-1 ≤ x ≤ 1): -0.5
arctan(-0.5) = -0.460342
 

Run 3 - Input Value 1

Enter the value of x (-1 ≤ x ≤ 1): 1
arctan(1) = 0.724772
 

Run 4 - Input Value -0.9

Enter the value of x (-1 ≤ x ≤ 1): -0.9
arctan(-0.9) = -0.692191