您的位置:首页 > 其它

北大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次方数,至少在这个算法看来是这样。)。

矩阵的应用,是对大大相乘的削减,取代为小乘小加。看图:

95123
9814591827
5452551015
195123
21810246
32715369
951232 = 9048385129

乘法的本质是什么呢?加法?前一步。于是你发现了:

95123
9814591827
5452551015
195123
21810246
32715369
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();

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: