Riemann Zeta Function in Java - Infinite Recursion with Functional Form
Asked Answered
R

2

7

Note: Updated on 06/17/2015. Of course this is possible. See the solution below.

Even if anyone copies and pastes this code, you still have a lot of cleanup to do. Also note that you will have problems inside the critical strip from Re(s) = 0 to Re(s) = 1 :). But this is a good start.

import java.util.Scanner;

public class NewTest{

public static void main(String[] args) {
    RiemannZetaMain func = new RiemannZetaMain();
    double s = 0;
    double start, stop, totalTime;
    Scanner scan = new Scanner(System.in);
    System.out.print("Enter the value of s inside the Riemann Zeta Function: ");
    try {
            s = scan.nextDouble();
    } 
    catch (Exception e) {
        System.out.println("You must enter a positive integer greater than 1.");
    }
    start = System.currentTimeMillis();
    if (s <= 0)
        System.out.println("Value for the Zeta Function = " + riemannFuncForm(s));
    else if (s == 1)
        System.out.println("The zeta funxtion is undefined for Re(s) = 1.");
    else if(s >= 2)
        System.out.println("Value for the Zeta Function = " + getStandardSum(s));
    else
        System.out.println("Value for the Zeta Function = " + getNewSum(s));
    stop = System.currentTimeMillis();
    totalTime = (double) (stop-start) / 1000.0;
    System.out.println("Total time taken is " + totalTime + " seconds.");
}

// Standard form the the Zeta function.
public static double standardZeta(double s) {
    int n = 1;
    double currentSum = 0;
    double relativeError = 1;
    double error = 0.000001;
    double remainder;

    while (relativeError > error) {
        currentSum = Math.pow(n, -s) + currentSum;
        remainder = 1 / ((s-1)* Math.pow(n, (s-1)));
        relativeError =  remainder / currentSum;
        n++;
    }
    System.out.println("The number of terms summed was " + n + ".");
    return currentSum;
}

public static double getStandardSum(double s){
    return standardZeta(s);
}

//New Form
// zeta(s) = 2^(-1+2 s)/((-2+2^s) Gamma(1+s)) integral_0^infinity t^s sech^2(t) dt  for Re(s)>-1
public static double Integrate(double start, double end) {
    double currentIntegralValue = 0;
    double dx = 0.0001d; // The size of delta x in the approximation
    double x = start; // A = starting point of integration, B = ending point of integration.

    // Ending conditions for the while loop
    // Condition #1: The value of b - x(i) is less than delta(x).
    // This would throw an out of bounds exception.
    // Condition #2: The value of b - x(i) is greater than 0 (Since you start at A and split the integral 
    // up into "infinitesimally small" chunks up until you reach delta(x)*n.
    while (Math.abs(end - x) >= dx && (end - x) > 0) {
        currentIntegralValue += function(x) * dx; // Use the (Riemann) rectangle sums at xi to compute width * height
        x += dx; // Add these sums together
    }
    return currentIntegralValue;
}

private static double function(double s) {
    double sech = 1 / Math.cosh(s); // Hyperbolic cosecant
    double squared = Math.pow(sech, 2);
    return ((Math.pow(s, 0.5)) * squared);
}

public static double getNewSum(double s){
double constant = Math.pow(2, (2*s)-1) / (((Math.pow(2, s)) -2)*(gamma(1+s)));
    return constant*Integrate(0, 1000);
}

// Gamma Function - Lanczos approximation
public static double gamma(double s){
                double[] p = {0.99999999999980993, 676.5203681218851, -1259.1392167224028,
                                  771.32342877765313, -176.61502916214059, 12.507343278686905,
                                  -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7};
                int g = 7;
                if(s < 0.5) return Math.PI / (Math.sin(Math.PI * s)*gamma(1-s));

                s -= 1;
                double a = p[0];
                double t = s+g+0.5;
                for(int i = 1; i < p.length; i++){
                        a += p[i]/(s+i);
                }

                return Math.sqrt(2*Math.PI)*Math.pow(t, s+0.5)*Math.exp(-t)*a;
        }

//Binomial Co-efficient - NOT CURRENTLY USING
/*
public static double binomial(int n, int k)
{
    if (k>n-k)
        k=n-k;

    long b=1;
    for (int i=1, m=n; i<=k; i++, m--)
        b=b*m/i;
    return b;
} */

// Riemann's Functional Equation
// Tried this initially and utterly failed.
public static double riemannFuncForm(double s) {
double term = Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s);
double nextTerm = Math.pow(2, (1-s))*Math.pow(Math.PI, (1-s)-1)*(Math.sin((Math.PI*(1-s))/2))*gamma(1-(1-s));
double error = Math.abs(term - nextTerm);

if(s == 1.0)
    return 0;
else 
    return Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s)*standardZeta(1-s);
}

}

