北大ACM 1001,关于高精度数的一些想法:
2012-08-20 18:07
447 查看
来自北京大学ACM的一道题目:http://poj.org/problem?id=1001&lang=zh-CN&change=true。
对于题目中的高精度幂,现成的应该没有哪一种数据类型可以直接进行运算(ps:好像Java有BigInteger 这种类可以,往后再多了解这个)。那该怎么办呢?
大一那时候的想法是分段而行。怎么样分?例如 (1111111111)2 ,可以转换为(11111*105+11111)2之流。但后来觉得不太可行,因为这会涉及到很多的10n,需要很多的标记,似乎也不好操控,特别是分得很细的时候。第二个想法是数组。但是遇到的问题还是从前一个想法继承下来了。后来也慢慢断绪了。
现在的这个想法的产生,应该说很大程度是归结于编程和语言操控能力的提高。因为算法的实现是基于很简单的道理。
1. 还是那样,大事化小:是分段。不过不再是基数,而是平方次数。
2. 乘法转加法:基于数组的矩阵使用,摒弃10n的实现。
现在单举整数为例。因为整数明白了,小数也只是多点细节的东西而已。
例如 (95123)12,ACM题目上的第一个数。
首先,把12分解。为什么?(95123)12 = ((951232 *
95123)2)2 =(95123)1+1+1+3+6 。
目的很简单,就是构造尽可能多的2次方次数。至于为什么要构造2次方呢,是因为2次方下,乘数于被乘数相等,可以省许多工作(你只需要记得当前的次方数,和原本的1次方数,至少在这个算法看来是这样。)。
矩阵的应用,是对大大相乘的削减,取代为小乘小加。看图:
951232 = 9048385129
乘法的本质是什么呢?加法?前一步。于是你发现了:
9,是951232的个位数。
6+6,是951232的十位数,减去进位的10,也就是2。
…
…
最后求出第一次的平方和。
这就是该算法的核心了。由此不断累积,得到更高阶的幂。往后的工作,也就是重复这种运算,实践这种思想。
该算法好不好?
以作者的身份来说,它的原理很坦白。而在时空复杂度的问题上,设计到大数组的操作,for循环的大量使用,时空复杂度应该不小,有待进一步优化。
(注:另外,short类型的最大值为32767,在最大斜线上32767/81=404。 也就是说,最大支持的数不能大于999..9(404个9)。)
后记:
很多时候吧,得出一个想法也许不是相像那么难。因为随着社会阅历的增加,更方面知识的扩展,只要注重积累,每个难题都有可能会找到它的原型。而也是很多时候,解决这些有想法的问题时,做起来可还挺费劲的。所以还真要多实践实践。(ps:多积累思想,多实践技术)
附上 实现源码(Java实现):
public class HighAccuracy
{
private byte n; //
times of square
short[] each; //
to store each short in inputNum
private short dot =
0; // original location of dot
public HighAccuracy(String
num, byte n){
this.n =
n;
getOriginalList_From_InputNum(throwUselessZero(num));
}
public void showResult(){
short[][]
matrix = this.toGet_Matrix();
short[]
shorts = this.getMatrixToShortList(matrix);
for(int index=0;
index<shorts.length; index++){ //检索 小数点dot 的位置
if(index==0
&& shorts[0] == 0){
System.out.print(".");continue;
}
if(shorts[0]!=0
&& index==(shorts.length-this.dot*this.n))
System.out.print(".");
System.out.print(shorts[index]);
}
System.out.println();
}
private String
throwUselessZero(String inputNum){
if(inputNum.charAt(inputNum.length()-1)=='0'){
int i=0;
while(inputNum.charAt(inputNum.length()-(++i))=='0');
if(inputNum.charAt(inputNum.length()-(i+1))=='.')
i++;
return inputNum.substring(0,
inputNum.length()-i+1);
}
return inputNum;
}
private void getOriginalList_From_InputNum(String
inputNum){
char[]
eachChar = inputNum.toCharArray();
if(inputNum.contains("."))
each = new short[inputNum.length()-1];
else
each = new short[inputNum.length()];
for(int i=0,j=0;
i<inputNum.length(); i++){
if(eachChar[i]
!= '.'){
each[j++]
= (short)(eachChar[i]-48);
}else{
dot =
(short)((inputNum.length()-1) -
i); //to get the dot's location
}
}
}
private byte[]
get_n_list(byte t){
byte[]
list = new byte[8];
int i
= 0;
while(
t>=1 ){
if(t%2
== 1){
list[i++] = 1;
t-=1;
}
if((t
= (byte)(t/2))== 1){
list[i++] = 1;
list[i] = 1;
}else{
if(t
!= 0)
list[i++] = t;
}
}
return list;
}
private short[][]
toGet_Matrix(){
short[][]
matrix = null;
short[]
previous = each;
short[]
current = each;
byte[]
nbytes = this.get_n_list(this.n);
for(int i=nbytes.length-1;
i>=0; i--){ // 根据 前一矩阵和序列 和当前矩阵和序列 得到 当前矩阵
//
previous 指向 当前矩阵 得出的求和序列
//
current 指向 下一个矩阵和序列, 根据 nbytes 得出
if(nbytes[i]
!= 0){ //reach the end of nList
if(nbytes[i+1]==0)i--;
matrix = new short[previous.length][current.length];
System.out.println();
for(int a=0;
a<matrix.length; a++){
for(int j=0;j<matrix[0].length;j++){
matrix[a][j] = (short)(previous[a]*current[j]);
}
}
if(i
!= 0){
previous = this.getMatrixToShortList(matrix);
if(nbytes[i-1]
== 1){
current = each;
}else{
current = previous;
}
}
}
}
return matrix;
}
private short[]
Once_Matrix_Sum(short[][] matrix_parameter){
int matrix_x
= matrix_parameter.length;
int matrix_y
= matrix_parameter[0].length;
short[]
matrix_sum = new short[matrix_x+matrix_y-1];
// 各位(个十百千万...)求和: 矩阵 反斜 求和
// 大乘 转 小加 运算
for(int index=0;
index<matrix_sum.length ; index++){
matrix_sum[index] = 0;
int moveStep_x
= index+1;
int moveStep_y
= 1;
int xx
= matrix_x-moveStep_x;
if(xx<0){
xx=0;
moveStep_y = index+2-matrix_x;
}
for(
; moveStep_y<=(index+1) && moveStep_y<=matrix_y; moveStep_x--,moveStep_y++ ){
if(moveStep_x
> 0){
matrix_sum[index] += matrix_parameter[xx++][matrix_y-moveStep_y];
}
}
}
return matrix_sum;
}
private short[]
getMatrixToShortList(short[][] matrix_parameter){ //
get the correct short list
short[]
matrix_sum = Once_Matrix_Sum(matrix_parameter);
for(int i=0;
i<matrix_sum.length-1; i++){ //最高位除外,其他位置 向上进位
if(matrix_sum[i]>9){
matrix_sum[i+1] += (short)((matrix_sum[i]/10));
matrix_sum[i] = (short)((matrix_sum[i]));
}
}
short[]
matrix_sum_;
int index;
if(matrix_sum[matrix_sum.length-1]
> 9){ // 判断最高位 是否有进位产生
matrix_sum_ = new short[matrix_sum.length+1]; // 由此 判断 创建的 short[] 数组是否长度+1
matrix_sum_[0] = (short)(matrix_sum[matrix_sum.length-1]/10);
matrix_sum_[1] = (short)(matrix_sum[matrix_sum.length-1]);
index = 2;
}else{
matrix_sum_ = new short[matrix_sum.length];
matrix_sum_[0] = matrix_sum[matrix_sum.length-1];
index = 1;
}
// 等到 真正的求和序列
for(int i=1;
i<matrix_sum.length; i++,index++){
matrix_sum_[index] = matrix_sum[matrix_sum.length-1-i];
}
return matrix_sum_;
}
public static void main(String[]
args) {
// TODO Auto-generated
method stub
String numStr = "95.123";
byte times
= (byte)12;
HighAccuracy ha = new HighAccuracy(numStr,
times);
ha.showResult();
}
}
对于题目中的高精度幂,现成的应该没有哪一种数据类型可以直接进行运算(ps:好像Java有BigInteger 这种类可以,往后再多了解这个)。那该怎么办呢?
大一那时候的想法是分段而行。怎么样分?例如 (1111111111)2 ,可以转换为(11111*105+11111)2之流。但后来觉得不太可行,因为这会涉及到很多的10n,需要很多的标记,似乎也不好操控,特别是分得很细的时候。第二个想法是数组。但是遇到的问题还是从前一个想法继承下来了。后来也慢慢断绪了。
现在的这个想法的产生,应该说很大程度是归结于编程和语言操控能力的提高。因为算法的实现是基于很简单的道理。
1. 还是那样,大事化小:是分段。不过不再是基数,而是平方次数。
2. 乘法转加法:基于数组的矩阵使用,摒弃10n的实现。
现在单举整数为例。因为整数明白了,小数也只是多点细节的东西而已。
例如 (95123)12,ACM题目上的第一个数。
首先,把12分解。为什么?(95123)12 = ((951232 *
95123)2)2 =(95123)1+1+1+3+6 。
目的很简单,就是构造尽可能多的2次方次数。至于为什么要构造2次方呢,是因为2次方下,乘数于被乘数相等,可以省许多工作(你只需要记得当前的次方数,和原本的1次方数,至少在这个算法看来是这样。)。
矩阵的应用,是对大大相乘的削减,取代为小乘小加。看图:
9 | 5 | 1 | 2 | 3 | |
9 | 81 | 45 | 9 | 18 | 27 |
5 | 45 | 25 | 5 | 10 | 15 |
1 | 9 | 5 | 1 | 2 | 3 |
2 | 18 | 10 | 2 | 4 | 6 |
3 | 27 | 15 | 3 | 6 | 9 |
乘法的本质是什么呢?加法?前一步。于是你发现了:
9 | 5 | 1 | 2 | 3 | |
9 | 81 | 45 | 9 | 18 | 27 |
5 | 45 | 25 | 5 | 10 | 15 |
1 | 9 | 5 | 1 | 2 | 3 |
2 | 18 | 10 | 2 | 4 | 6 |
3 | 27 | 15 | 3 | 6 | 9 |
6+6,是951232的十位数,减去进位的10,也就是2。
…
…
最后求出第一次的平方和。
这就是该算法的核心了。由此不断累积,得到更高阶的幂。往后的工作,也就是重复这种运算,实践这种思想。
该算法好不好?
以作者的身份来说,它的原理很坦白。而在时空复杂度的问题上,设计到大数组的操作,for循环的大量使用,时空复杂度应该不小,有待进一步优化。
(注:另外,short类型的最大值为32767,在最大斜线上32767/81=404。 也就是说,最大支持的数不能大于999..9(404个9)。)
后记:
很多时候吧,得出一个想法也许不是相像那么难。因为随着社会阅历的增加,更方面知识的扩展,只要注重积累,每个难题都有可能会找到它的原型。而也是很多时候,解决这些有想法的问题时,做起来可还挺费劲的。所以还真要多实践实践。(ps:多积累思想,多实践技术)
附上 实现源码(Java实现):
public class HighAccuracy
{
private byte n; //
times of square
short[] each; //
to store each short in inputNum
private short dot =
0; // original location of dot
public HighAccuracy(String
num, byte n){
this.n =
n;
getOriginalList_From_InputNum(throwUselessZero(num));
}
public void showResult(){
short[][]
matrix = this.toGet_Matrix();
short[]
shorts = this.getMatrixToShortList(matrix);
for(int index=0;
index<shorts.length; index++){ //检索 小数点dot 的位置
if(index==0
&& shorts[0] == 0){
System.out.print(".");continue;
}
if(shorts[0]!=0
&& index==(shorts.length-this.dot*this.n))
System.out.print(".");
System.out.print(shorts[index]);
}
System.out.println();
}
private String
throwUselessZero(String inputNum){
if(inputNum.charAt(inputNum.length()-1)=='0'){
int i=0;
while(inputNum.charAt(inputNum.length()-(++i))=='0');
if(inputNum.charAt(inputNum.length()-(i+1))=='.')
i++;
return inputNum.substring(0,
inputNum.length()-i+1);
}
return inputNum;
}
private void getOriginalList_From_InputNum(String
inputNum){
char[]
eachChar = inputNum.toCharArray();
if(inputNum.contains("."))
each = new short[inputNum.length()-1];
else
each = new short[inputNum.length()];
for(int i=0,j=0;
i<inputNum.length(); i++){
if(eachChar[i]
!= '.'){
each[j++]
= (short)(eachChar[i]-48);
}else{
dot =
(short)((inputNum.length()-1) -
i); //to get the dot's location
}
}
}
private byte[]
get_n_list(byte t){
byte[]
list = new byte[8];
int i
= 0;
while(
t>=1 ){
if(t%2
== 1){
list[i++] = 1;
t-=1;
}
if((t
= (byte)(t/2))== 1){
list[i++] = 1;
list[i] = 1;
}else{
if(t
!= 0)
list[i++] = t;
}
}
return list;
}
private short[][]
toGet_Matrix(){
short[][]
matrix = null;
short[]
previous = each;
short[]
current = each;
byte[]
nbytes = this.get_n_list(this.n);
for(int i=nbytes.length-1;
i>=0; i--){ // 根据 前一矩阵和序列 和当前矩阵和序列 得到 当前矩阵
//
previous 指向 当前矩阵 得出的求和序列
//
current 指向 下一个矩阵和序列, 根据 nbytes 得出
if(nbytes[i]
!= 0){ //reach the end of nList
if(nbytes[i+1]==0)i--;
matrix = new short[previous.length][current.length];
System.out.println();
for(int a=0;
a<matrix.length; a++){
for(int j=0;j<matrix[0].length;j++){
matrix[a][j] = (short)(previous[a]*current[j]);
}
}
if(i
!= 0){
previous = this.getMatrixToShortList(matrix);
if(nbytes[i-1]
== 1){
current = each;
}else{
current = previous;
}
}
}
}
return matrix;
}
private short[]
Once_Matrix_Sum(short[][] matrix_parameter){
int matrix_x
= matrix_parameter.length;
int matrix_y
= matrix_parameter[0].length;
short[]
matrix_sum = new short[matrix_x+matrix_y-1];
// 各位(个十百千万...)求和: 矩阵 反斜 求和
// 大乘 转 小加 运算
for(int index=0;
index<matrix_sum.length ; index++){
matrix_sum[index] = 0;
int moveStep_x
= index+1;
int moveStep_y
= 1;
int xx
= matrix_x-moveStep_x;
if(xx<0){
xx=0;
moveStep_y = index+2-matrix_x;
}
for(
; moveStep_y<=(index+1) && moveStep_y<=matrix_y; moveStep_x--,moveStep_y++ ){
if(moveStep_x
> 0){
matrix_sum[index] += matrix_parameter[xx++][matrix_y-moveStep_y];
}
}
}
return matrix_sum;
}
private short[]
getMatrixToShortList(short[][] matrix_parameter){ //
get the correct short list
short[]
matrix_sum = Once_Matrix_Sum(matrix_parameter);
for(int i=0;
i<matrix_sum.length-1; i++){ //最高位除外,其他位置 向上进位
if(matrix_sum[i]>9){
matrix_sum[i+1] += (short)((matrix_sum[i]/10));
matrix_sum[i] = (short)((matrix_sum[i]));
}
}
short[]
matrix_sum_;
int index;
if(matrix_sum[matrix_sum.length-1]
> 9){ // 判断最高位 是否有进位产生
matrix_sum_ = new short[matrix_sum.length+1]; // 由此 判断 创建的 short[] 数组是否长度+1
matrix_sum_[0] = (short)(matrix_sum[matrix_sum.length-1]/10);
matrix_sum_[1] = (short)(matrix_sum[matrix_sum.length-1]);
index = 2;
}else{
matrix_sum_ = new short[matrix_sum.length];
matrix_sum_[0] = matrix_sum[matrix_sum.length-1];
index = 1;
}
// 等到 真正的求和序列
for(int i=1;
i<matrix_sum.length; i++,index++){
matrix_sum_[index] = matrix_sum[matrix_sum.length-1-i];
}
return matrix_sum_;
}
public static void main(String[]
args) {
// TODO Auto-generated
method stub
String numStr = "95.123";
byte times
= (byte)12;
HighAccuracy ha = new HighAccuracy(numStr,
times);
ha.showResult();
}
}
相关文章推荐
- 北大ACM作业练习 1001
- 关于CEGUI的一些想法
- 关于面向对象和设计模式的一些想法
- 关于REST的一些想法
- 关于利用python进行验证码识别的一些想法
- 关于监控系统的一些想法心得
- 关于 Apple Metal API 的一些想法
- 20130910 一些想法,关于项目中异常处理的解决方案,以及Elmah
- 关于Java虚拟机装载类的一些想法
- 关于asp.net服务器控件的一些想法
- 关于一些初级ACM竞赛题目的分析和题解(七)。
- 关于一些初级ACM竞赛题目的分析和题解(八)
- 关于mysql中enum数据字段类型的一些想法!!
- 关于Xcode提高读写速度遇到的问题和一些想法
- 北大acm 1611,2524解题报告--关于并查集
- 关于新书出版的一些想法
- 在ACM中Java关于大数的一些相关操作
- 关于接口、依赖、耦合,我的一些想法
- ACM 1001 Exponentiation 高精度幂浮点型的运算
- 关于电话营销的一些不太成熟的想法