ACM 1001题:求高精度幂



求高精度幂
Time Limit: 500MS   Memory Limit: 10000K
Total Submissions: 87719   Accepted: 20833

Description

对数值很大、精度很高的数进行高精度计算是一类十分常见的问题。比如,对国债进行计算就是属于这类问题。 

现在要你解决的问题是:对一个实数R( 0.0 < R < 99.999 ),要求写程序精确计算 R 的 n 次方(R n ),其中n 是整数并且 0 < n <= 25。

Input

T输入包括多组 R 和 n。 R 的值占第 1 到第 6 列,n 的值占第 8 和第 9 列。

Output

对于每组输入,要求输出一行,该行包含精确的 R 的 n 次方。输出需要去掉前导的 0 后不要的 0 。如果输出是整数,不要输出小数点。

Sample Input

95.123 12 0.4321 20 5.1234 15 6.7592 9 98.999 10 1.0100 12

Sample Output

548815620517731830194541.899025343415715973535967221869852721 .00000005148554641076956121994511276767154838481760200726351203835429763013462401 43992025569.928573701266488041146654993318703707511666295476720493953024 29448126.764121021618164430206909037173276672 90429072743629540498.107596019456651774561044010001 1.126825030131969720661201

分析:

在计算机上进行高精度计算,首先要处理好以下几个基本问题:
 1、数据的输入与存储;
 2、计算结果位数的确定;
 3、进位处理和借位处理;
 4、商和余数的求法;

 

这题是乘幂计算,所以关键是输入和储存,还有乘法算法的实现.


1、数据的输入与存储;

(1)数组:每个数组元素存储1位(在优化时,这里是一个重点!),有多少位就需要多少个数组元素;

优点:每一位都是数的形式,可以直接加减;运算时非常方便

缺点:数组不能直接输入;输入时每两位数之间必须有分隔符,不符合数值的输入习惯;
(2)字符串:字符串的最大长度是255,可以表示255位。

优点:能直接输入输出,输入时,每两位数之间不必分隔符,符合数值的输入习惯;

缺点:字符串中的每一位是一个字符,不能直接进行运算,必须先将它转化为数值再进行运算;运算时非常不方便(注意‘0’的问题)。


2、乘法算法的实现

思路:按照竖式乘法计算的方式,就像我们以前打草稿那样.简单点来说就是:申请一个空间为250的一维数组m[250]。然后里面只存放每次相乘得到的结果。乘数用m_data表示.把m[]中的每位数每次都乘以m_data.再与原有的m[]相加,要注意进位的处理.具体的看代码吧


3完整代码:

注释的比较清楚,但是这个算法效率很差.还没有优化.反正思路就是这样.

#include <iostream>
#include <stdio.h>
using namespace std;

//功能:计算,并将结果储存在数组m中
//参数:d:要计算的大数;n1:指数幂;m[]用于存放结果
//思路:按照竖式乘法计算的方式,来获得.每一次的结果都存在数组m[]中
void calculate(int d, int n1, int m[])
{
	int m_data = d;
	int n = n1;
	int data = m_data;
	int j = 250;

	//将大数先存到m中
	while(data/10 != 0 || data%10 != 0 )
	{
		j--;
		m[j] = data%10;
		data = data/10;
	}

	int i = j-1;//设置i从m数组中已经存在数的前一位开始

	//指数幂,循环n次
	while(--n)
	{
		//将m中所有的数都循环一次
		for (j = i+1; j < 250; j++)
		{
			int temp = m[j] * m_data; //将数组m中,每一位数都与大数相乘
			m[j] = 0;
			int t = j;

			//将相乘的结果与原先在m中的相加
			while(temp / 10 != 0 || temp % 10 != 0)
			{
				m[t] = m[t] + temp % 10;
				int t1 = t;
				//若大于9则要进位
				while(m[t1] > 9)
				{
					int tem2 = m[t1];
					m[t1] = tem2 % 10; 
					t1--;
					m[t1] = m[t1] + (tem2 / 10);
				}
				t--;
				temp = temp / 10;
			}
		}

		i=0;
		while(m[i]==0)i++; //找到m中第一个非零的位置 
		i--;
	}
}

//功能:确定r[]是否有小数点
//其实这个函数不必要.等优化的时候再改
int wheredot(char r[])
{
	for (int i = 0; i < 6; i++)
	{
		if (r[i] != '.')
		{
			continue;
		}else {
			return i;
		}
	}
	return 5;
}