Respect answered 12/6, 2015 at 4:36 Comment(1)
The recursion seems correct. Most likely the ending conditions are never met. So, basically s is never becoming 1 or 0. I would have been able to help further if I understood Riemann Zeta function better.Rosalia
R
-1

I think I need to use a different form of the zeta function. When I run the entire program ---

import java.util.Scanner;

public class Test4{

public static void main(String[] args) {
    RiemannZetaMain func = new RiemannZetaMain();
    double s = 0;
    double start, stop, totalTime;
    Scanner scan = new Scanner(System.in);
    System.out.print("Enter the value of s inside the Riemann Zeta Function: ");
    try {
            s = scan.nextDouble();
    } 
    catch (Exception e) {
        System.out.println("You must enter a positive integer greater than 1.");
    }
    start = System.currentTimeMillis();
    if(s >= 2)
        System.out.println("Value for the Zeta Function = " + getStandardSum(s));
    else
        System.out.println("Value for the Zeta Function = " + getRiemannSum(s));
    stop = System.currentTimeMillis();
    totalTime = (double) (stop-start) / 1000.0;
    System.out.println("Total time taken is " + totalTime + " seconds.");
}

// Standard form the the Zeta function.
public static double standardZeta(double s) {
    int n = 1;
    double currentSum = 0;
    double relativeError = 1;
    double error = 0.000001;
    double remainder;

    while (relativeError > error) {
        currentSum = Math.pow(n, -s) + currentSum;
        remainder = 1 / ((s-1)* Math.pow(n, (s-1)));
        relativeError =  remainder / currentSum;
        n++;
    }
    System.out.println("The number of terms summed was " + n + ".");
    return currentSum;
}

public static double getStandardSum(double s){
    return standardZeta(s);
}

// Riemann's Functional Equation
public static double riemannFuncForm(double s, double threshold, double currSteps, int maxSteps) {
double term = Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s);
//double nextTerm = Math.pow(2, (1-s))*Math.pow(Math.PI, (1-s)-1)*(Math.sin((Math.PI*(1-s))/2))*gamma(1-(1-s));
//double error = Math.abs(term - nextTerm);

if(s == 1.0)
    return 0;
else if (s == 0.0)
    return -0.5;
else if (term < threshold) {//The recursion will stop once the term is less than the threshold
    System.out.println("The number of steps is " + currSteps);
    return term;
}
else if (currSteps == maxSteps) {//The recursion will stop if you meet the max steps
    System.out.println("The series did not converge.");
    return term;
}    
else //Otherwise just keep recursing
    return term*riemannFuncForm(1-s, threshold, ++currSteps, maxSteps);
}

public static double getRiemannSum(double s) {
    double threshold = 0.00001;
    double currSteps = 1;
    int maxSteps = 1000;
    return riemannFuncForm(s, threshold, currSteps, maxSteps);
}

// Gamma Function - Lanczos approximation
public static double gamma(double s){
                double[] p = {0.99999999999980993, 676.5203681218851, -1259.1392167224028,
                                  771.32342877765313, -176.61502916214059, 12.507343278686905,
                                  -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7};
                int g = 7;
                if(s < 0.5) return Math.PI / (Math.sin(Math.PI * s)*gamma(1-s));

                s -= 1;
                double a = p[0];
                double t = s+g+0.5;
                for(int i = 1; i < p.length; i++){
                        a += p[i]/(s+i);
                }

                return Math.sqrt(2*Math.PI)*Math.pow(t, s+0.5)*Math.exp(-t)*a;
        }

//Binomial Co-efficient
public static double binomial(int n, int k)
{
    if (k>n-k)
        k=n-k;

    long b=1;
    for (int i=1, m=n; i<=k; i++, m--)
        b=b*m/i;
    return b;
}

}

