高精度计算PI值

题目描述

​    使用双向链表作为存储结构,请根据用户输入的一个整数(该整数表示精确到小数点后的位数,可能要求精确到小数点后 500 位),高精度计算PI值。提示:可以利用反三角函数幂级展开式来进行计算。

解题思路

求PI的算法

​    首先这道题是要求必须使用双向链表作为存储结构的,这个需要注意,而且也不能用数组计算完了之后挨个赋值给链表的每个节点,这是耍赖 。

​    那么我们开始再想,用什么公式来求 PI 呢?这是一个问题。先没管题目的提示,我去百度了一通,发现了一个很神奇的算法,用三行就可以计算到圆周率小数点后800+位。#include

using namespace std;

long a=1000,b,c=2800,d,e,f[2801],g;

int main(){

for(;b-c;) f[b++]=a/5;

for(;d=0,g=c*2;c-=14,printf("%.3d",e+d/a),e=d%a)

for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);

}

​    实验了一下发现居然真的是,而且效率还挺高的?看了一会实现的原理一直没看懂,作罢。

​    最后发现是找不到什么除了幂级展开还有啥高效率的算法了好像,还是考虑题目提示的 三角函数幂级展开。然后继续在网上搜索了一下,在学长的一个博客里发现了公式。

$$

f_{i} =

\begin{cases}

1 & {i=1}\\

f_{i-1}\times \frac{i-1}{2\times i-1} & {i>1}

\end{cases}

$$

$$

\frac{\pi}{2} = \sum_{i=1}^{\infty}{f_i}

$$

​    那么拿到了这个式子,我们就可以分析一下,怎么和链表结合起来做了。

链表的设计

​    双向链表,也就是每个节点有一个数据域,有前和后两个指针。我考虑到我们做加法和乘法是需要从后往前做,除法是需要从前往后做。因此需要双向遍历,我又添加了一个尾指针,记录当前链表的尾节点的地址,方便从后往前遍历,头节点可以保证从前往后遍历。设计如下:typedef struct node

{

int data;

struct node *nxt;

struct node *pre;

struct node *tail;

node() //构造函数,用于初始化

{

nxt = NULL;

pre = NULL;

tail = this;

data = 0;

}

}

​    数据域就用来存储每一位数字,好比 3.1415926 就从第一个节点到第八个节点依次存 31415926 。

乘法的实现

​    另出一个函数,函数声明类似于 void Multi(List L,int k)

​    乘法我们模拟竖式的乘法运算,考虑到这是一个 高精度大数 * 低精度整数 ,因此我们只需要从尾部到头部依次对每一位做乘法即可,考虑到进位问题,可以有两种办法可以是先做完乘法,然后再回到尾部,再从尾到头依次处理进位,这样的好处是这两种操作分隔开了,操作起来难度不大,也比较好想。

可以是边做乘法边进位,我们定义一个 temp 用来存储低位到高一位的进位,这里要注意的是,对于某一位的操作不是先加上进位再做乘法,是先做乘法,再加低位的进位。

​    但是有一个问题我们需要注意,就是好比 52 * 3 ,这时候原来的两位数变成三位数了,因此需要我们在头节点和第一个节点之间增加新的节点,并且可能增加的不只是一个,只要 temp 这个进位大于等于10就需要一直创建新的节点来保证进位。

除法的实现

​    另出一个函数,函数声明类似于 void Division(List L,int k)

​    除法也是模拟竖式运算,这个是从头到尾进行处理,这一位的数据做完除法,余数作为下一位的“进位” ,这里可以边做边 “进位” 。这里这个进位不是乘法的那种进位,注意区分。

​    需要注意的是最后可能有除不尽的情况,这样我们就可以一直在尾部插入节点,然后处理一直处理数据到最大位数 或者 到 “进位” 为0为止 。最后不要忘记重置一下尾指针,指向新的尾节点。

加法的实现

​    函数声明类似于 void Sum(List L,List p) 。

​    加法的实现也是需要从后往前遍历,然后依次对每一位做加法。先取两个链表的尾节点出来,然后依次向前遍历相加,我们可以把相加的答案放在前面那个链表里面,注意这样是不需要返回值的,因为链表内部是通过地址索引的,我们改变的就是传入链表的值。

最后的整合

​    最后我们总共需要一个和链表,一个 $f_{i}$。 和链表用于计算所有式子的累加和,而 $f_{i-1}$ 其实就是 $f_i$ 的上个阶段 。我们每次把 $f_i$ 和 和链表进行累加即可。