//功能:输出计算好的m[],并根据小数位数,添加小数点.
void output(int m[],  int decimals, int n)
{
	//得到m中第一个不为0的数
	int f = 0;
	while (m[f] == 0) f++;

	//得到总的小数位数
	int sumDecimals = n * (decimals);

	//当第一个不为0的数在小数点之前,则先输入数,当到小数点时,输出小数点
	if (f <= (250-sumDecimals))
	{
		for (int i = f; i < 250; i++)
		{
			//当i的位置等于(250 -小数的位数,就是已经输出的位数)时,输出小数点
			if (250 - sumDecimals == i )
			{
				cout << '.';
			}
			cout << m[i];
		}
	} else {
		//当第一个不为0的数在小数点之后,先输出小数点.后面的0也不省略
		for (int i = (250 - sumDecimals); i < 250; i++)
		{
			if (250 - sumDecimals == i )
			{
				cout << '.';
			}
			cout << m[i];
		}
	}
}

//功能:从输入的r[]获得大数,并确定小数点位数
//思路:将r去掉小数点和小数末尾的0后,在转化成大数.
int large(char r[], int *decimals)
{
	int m_data = 0;
	int number[6] = {-1,-1,-1,-1,-1,-1};//将r数组中去掉小数点和最后的0,再存入.
	int m_dot = 0;		//小数点位于哪里
	int m_decimals = 0; //小数位数

	m_dot = wheredot(r);
	m_decimals = 5 - m_dot;

	if (m_decimals != 0)
	{
		//有小数
		int l = 5;
		while(r[l] == '0') 
		{
			l--;//去掉r中小数后的0
			m_decimals--;//小数位数-1
		}
		for (int i = l, j = 5; i >= 0; i--)
		{
			if (r[i] != '.')
			{
				//将数组r中的字符数转为int型后存入数组number[]中
				number[j] = r[i] - '0';
				j--;
			} else {
				//跳过数组
				continue;
			}
		}
	
	} else {
		//没有小数
		for (int i = 5, j = 5; i >= 0; i--)
		{
			number[j] = r[i]- '0';
			j--;
		}
	}

	for (int i = 0; i < 6; i++)
	{
		if (number[i] != -1)
		{
			m_data = number[i]  + m_data * 10;
		}	
	}

	*decimals = m_decimals;
	return m_data;
}


int main()
{
	char r[6] = {'0'};  //底数
	int n; //指数
	
	//输入
	while(cin >> r >> n)
	{
		int m[250] = {0}; //存放结果
		int m_data = 0;  //小数点去掉后的大数
		int *m_decimals = new int; //小数位数

		m_data = large(r, m_decimals); //获得大数
		calculate(m_data, n, m); //计算,并将结果储存在数组m中
		output(m, *m_decimals, n);//输出结果,添加上小数点.
		delete m_decimals;
		cout << endl;
	}
	return 0;
}


4.java实现

如果是用java的话,就很好解决了.直接使用Java中的BigDecimal类就可以了.下面对BigDecimal类,简单介绍一下:

       Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。


BigDecimal一共有4个构造方法:

BigDecimal(int) 创建一个具有参数所指定整数值的对象。
BigDecimal(double) 创建一个具有参数所指定双精度值的对象。
BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。

BigDecimal 的运算方式 不支持 + - * / 这类的运算 ,它有自己的运算方法
BigDecimal add(BigDecimalaugend) 加法运算
BigDecimal subtract(BigDecimal subtrahend) 减法运算
BigDecimal multiply(BigDecimal multiplicand) 乘法运算
BigDecimal divide(BigDecimal divisor) 除法运算

下面使用BigDecimal类就能很容易实现高精度的乘幂运算了.

代码如下:

import java.math.BigDecimal;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
	    Scanner cin = new Scanner(System.in);//用于输入读取

	    while (cin.hasNext())
	    {
		    BigDecimal data = cin.nextBigDecimal();
		    int n = cin.nextInt();
		    BigDecimal result;
		    result = data.pow(n).stripTrailingZeros();//去掉小数点后面的零
		    String m_string = result.toPlainString();
		    if (m_string.charAt(0) == '0') {
			    m_string = m_string.substring(1);
		    }
		    System.out.println(m_string);
	    }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值