沃里斯乘积公式求圆周率-2

为了计算超出double精度的圆周率数值,可以考虑用大整数分别计算分子和分母的数值,最后再将分子左移(相当于在右侧填0),然后再相除,就可以得到一个定点数,而这个定点数的位数显然对应圆周率的每一位,也就是说,结果不是3.1415926……而是31415926……

C#代码和C-GMP代码如下:

using System.Diagnostics;
using System.Numerics;

namespace WallisFormulaForPi;

public class Program
{
    //计算圆周率的沃里斯乘积算法
    //分子和分母分别计算
    //最后彼此相除
    //可以得到极高精度的计算结果
    //Pi/2 = Sum(n!/(2n+1)!!,n>=0)
    //Pi/2 = 0!/1! + 1!/3! + 2!/5!+...
    static BigInteger WallisProductForPi(BigInteger n)
    {
        var upper = n;
        var lower = (n<<1)+1;
        upper += lower;
        for(--n; n >= 1; --n)
        {
            upper *= n;
            upper += (lower *= ((n << 1) + 1));
        }
        
        upper *= BigInteger.Pow(10, (int)Math.Ceiling(BigInteger.Log10(lower)));

        return (upper<<1) / lower;
    }

    //单阶乘
    static long Factorial1(long n) => n >= 1 ? n * Factorial1(n - 1) : 1;
    //双阶乘
    static long Factorial2(long n) => n >= 1 ? n * Factorial2(n - 2) : 1;
    //沃里斯乘积形式求圆周率(的一半)
    //Pi/2 = Sum(n!/(2n+1)!!,n>=0)
    //Pi/2 = 0!/1! + 1!/3! + 2!/5!+...
    //此函数因为decimal精度不够,所以结果在第15次迭代之后出错
    //若要获得更正确的结果,请使用Python版本的WFP.py进行高次迭代
    static decimal WallisProductForPiDiv2(int max)
    {
        decimal s = 0;
        for(int n = 0; n <= max; n++)
        {
            decimal f1 = Factorial1(n);
            decimal f2 = Factorial2(2 * n + 1);
            s += f1 / f2;
        }
        return s;
    }
    /// <summary>
    //0       Pi / 2 = 1.5707963267948966, s=1
    //1       Pi / 2 = 1.5707963267948966, s=1.3333333333333333333333333333
    //2       Pi / 2 = 1.5707963267948966, s=1.4666666666666666666666666666
    //3       Pi / 2 = 1.5707963267948966, s=1.5238095238095238095238095237
    //4       Pi / 2 = 1.5707963267948966, s=1.5492063492063492063492063491
    //5       Pi / 2 = 1.5707963267948966, s=1.5607503607503607503607503606
    //6       Pi / 2 = 1.5707963267948966, s=1.5660783660783660783660783659
    //7       Pi / 2 = 1.5707963267948966, s=1.5685647685647685647685647684
    //8       Pi / 2 = 1.5707963267948966, s=1.5697348403230756171932642519
    //9       Pi / 2 = 1.5707963267948966, s=1.5702890848401684314997008494
    //10      Pi / 2 = 1.5707963267948966, s=1.5705530108006888192646706577
    //11      Pi / 2 = 1.5707963267948966, s=1.5706792362600681351522649138
    //12      Pi / 2 = 1.5707963267948966, s=1.5707398244805702067783101568
    //13      Pi / 2 = 1.5707963267948966, s=1.5707689965867378708945541627
    //14      Pi / 2 = 1.5707963267948966, s=1.5707830796724739846058443724
    //15      Pi / 2 = 1.5707963267948966, s=1.5707898940687979105951783448
    //16      Pi / 2 = 1.5707963267948966, s=1.5707931980185307231960675436
    //17      Pi / 2 = 1.5707963267948966, s=1.5720537562771873841109201530
    //18      Pi / 2 = 1.5707963267948966, s=1.5712541172833223799181741024
    //19      Pi / 2 = 1.5707963267948966, s=1.6621681818083471872333682946
    //20      Pi / 2 = 1.5707963267948966, s=-3.3927582543189338135211826460
    //21      Pi / 2 = 1.5707963267948966, s=-1.5032325578386644392795104774
    //22      Pi / 2 = 1.5707963267948966, s=-1.3637316045664853349204097715
    //23      Pi / 2 = 1.5707963267948966, s=1.4310016743495748554473553210
    //24      Pi / 2 = 1.5707963267948966, s=2.9792169001591796295424227251
    //25      Pi / 2 = 1.5707963267948966, s=48.560443173620516089682171666
    //26      Pi / 2 = 1.5707963267948966, s=48.368557499795514338475059471
    //27      Pi / 2 = 1.5707963267948966, s=47.601518302259020662711590070
    //28      Pi / 2 = 1.5707963267948966, s=44.029583981379705653595686502
    //29      Pi / 2 = 1.5707963267948966, s=42.917767093641865804038915153
    //30      Pi / 2 = 1.5707963267948966, s=77.236176026210844772999552441    
    /// </summary>
    /// <param name="args"></param>
    static void Main(string[] args)
    {
        BigInteger n = 100000;
        
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        var pi = WallisProductForPi(n);
        stopwatch.Stop();
        using var output = new StreamWriter("result.txt");
        output.WriteLine(pi);

        //Console.WriteLine(pi);
        Console.WriteLine($"Iterates = {n}, Digits = {(int)Math.Ceiling(BigInteger.Log10(pi))}, Duration = {stopwatch.Elapsed}");

        //double MP2 = Math.PI / 2.0;
        //for (int n = 0; n <= 30; n++)
        //{
        //    Console.WriteLine($"{n}\tPi / 2 = {MP2}, s={WallisProductForPiDiv2(n)}");
        //}
    }
}

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<gmp.h>
#include<iostream>
#include<time.h>

void wallis_product(mpz_t n, mpz_t result) {
	//upper = n
	mpz_t upper;
	mpz_init_set(upper, n);
	mpz_t lower;
	mpz_init(lower);
	mpz_add(lower, n, n);
	//lower = 2n+1
	mpz_add_ui(lower, lower, 1);
	//upper += lower
	mpz_add(upper, upper, lower);
    //n--
	mpz_sub_ui(n, n, 1);

	while (mpz_cmp_ui(n, 0) > 0) {
		mpz_mul(upper, upper, n);
		mpz_t s;
		mpz_init_set(s, n);
		//s=2n+1
		mpz_add(s, s, n);
		mpz_add_ui(s, s, 1);
		//lower*=s
		mpz_mul(lower, lower, s);
		//upper+=lower;
		mpz_add(upper, upper, lower);
		//n--
		mpz_sub_ui(n, n, 1);
	}

	size_t sz = mpz_sizeinbase(lower, 10);
	//upper*=2
	mpz_add(upper, upper, upper);

	mpz_t ten;
	mpz_init_set_ui(ten, 10);
	mpz_pow_ui(ten, ten, sz);

	mpz_mul(upper, upper, ten);
	mpz_div(result, upper, lower);
}


int main() {
	clock_t start, finish;
	start = clock();
	int m = 100000;
	mpz_t n, result;
	mpz_init(n);
	mpz_init(result);
	mpz_init_set_ui(n, m);

	wallis_product(n, result);
	size_t sz = mpz_sizeinbase(result, 10);

	finish = clock();
	gmp_printf("pi = %Zd\n", result);
	printf("iterate: %d, digits: %llu, time is :%lfms\n", m,sz, (double)(finish - start));

	return 0;
}

从比较可以看出,计算同样的迭代次数(100000次),产生同样的精度C-GMP的速度要比C#快10倍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值