Exponentiation(高精度幂计算)(标签:链表,字符串,快速幂计算)

题目描述

对数值很大、精度很高的数进行高精度计算是一类十分常见的问题。比如,对国债进行计算就是属于这类问题。
现在要你解决的问题是:对一个实数R( 0.0 < R < 99.999 ),要求写程序精确计算 R 的 n 次方(Rn),其中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

解题思路

对于此类高精度计算,一定不能用常规的+、-、*、/运算符运算,因其会导致溢出问题,所以考虑将原数据的每一位分开,并且顺序放入线性表中,模拟竖式的计算过程进行计算。在选择线性表的种类时,根据实际需要选择数组或链表。在C++ STL标准模板库中,以数组实现的线性表的容器是vector,其容量可以随数据大小变化,即在开始时会根据初始数据的大小开辟一个比数据的长度稍大的空间,比如初始数组中只有一个数,但是容器会先开辟8个数据的容量。若后续对容器进行数据量的增加,会填满这8个空位,那么容器会自动判断是否有空位,容量不够的话,会重新开辟一块更大容量的空间,并且将原数组中的内容拷贝到新的空间。这就导致,在此问题中若用数组实现线性表时,会浪费更多的程序运行时间,所以此处选择链表形式的线性表,在STL中,使用线性表需要添加头文件:

#include <list>

将题目中输入的s转存到list当中,在转存时,为了方便操作,将高位与低位倒置放在链表中(因为模拟竖式运算时,需要从低位向高位计算)。并且同时记录原始数据中小数点的位置。

list<int> origin, res(, ); //origin为转换为链表之后存储底数的变量
int dot_pos = , res_dot = ; //dot_pos为原底数小数点位置, res_dot记录结果的小数点位置
for (int i = s.size() - ; i >= ; --i) {
if (s[i] == '.') dot_pos = s.size() - i - ; //记录是否在原底数中有小数点的存在,记录逆序小数点位置
else origin.push_back(s[i]-''); //转化char为int类型
}

接下来运用快速幂计算的算法思路来求解s的n次幂。普通的幂计算是用递归(递推)方式求解,计算n次幂需要n次递归计算,时间复杂度为O(n)。而快速幂算法的简单思路为:若计算s^n,将n表示为二进制形式,如5可表示为101,即1+4,则,s^5=s^(1+4)=s^1*s^4=1*s^1*s^4。根据以上所述,依次计算s^1, s^2, s^4, ...,将s^1与s^4累乘到1上,即可得到结果。时间复杂度为O(logn)。详细代码如下:

/*使用快速幂运算求高精度幂*/
while (n > ) {
if (n & ) {
multiple(res, origin);
res_dot += dot_pos; //更新结果的小数点位置
}
multiple(origin, origin);
if (dot_pos > ) dot_pos <<= ;
n >>= ;
}

其中,multiple计算两个存储在链表中的两个数的乘积,结果通过第一个参数的引用返回。multiple函数的定义如下:

/*计算存储在两个链表中的两个数的乘积,结果通过Num1的引用返回*/
void multiple(list<int>& num1, list<int> num2) { //模拟乘法竖式进行计算,规定num2为乘数,num1为被乘数
list<int> temp, origin_num1(num1); //temp存储乘数与被乘数其中一位的临时结果,origin_num1为num1的拷贝,以防后续将num1清空导致原数据丢失
int key_index = ; //记录两位数相乘时被乘数的左移位数
num1.erase(num1.begin(), num1.end());
for (auto key : origin_num1) {
temp.erase(temp.begin(), temp.end());
for (int i = ; i < key_index; ++i) temp.push_back();
multiple_one(num2, key, temp);
add(num1, temp);
++key_index;
}
}

注意,因为传入的两个数num1与num2可能是同一个变量,所以此处实现时,为了函数体中能够保留num2原参数值,没有传入引用。传入引用时,C++编译器直接对实参进行操作,类似指针,而不加&符号时,C++在初始化函数后,拷贝一份实参的实体,并对这个新的实体进行操作,关于C++引用的详细内容,可参考这里

上述代码中的multiple_one与add函数分别计算一位数与多位数相乘以及两个多位数相加,实现思想与multiple函数相同,即模拟竖式计算。其函数原型如下:

/*计算一个一位整数与存储在链表L-H中的数的乘积,结果存储在result的引用返回*/
void multiple_one(list<int>& num, int one, list<int>& result); //模拟竖式计算多位乘以一位/*计算存储在链表L-H中的两个数的和,结果存储在num1*/
void add(list<int>& num1, list<int>& num2); //模拟竖式计算两个数相加

完成快速幂计算后,程序的主体已基本完成,最后的步骤就是根据题目要求格式化输出,即去掉前导与末尾的0。此处实现格式化时得出最后结果,对结果进行操作。也可以直接对原数据进行去零操作。对于程序运行时间来说,后者效果更好。

源代码

~~~Code~~~

