数据结构实验-高精度计算PI值-NOJ2

题目:输入一个正整数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");
}

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值