I notice that plugging in zeta(-1) returns -

Enter the value of s inside the Riemann Zeta Function: -1
The number of steps is 1.0
Value for the Zeta Function = -0.0506605918211689
Total time taken is 0.0 seconds.

I knew that this value was -1/12. I checked some other values with wolfram alpha and observed that --

double term = Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s);

Returns the correct value. It is just that I am multiplying this value every time by zeta(1-s). In the case of Zeta(1/2), this will always multiply the result by 0.99999999.

Enter the value of s inside the Riemann Zeta Function: 0.5
The series did not converge.
Value for the Zeta Function = 0.999999999999889
Total time taken is 0.006 seconds.

I am going to see if I can replace the part for --

    else if (term < threshold) {//The recursion will stop once the term is less than the threshold
    System.out.println("The number of steps is " + currSteps);
    return term;
}

This difference is the error between two terms in the summation. I may not be thinking about this correctly, it is 1:16am right now. Let me see if I can think better tomorrow ....

Respect answered 13/6, 2015 at 5:18 Comment(8)
Hmmm yes. This seems to be another error from the fact that 1-(1-s) = s (1-s = t, 1-t = s). Thresholds based on the term are not particularly useful in this case as the term will always be either zeta(s) or zeta(t) (alternating). What I meant to do in my code was provide a threshold that represents the change in your total. I got confused thinking about sums, where a product (percentage) would've worked, but using products, I need to give you a threshold based off of differences (subtraction) between terms. I will edit my answer to fix this.Soracco
Ok, I have updated my code to pass a running total along and calculate absolute differences instead of a percentage. This absolute difference should actually decrease over time in a converging series. (E.g. A-B > B-C in converging series A,B,C,D,...etc. With the 1/2 case, {1/2-1/2*.9999} > {1/2*.9999-1/2*(.9999)^2} )Soracco
This is impossible. I am sorry for not seeing it before. The functional form of the Zeta Function is not an infinite sum, it is a direct relationship between Zeta(s) and Gamm(s)*Zeta(s). If doesn't matter how we write this in Java, it will never match zeta(s) = 2^s pi^(-1+s) Gamma(1-s) sin((pi s)/2) zeta(1-s). I tried a bunch of different stuff and noticed that it didn't work. I will have to use a different approximation, I am sorry for not seeing this before.Respect
Is it not an infinite product? Or does this product just not converge on a single value? (For example your zeta(1/2) converges on 0. This might not be the case for other values of s.) Or is it simply not recursive at all? (As Zeta(s) = k*Gamma(s)*Zeta(s) would suggest.)Soracco
The definition is similar to (recursion in programming). zeta(s) = 2^s pi^(-1+s) Gamma(1-s) sin((pi s)/2) zeta(1-s). Although, there is no way to write this in a computer program. The computer program doesn't know the value for zeta(1-s).Respect
Wow, I am an idiot. I am a complete moron. Of course the functional equation works. All I had to do was reference the infinite sum inside the functional equation. This is NOT difficult to write, it is actually extremely simple.Respect
So you calculate zeta(1-s) using the infinite sum....? Well I hope you can use my method after all then =D ! (At least for the infinite sum part.)Soracco
Yes, you have to use a different form of the zeta function for zeta(1-s). You can use the standard infinite sum. You just run into issues at the critical strip where Re(s) > 0 and Re(s) < 1. I have shown a method of dealing with that. It isn't completed yet.....Respect
S
3

Ok well we've figured out that for this particular function, since this form of it isn't actually a infinite series, we cannot approximate using recursion. However the infinite sum of the Riemann Zeta series (1\(n^s) where n = 1 to infinity) could be solved through this method.

Additionally this method could be used to find any infinite series' sum, product, or limit.

If you execute the code your currently have, you'll get infinite recursion as 1-(1-s) = s (e.g. 1-s = t, 1-t = s so you'll just switch back and forth between two values of s infinitely).

Below I talk about the sum of series. It appears you are calculating the product of the series instead. The concepts below should work for either.

Besides this, the Riemann Zeta Function is an infinite series. This means that it only has a limit, and will never reach a true sum (in finite time) and so you cannot get an exact answer through recursion.