C++ 11新标准实现POJ No.1001-Exponentiation的更多相关文章

  1. C++ 11新标准实现POJ No.1002-487-3279

    487-3279(重复的电话号码查询)(标签:优先队列,哈希表) 题目描述 企业喜欢用容易被记住的电话号码.让电话号码容易被记住的一个办法是将它写成一个容易记住的单词或者短语.例如,你需要给滑铁卢大学 ...

  2. C++ 11新标准实现POJ No.2195-GoingHome

    Going Home(回家)(标签:二部图,匈牙利算法,KM算法) 题目描述 在网格地图上,有n个男人和n个房屋. 在每个单位时间内,每个小人都可以水平或垂直移动一个单位步长到相邻的点. 对于每个小人 ...

  3. C++11新标准学习

    <深入理解C++11:C++11新特性解析与应用> <华章科技:深入理解C++11:C++11新特性解析与应用>一共8章:第1章从设计思维和应用范畴两个维度对C++11新标准中 ...

  4. C++11新标准:nullptr关键字

    一.nullptr的意义 1.NULL在C中的定义 #define NULL (void*)0 2.NULL在C++中的定义 #ifndef NULL #ifdef __cplusplus #defi ...

  5. C++11新标准:decltype关键字

    一.decltype意义 有时我们希望从表达式的类型推断出要定义的变量类型,但是不想用该表达式的值初始化变量(如果要初始化就用auto了).为了满足这一需求,C++11新标准引入了decltype类型 ...

  6. C++11新标准:auto关键字

    一.auto意义 编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚地知道表达式的类型,然后要做到这一点并非那么容易.为了解决这个问题,C++11新标准引入了auto类型说明符,用它就能 ...

  7. c++11新标准for循环和lambda表达式

    :first-child { margin-top: 0px; } .markdown-preview:not([data-use-github-style]) h1, .markdown-previ ...

  8. 关注C++细节——C++11新标准之decltype的使用注意

    c++11新特性--decltype decltype是C++11加入的一个新的keyword,目的是选择并返回操作数的数据类型,重要的是,在此过程中编译器分析表达式并得到它的类型,却不实际计算表达式 ...

  9. 基于c++11新标准开发一个支持多线程高并发的网络库

    背景 新的c++11标准出后,c++语法得到了非常多的扩展,比起以往不论什么时候都要灵活和高效,提高了程序编码的效率,为软件开发者节省了不少的时间. 之前我也写过基于ACE的网络server框架,但A ...

随机推荐

  1. 2019.10.15 CSP初赛知识点整理

    初赛需要的知识点整理如下: (1)计算机的硬件组成与基本常识 (2)单位/进制的转换 (3)进制/逻辑运算相关 (4)概率与期望 (5)排序的各种性质 (6)简单数据结构的使用(栈.队列.链表等) ( ...

  2. QR 码详解(下)

    快速响应矩阵码(下) 书接上回,继续下半场. 纠错码 QR 码采用纠错算法生成一系列纠错码字,添加在数据码字序列之后,使得符号可以在遇到损坏时可以恢复.这就是为什么二维码即使有残缺也可以扫出来.没有残 ...

  3. InfluxDB从原理到实战 - InfluxDB常用的基础操作

    0x00 基础操作介绍 在本文中将介绍InfluxDB常用的基础操作,帮助读者建立对InfluxDB的感性认识,快速的动手玩起来,持续查询(Continuous Queies).Group by.Se ...

  4. icon font在sketch中的下载与安装

    icon font的下载安装: 1.首先打开sketch--插件--管理插件--获取插件--搜索 icon font--点击icon font--clone or download--下载的是一个sk ...

  5. Python玩转人工智能最火框架 TensorFlow应用实践 ☝☝☝

    Python玩转人工智能最火框架 TensorFlow应用实践 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 全民人工智能时代,不甘心只做一个旁观者,那就现在 ...

  6. Django中CKEditor富文本编译器的使用

    CKEditor富文本编辑器 1. 安装 pip install django-ckeditor 2. 添加应用 在INSTALLED_APPS中添加 INSTALLED_APPS = [ ... ' ...

  7. Mybatis入门简版(一)

    一.Mybatis介绍 MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动.创建connection.创 ...

  8. DRF框架中csrf异常

    一.报错信息 "detail": "CSRF Failed: CSRF cookie not set." 二.解决办法 方法一: 在配置文件中配置 REST_F ...

  9. formidable处理提交的表单或文件的简单介绍

    一般来说,客户端向服务端提交数据有GET和POST这两种方式,在之前的文章node.js当中的http模块与url模块的简单介绍当中我们可以知道通过req.url与url模块的配合处理可以快速得到客户 ...

  10. jar包的多层级maven依赖的坑与正确传递方法

    这个问题简述起来就是项目加载jar包但是无法加载jar包的依赖 这是一个maven的特性吗? 问题发生前 程序猿经常自己写一些库实现或收集常用的逻辑方法(算法和设计模式等等),以方便多个项目使用,避免 ...