题目:输入一个正整数n(n最多可能为500),要求输出PI,精确到小数点后n位.
这一题是我学习数据结构初期碰到的一题,刚开始感觉无从下手,后来慢慢找到了思路.
题目要求可能精确到小数点后500位,那么显然double类型是远远不够的,采用双向循环链表结构计算.
双向链表的每一个结点存储一位数字,算法思想简单说是,首先计算出迭代次数,然后调用大数运算函数进行迭代,最后输出即可。
使用的求PI公式:PI = 2*Σn!/(2n+1)!!
代码如下,望高手批评指正.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
typedef struct DNode
{
int data;
struct DNode *prior, *next;
} DNode, *DoubleList;
void getInput(int *n);//输入
int computeTermN(int n);//计算精度
DoubleList computePi(DoubleList PI, int k);//计算pi
void createDLList(DoubleList *L, int k);//创建长度为k的双向链表L并全部初始为0
DoubleList bigSum(DoubleList L1, DoubleList L2);//大数加法,并将结果返回至L1
DoubleList bigDiv(DoubleList L, DoubleList result, int k);//大数除法,并将结果返回至result
DoubleList bigMul(DoubleList L, DoubleList result, int k);//大数乘法,并将结果返回至result
void printDLList(DoubleList L, int k);//输出链表
int main()
{
int n;//小数点后位数
int k;//计算次数
///1.输入
getInput(&n);
///2.计算精度
k = computeTermN(n);
///3.计算pi
DoubleList PI;
createDLList(&PI, k);
computePi(PI, k);
///4.输出pi
printDLList(PI, n);
///5.释放节点
free(PI);
return 0;
}
void getInput(int *n)
{
scanf("%d", n);
}
int computeTermN(int n)
{
int k;
double i, sum = 0;
for(i = 1; sum <= n + 1; i++)
{
sum = sum + log10((2 * i + 1) / i);
}
k = i + 20;
return k;
}
DoubleList computePi(DoubleList PI, int k)
{//求和公式:2*Σn!/(2n+1)!!
DoubleList L, R;
createDLList(&L, k);
createDLList(&R, k);
double i;
PI->next->data = 2;//初始化为2*1
R->next->data = 2;//从第二项开始,第一项初始化为2*1
for(i = 1; i < k; i++)
{
bigMul(R, L, i);//r = r * i,结果在L中
bigDiv(L, R, (2 * i + 1));//r = r * i / (2 * i + 1),结果在R中
bigSum(PI, R);//pi = pi + r,结果在PI中
}
free(L);
free(R);
return PI;
}
void createDLList(DoubleList *L, int k)
{
DoubleList q, s;
int i;
*L = (DoubleList)malloc(sizeof(DNode));
(*L)->prior = *L;
(*L)->next = *L;
q = *L;
for(i = 0; i < k; i++)//创建k个链表结点,并全部置为0
{
s = (DoubleList)malloc(sizeof(DNode));
s->data = 0;
q->next = s;
s->next = *L;
s->prior = q;
(*L)->prior = s;
q = s;
}
}
DoubleList bigSum(DoubleList L1, DoubleList L2)
{
DoubleList p, q;
int carry = 0;
p = L1->next;
q = L2->next;
while(p != L1 && q != L2)//每一位相加
{
p->data = p->data + q->data;
p = p->next;
q = q->next;
}
p = L1->prior;
while(p != L1)//从尾结点开始往前依次进位
{
p->data = p->data + carry;
carry = p->data / 10;
p->data = p->data % 10;
p = p->prior;
}
return L1;
}
DoubleList bigMul(DoubleList L, DoubleList result, int k)
{
DoubleList p, q;
int carry = 0;
p = L->next;
q = result->next;
while(p != L && q != result)//每一位都乘以k
{
q->data = p->data * k;
p = p->next;
q = q->next;
}
q = result->prior;
while(q != result)//从尾结点开始往前依次进位
{
q->data = q->data + carry;
carry = q->data / 10;
q->data = q->data % 10;
q = q->prior;
}
return result;
}
DoubleList bigDiv(DoubleList L, DoubleList result, int k)
{
int num;
DoubleList p, q;
p = L->next;
q = result->next;
num = 0;
while(q != result)
{
num = num + p->data;
while(num < k)//当被除数不够时向后移动p
{
q->data = 0;
q = q->next;
p = p->next;
num = num * 10 + p->data;
if(q == result)//如果找到结尾还不能除,则直接跳出循环
{
return result;
}
}
q->data = num / k;
q = q->next;
p = p->next;
num = (num % k) * 10;
}
return result;
}
void printDLList(DoubleList L, int k)
{
DoubleList p;
int i;
p = L->next;
printf("%d", p->data);
p = p->next;
printf(".");//打印小数点
for(i = 0; i < k; i++)//打印小数点后k位
{
printf("%d", p->data);
p = p->next;
}
printf("\n");
}