However, if you introduce a "threshold" factor, you can get an approximation that is as good as you like. The sum will increase/decrease as each term is added. Once the sum stabilizes, you can quit out of recursion and return your approximate sum. "Stabilized" is defined using your threshold factor. Once the sum varies by an amount less than this threshold factor (which you have defined), your sum has stabilized.

A smaller threshold leads to a better approximation, but also longer computation time.

(Note: this method only works if your series converges, if it has a chance of not converging, you might also want to build in a maxSteps variable to cease execution if the series hasn't converged to your satisfaction after maxSteps steps of recursion.)

Here's an example implementation, note that you'll have to play with threshold and maxSteps to determine appropriate values:

/* Riemann's Functional Equation
 * threshold - if two terms differ by less than this absolute amount, return
 * currSteps/maxSteps - if currSteps becomes maxSteps, give up on convergence and return
 * currVal - the current product, used to determine threshold case (start at 1)
 */
public static double riemannFuncForm(double s, double threshold, int currSteps, int maxSteps, double currVal) {
    double nextVal = currVal*(Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s)); //currVal*term
    if( s == 1.0)
        return 0;
    else if ( s == 0.0)
        return -0.5;
    else if (Math.abs(currVal-nextVal) < threshold) //When a term will change the current answer by less than threshold
        return nextVal; //Could also do currVal here (shouldn't matter much as they differ by < threshold)
    else if (currSteps == maxSteps)//When you've taken the max allowed steps
        return nextVal; //You might want to print something here so you know you didn't converge
    else //Otherwise just keep recursing
        return riemannFuncForm(1-s, threshold, ++currSteps, maxSteps, nextVal);
    }
}
Soracco answered 12/6, 2015 at 5:15 Comment(0)
R
-1

I think I need to use a different form of the zeta function. When I run the entire program ---

import java.util.Scanner;

public class Test4{

public static void main(String[] args) {
    RiemannZetaMain func = new RiemannZetaMain();
    double s = 0;
    double start, stop, totalTime;
    Scanner scan = new Scanner(System.in);
    System.out.print("Enter the value of s inside the Riemann Zeta Function: ");
    try {
            s = scan.nextDouble();
    } 
    catch (Exception e) {
        System.out.println("You must enter a positive integer greater than 1.");
    }
    start = System.currentTimeMillis();
    if(s >= 2)
        System.out.println("Value for the Zeta Function = " + getStandardSum(s));
    else
        System.out.println("Value for the Zeta Function = " + getRiemannSum(s));
    stop = System.currentTimeMillis();
    totalTime = (double) (stop-start) / 1000.0;
    System.out.println("Total time taken is " + totalTime + " seconds.");
}

// Standard form the the Zeta function.
public static double standardZeta(double s) {
    int n = 1;
    double currentSum = 0;
    double relativeError = 1;
    double error = 0.000001;
    double remainder;

    while (relativeError > error) {
        currentSum = Math.pow(n, -s) + currentSum;
        remainder = 1 / ((s-1)* Math.pow(n, (s-1)));
        relativeError =  remainder / currentSum;
        n++;
    }
    System.out.println("The number of terms summed was " + n + ".");
    return currentSum;
}

public static double getStandardSum(double s){
    return standardZeta(s);
}

// Riemann's Functional Equation
public static double riemannFuncForm(double s, double threshold, double currSteps, int maxSteps) {
double term = Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s);
//double nextTerm = Math.pow(2, (1-s))*Math.pow(Math.PI, (1-s)-1)*(Math.sin((Math.PI*(1-s))/2))*gamma(1-(1-s));
//double error = Math.abs(term - nextTerm);

if(s == 1.0)
    return 0;
else if (s == 0.0)
    return -0.5;
else if (term < threshold) {//The recursion will stop once the term is less than the threshold
    System.out.println("The number of steps is " + currSteps);
    return term;
}
else if (currSteps == maxSteps) {//The recursion will stop if you meet the max steps
    System.out.println("The series did not converge.");
    return term;
}    
else //Otherwise just keep recursing
    return term*riemannFuncForm(1-s, threshold, ++currSteps, maxSteps);
}

public static double getRiemannSum(double s) {
    double threshold = 0.00001;
    double currSteps = 1;
    int maxSteps = 1000;
    return riemannFuncForm(s, threshold, currSteps, maxSteps);
}

