【问题标题】:Riemann Zeta Function in Java - Infinite Recursion with Functional FormJava 中的黎曼 Zeta 函数 - 具有函数形式的无限递归
【发布时间】:2015-06-12 04:36:54
【问题描述】:

注意:更新于 2015 年 6 月 17 日。当然这是可能的。请参阅下面的解决方案。

即使有人复制并粘贴此代码,您仍然需要进行大量清理工作。另请注意,从 Re(s) = 0 到 Re(s) = 1 :) 的关键带内会有问题。但这是一个好的开始。

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);
}

}

【问题讨论】:

  • 递归似乎是正确的。很可能永远不会满足结束条件。所以,基本上 s 永远不会变成 1 或 0。如果我更好地理解黎曼 Zeta 函数,我将能够提供进一步的帮助。

标签: java recursion number-theory


【解决方案1】:

好吧,我们已经知道对于这个特定的函数,因为它的这种形式实际上并不是一个无限级数,所以我们不能使用递归来近似。但是黎曼 Zeta 级数的无穷和(1\(n^s) where n = 1 to infinity)可以通过这种方法求解。

此外,此方法可用于查找任何无限级数的总和、乘积或极限。

如果您执行您当前拥有的代码,您将获得无限递归为1-(1-s) = s(例如1-s = t1-t = s,因此您只需在s 的两个值之间来回切换无限)。

下面我谈谈系列的总和。看来您正在计算该系列的乘积。下面的概念应该适用于任何一个。

除此之外,黎曼 Zeta 函数是 infinite series。这意味着它只有一个限制,并且永远不会达到真正的和(在有限的时间内),因此您无法通过递归得到准确的答案。

然而,如果你引入一个“阈值”因子,你可以得到一个尽可能好的近似值。随着每个术语的添加,总和将增加/减少。一旦总和稳定下来,您就可以退出递归并返回您的近似总和。 “稳定”是使用您的阈值因子定义的。一旦总和的变化量小于此阈值因子(您已定义),您的总和就稳定了。

较小的阈值导致更好的近似值,但计算时间也更长。

(注意:此方法仅在您的系列收敛时才有效,如果它有可能不收敛,您可能还需要构建一个 maxSteps 变量以在系列未收敛到您满意之后停止执行maxSteps 递归步骤。)

这是一个示例实现,请注意,您必须使用 thresholdmaxSteps 来确定合适的值:

/* 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);
    }
}

【讨论】:

    【解决方案2】:

    这是不可能的。

    黎曼 Zeta 函数的函数形式是 --

    zeta(s) = 2^s pi^(-1+s) Gamma(1-s) sin((pi s)/2) zeta(1-s)
    

    这与标准方程不同,在标准方程中,对于所有 k = 1 到 k = 无穷大,从 1/k^s 测量无穷和。可以将其写成类似于 --

    // 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;
    }
    

    同样的逻辑不适用于函数方程(它不是直接求和,而是数学关系)。这需要一种相当聪明的方法来设计一个程序来计算 Zeta 的负值!

    这段Java代码的字面解释是---

    // Riemann's Functional Equation
    public static double riemannFuncForm(double s) {
    double currentVal = (Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s));
    if( s == 1.0)
        return 0;
    else if ( s == 0.0)
        return -0.5;
    else
        System.out.println("Value of next value is " + nextVal(1-s));
        return currentVal;//*nextVal(1-s);
    }
    
    public static double nextVal(double s)
    {
        return (Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s));
    }
    
    public static double getRiemannSum(double s) {
        return riemannFuncForm(s);
    }
    

    对三个或四个值的测试表明这是行不通的。如果你写类似于 --

    // Riemann's Functional Equation
    public static double riemannFuncForm(double s) {
    double currentVal = 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 //Otherwise just keep recursing
        return currentVal * nextVal(1-s);
    }
    
    public static double nextVal(double s)
    {
        return (Math.pow(2, s)*Math.pow(Math.PI, s-1)*(Math.sin((Math.PI*s)/2))*gamma(1-s));
    }
    

    我误解了如何通过数学来做到这一点。对于小于 2 的值,我将不得不使用不同的 zeta 函数近似值。

    【讨论】:

      【解决方案3】:

      我想我需要使用不同形式的 zeta 函数。当我运行整个程序时---

      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;
      }
      

      }

      我注意到插入 zeta(-1) 会返回 -

      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.
      

      我知道这个值为 -1/12。我用 wolfram alpha 检查了一些其他值并观察到 ​​--

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

      返回正确的值。只是我每次都将这个值乘以 zeta(1-s)。在 Zeta(1/2) 的情况下,这将始终将结果乘以 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.
      

      我去看看能不能换个零件——

          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;
      }
      

      这个差异是求和中两项之间的误差。我可能没有正确考虑这个问题,现在是凌晨 1 点 16 分。让我看看明天我能不能想得更好......

      【讨论】:

      • 嗯,是的。这似乎是1-(1-s) = s (1-s = t, 1-t = s) 的另一个错误。在这种情况下,基于术语的阈值并不是特别有用,因为术语将始终zeta(s)zeta(t)(交替)。我在我的代码中要做的是提供一个阈值来代表你的总数的变化。我对总和感到困惑,其中一个产品(百分比)会起作用,但是使用产品,我需要根据术语之间的差异(减法)给你一个阈值。我将编辑我的答案来解决这个问题。
      • 好的,我已经更新了我的代码以传递运行总计并计算绝对差异而不是百分比。这种绝对差异实际上应该在收敛序列中随着时间的推移而减小。 (例如 A-B > B-C 收敛系列 A,B,C,D,...等。对于 1/2 情况,{1/2-1/2*.9999} > {1/2*.9999-1 /2*(.9999)^2} )
      • 这是不可能的。我很抱歉之前没有看到它。 Zeta 函数的函数形式不是无限和,它是 Zeta(s) 和 Gamm(s)*Zeta(s) 之间的直接关系。如果我们在 Java 中如何编写它并不重要,它永远不会匹配 zeta(s) = 2^s pi^(-1+s) Gamma(1-s) sin((pi s)/2) zeta(1 -s)。我尝试了一堆不同的东西,发现它不起作用。我将不得不使用不同的近似值,很抱歉之前没有看到这个。
      • 不是无限积吗?或者这个产品只是不集中在一个单一的价值上? (例如,您的zeta(1/2) 收敛于0s 的其他值可能不是这种情况。)或者它根本不是递归的? (正如 Zeta(s) = k*Gamma(s)*Zeta(s) 所建议的那样。)
      • 定义类似于(编程中的递归)。 zeta(s) = 2^s pi^(-1+s) Gamma(1-s) sin((pi s)/2) zeta(1-s)。虽然,没有办法在计算机程序中编写它。计算机程序不知道 zeta(1-s) 的值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-23
      • 2012-01-31
      • 1970-01-01
      • 2015-04-19
      • 1970-01-01
      相关资源
      最近更新 更多