​    最后再对和链表乘2 即可。

代码实现#include

#include

#include

#include

#include

using namespace std;

const int maxsize = 600; // 设定求小数点后多少位

typedef struct node // 双向链表的结构体

{

int data;

struct node *nxt;

struct node *pre;

struct node *tail;

node() //构造函数,用于初始化

{

data = 0;

tail = this;

nxt = NULL;

pre = NULL;

}

}Node,*List;

void Mult(List Head,int k); // 对链表每一位 *k

void Divi(List Head,int k); // 对链表每一位 ÷k

void Sum(List a,List b); // 对两个链表的数求和,所得数放在前面链表中

void InitList(List &L); // 初始化链表,并把第一个节点值设为 1

void InitSum(List &L); // 初始化最后存和的链表,并把第一个节点置为1

void Output(List L,int k); // 输出一个链表,保留 k 位小数

int main()

{

int n = 0;

scanf("%d",&n);

List Head,S;

InitList(Head);

InitSum(S);

for(int i=2;i<=3000;i++) //计算pi值

{

Mult(Head,i-1);

Divi(Head,2*i-1);

Sum(S,Head);

}

Mult(S,2);

Output(S,n);

return 0;

}

void Mult(List Head,int k) // 对以Head为头节点的链表中的每一位做乘法

{

List p = Head->tail; // 先把指针指向链表的末尾,方便从后往前做乘法

while(p != Head) // 从前往后开始算乘法

{

p->data *= k;

p = p->pre;

// printf("I have done Multi.\n");

}

p = Head->tail;

while(p != Head->nxt) //开始处理进位

{

p->pre->data += p->data /10;

p->data %= 10;

p = p->pre;

}

while( p->data > 10) //一直处理最高位的进位

{

List s = (List)malloc(sizeof(Node));

s->data = p->data/10;

p->data %= 10;

s->pre = Head;

s->nxt = p;

p->pre = s;

Head->nxt = s;

p = s;

}

}

void Divi(List Head,int k) // 对链表每一位除k

{

int temp = 0,depth = 0; //temp用于进位计算 ,depth 用于计算链表长度

List p = Head->nxt;

List t; // 存尾部节点

while(p != NULL) //模拟做除法

{

depth++;

p->data += temp*10;

temp = p->data % k;

p->data /= k;

t = p;

p = p->nxt;

}

p = t;

while(temp!=0 && depth <= maxsize) // 如果除不尽,就一直往后拓展节点,但注意不要超过最大位数

{

// printf("I have done Division!\n");

depth++;

List s = (List)malloc(sizeof(Node));

s->data = temp*10;

s->nxt = NULL;

s->pre = p;

temp = s->data % k;

s->data /= k;

p->nxt = s;

p = s;

}

Head->tail = p;

}

void Sum(List a,List b) // 对两个链表的数求和,所得数放在前面链表中

{

List p = a->tail,k = b->tail; // 先指向各自的尾部,开始从前往后加

while(p!=a && k!=b) // 遍历到有一个到头节点为止

{

p->data += k->data;

p->pre->data += p->data / 10;

p->data %= 10;

p = p->pre;

k = k->pre;

}

}

void InitList(List &L) // 初始化链表,并把第一个节点值设为 1

{

L = (List)malloc(sizeof(Node));

L->data = 0;

List s = (List)malloc(sizeof(Node));

s->data = 1;

s->pre = L;

L->nxt = s;

L->pre = NULL;

L->tail = s;

s->nxt = NULL;

}

void InitSum(List &L) // 初始化最后存和的链表,并把第一个节点置为1

{

L = (List)malloc(sizeof(Node));

L->nxt = NULL;

L->pre = NULL;

L->data = 0;

List p = L;

int depth = 0;

while(depth <= maxsize)

{

List s = (List)malloc(sizeof(Node));

s->data = 0;

s->pre = p;

p->nxt = s;

s->nxt = NULL;

p = s;

depth++;

}

L->nxt->data = 1;

L->tail = p;

}

void Output(List L,int k) //输出一个列表,保留 k 位小数