// Gamma Function - Lanczos approximation
public static double gamma(double s){
                double[] p = {0.99999999999980993, 676.5203681218851, -1259.1392167224028,
                                  771.32342877765313, -176.61502916214059, 12.507343278686905,
                                  -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7};
                int g = 7;
                if(s < 0.5) return Math.PI / (Math.sin(Math.PI * s)*gamma(1-s));

                s -= 1;
                double a = p[0];
                double t = s+g+0.5;
                for(int i = 1; i < p.length; i++){
                        a += p[i]/(s+i);
                }

                return Math.sqrt(2*Math.PI)*Math.pow(t, s+0.5)*Math.exp(-t)*a;
        }

//Binomial Co-efficient
public static double binomial(int n, int k)
{
    if (k>n-k)
        k=n-k;

    long b=1;
    for (int i=1, m=n; i<=k; i++, m--)
        b=b*m/i;
    return b;
}

}

I notice that plugging in zeta(-1) returns -

Enter the value of s inside the Riemann Zeta Function: -1
The number of steps is 1.0
Value for the Zeta Function = -0.0506605918211689
Total time taken is 0.0 seconds.

I knew that this value was -1/12. I checked some other values with wolfram alpha and observed that --

double term = Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s);

Returns the correct value. It is just that I am multiplying this value every time by zeta(1-s). In the case of Zeta(1/2), this will always multiply the result by 0.99999999.

Enter the value of s inside the Riemann Zeta Function: 0.5
The series did not converge.
Value for the Zeta Function = 0.999999999999889
Total time taken is 0.006 seconds.

I am going to see if I can replace the part for --

    else if (term < threshold) {//The recursion will stop once the term is less than the threshold
    System.out.println("The number of steps is " + currSteps);
    return term;
}

This difference is the error between two terms in the summation. I may not be thinking about this correctly, it is 1:16am right now. Let me see if I can think better tomorrow ....

Respect answered 13/6, 2015 at 5:18 Comment(8)
Hmmm yes. This seems to be another error from the fact that 1-(1-s) = s (1-s = t, 1-t = s). Thresholds based on the term are not particularly useful in this case as the term will always be either zeta(s) or zeta(t) (alternating). What I meant to do in my code was provide a threshold that represents the change in your total. I got confused thinking about sums, where a product (percentage) would've worked, but using products, I need to give you a threshold based off of differences (subtraction) between terms. I will edit my answer to fix this.Soracco
Ok, I have updated my code to pass a running total along and calculate absolute differences instead of a percentage. This absolute difference should actually decrease over time in a converging series. (E.g. A-B > B-C in converging series A,B,C,D,...etc. With the 1/2 case, {1/2-1/2*.9999} > {1/2*.9999-1/2*(.9999)^2} )Soracco
This is impossible. I am sorry for not seeing it before. The functional form of the Zeta Function is not an infinite sum, it is a direct relationship between Zeta(s) and Gamm(s)*Zeta(s). If doesn't matter how we write this in Java, it will never match zeta(s) = 2^s pi^(-1+s) Gamma(1-s) sin((pi s)/2) zeta(1-s). I tried a bunch of different stuff and noticed that it didn't work. I will have to use a different approximation, I am sorry for not seeing this before.Respect
Is it not an infinite product? Or does this product just not converge on a single value? (For example your zeta(1/2) converges on 0. This might not be the case for other values of s.) Or is it simply not recursive at all? (As Zeta(s) = k*Gamma(s)*Zeta(s) would suggest.)Soracco
The definition is similar to (recursion in programming). zeta(s) = 2^s pi^(-1+s) Gamma(1-s) sin((pi s)/2) zeta(1-s). Although, there is no way to write this in a computer program. The computer program doesn't know the value for zeta(1-s).Respect
Wow, I am an idiot. I am a complete moron. Of course the functional equation works. All I had to do was reference the infinite sum inside the functional equation. This is NOT difficult to write, it is actually extremely simple.Respect
So you calculate zeta(1-s) using the infinite sum....? Well I hope you can use my method after all then =D ! (At least for the infinite sum part.)Soracco
Yes, you have to use a different form of the zeta function for zeta(1-s). You can use the standard infinite sum. You just run into issues at the critical strip where Re(s) > 0 and Re(s) < 1. I have shown a method of dealing with that. It isn't completed yet.....Respect

© 2022 - 2024 — McMap. All rights reserved.