{

List p = L->nxt;

printf("%d.",p->data);

p = p->nxt;

int t = 0;

while(p != NULL && t

{

t++;

printf("%d",p->data);

p = p->nxt;

}

printf("\n");

}

参考链接

高精度计算PI值 C语言 思路,高精度计算pi相关推荐

  1. 高精度计算PI值 C语言 思路,高精度计算PI值

    高精度计算PI值 高精度计算PI值 所用公式: #include #include typedef struct list{ int data; struct list *next; struct l ...

  2. [JuliaDelphi]快速计算圆周率 Pi (π) 值

    算法参考自: 计算圆周率Pi(π)值,精确到小数点后10000位 - 圆周率10000位 - C++ 爱好者 分别以Julia 1.72和Delphi 10.3写的算法如下面所示.在一台10年的老电脑 ...

  3. Python获取pi值的五种方法

    五种获取PI值的方法 1.自己把PI背下来 2.math库直接获取PI 3.math库计算获取PI 4.numpy库直接获取PI值 5.scipy库直接获取PI值 1.自己把PI背下来 PI = 3. ...

  4. 数据结构28——高精度计算PI值

    题目:输入n,输出PI精确到小数点后n位的PI值. #include<stdio.h> #include<stdlib.h>typedef struct node {int d ...

  5. 高精度计算Π的值(C语言)

    题目要求 限制使用双向链表作存储结构,请根据用户输入的一个整数(该整数表示精确到小数点后的位数,可能要求精确到小数点后500位),高精度计算PI值.可以利用反三角函数幂级展开式来进行计算. 输入5 输 ...

  6. C语言程序设计入门08——初识格式化输出1:格式化输出PI值

    格式化输出函数 printf() 作为 C 程序的标准格式化输出函数,printf()的函数声明包含在头文件 <stdio.h> 中,函数的一般形式为:  printf ( <待打印 ...

  7. C语言——PTA 用格里高利公式求给定精度的PI值

    打赏一点钱,帮我买包辣条,继续创作,谢大家! PTA 用格里高利公式求给定精度的PI值 本题要求编写程序,计算序列部分和 4∗(1−1/3+1/5−1/7+-) ,直到最后一项的绝对值小于给定精度ep ...

  8. [Julia语言]使用Chudnovsky 算法快速计算圆周率 Pi (π) 值

    测试用的电脑是一台10年老电脑,CPU型号:E3 1230V2,3.3GHZ,4核8线程,8GB内存. 用下面的Julia程序,计算1万位的Pi值,耗时为0.26秒. 作为比较,用Julia实现的另一 ...

  9. Java循环案例-求PI值

    30天! 题目一: PI=4/1-4/3+4/5-4/7+4/9-4/11+4/13-4/15+4/17- 1.这个算式的结果会无限接近于圆周率的值,我国古代数学家祖冲之计算出,圆周率在3.14159 ...

最新文章

  1. 基于Charlieplexing算法(查理复用算法)的LED矩阵控制设计
  2. 一文告诉你,如何使用Python构建一个“谷歌搜索”系统 | 内附代码
  3. 盘点深度学习中的各种数据增强技巧
  4. vue项目打包之后原本好的样式变得不好了的原因分析
  5. stackexchange_通过Spring Social发推StackExchange问​​题
  6. sqlserver垮库查询_Oracle和SQLServer中实现跨库查询
  7. 纯做技术是自娱自乐 抛开技术做技术才是出路
  8. 题目1439:Least Common Multiple
  9. Tomcat下ajax请求路径总结
  10. JS实现返回顶部功能
  11. 使用RN开发App,引入图标失效问题的解决
  12. sublime 前端css 插件
  13. 《山水情》------中国水墨山水动画的颠峰与极至
  14. 手机里舍不得删的48条短信
  15. 网易im即时聊天php怎么接入,网易云信IM即时通讯功能接入方式与流程_如何收费_企业服务汇...
  16. 培训班出来的程序员现在怎么样了
  17. 关于错排公式的推导与应用
  18. MyBatis框架学习笔记01:初生牛犊
  19. 史上最详细浏览器端网页截图方案分析
  20. 明解C语言入门篇_第13章_文件处理

热门文章

  1. 2019年9月数据库流行度排行:MySQL 强劲增长完成深 V 反转
  2. 19个强大、有趣、又好玩的 Linux 命令!
  3. 倒计时7天:招行、平安齐聚DTC 2019之金融峰会,讲师议题抢先看
  4. 19C新特性:Voting Disk管理
  5. 数仓备机DN重建:快速修复你的数仓DN单点故障
  6. 华为云数据库首席架构师:关于数据库他这样说……
  7. MindSpore21天实战营丨基于MindSpore的ResNet-50蘑菇“君”的识别应用体验
  8. 【华为云技术分享】Linux内核的分布式编译(2)
  9. 【华为云技术分享】云图说|人工智能新科技—文字识别服务
  10. 无人车飞速狂飙,黑科技如何为其加油打气?