加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

大数的加减乘除、取对数、求次方、进制转换、三角函数的原理

发布时间:2020-12-14 04:03:38 所属栏目:大数据 来源:网络整理
导读:以下是大数运算的算法,没有经过科学论证,也没有参考算法书,只是自己想的,如果你有更快的算法,请也给我一份,让我参考一下. 说明: 1、以下说的大数运算均是针对是大自然数的运算,至于负数大数的运算,我想只要实现了正数大数的运算,负数的大数的运算应该不难吧.

以下是大数运算的算法,没有经过科学论证,也没有参考算法书,只是自己想的,如果你有更快的算法,请也给我一份,让我参考一下.

说明:

1、以下说的大数运算均是针对是大自然数的运算,至于负数大数的运算,我想只要实现了正数大数的运算,负数的大数的运算应该不难吧.

2、第1点已经说了,这是针对整数的大数的运算,对于有小数的情况,请注意移位操作.例如3.5*4.5在大数里的运算是35*45,在最后的结果里移动2个小数点即可,而对于3.5/4.55则在大数里的运算应该是350/455,最后结果就不用处理小数点了.所以,如何处理小数点的问题,我想应该不难吧.如果处理小数点的问题,你都显得很困难,请你不用再看此文章了.

3、我们知道在VB.NET里,一个Long(Int64)类型的数据可以存储的最大整数是2^62=4611686018427387904,而2^31=2147483648即10^10>2^31>10^9,为了进行大数的运算,我们得充分利用Long给我们的存储空间,我们这里选择10^9为基,为什么我们这里不是用10^10或者更大的数据为基呢,那是因为2个10^10以内的数据相乘,结果可能超过Long类型存储空间的2^62,而为什么不选择2^8或者更小的数据为基呢,因为我们这里得充分利用Long类型的存储空间,而10^9为基就正好适合我们的选择.之前在网上看到有很多人进行大数运算时是1位1位取出来进行运算的,那相当于是以10^1为基进行运算,那是相当慢的(用汇编移位操作的除外,这篇文章不涉及汇编的知识,不涉及C语言里的位的操作的知识).所以下面讲解的算法都是以10^9次方为一个基进行讲解的.注意10^9表示9位数据.

4、为了对大数进行快速运算,我们这里以Long类型的一维数组来存储大数,其存储原理是数组根据下标由小到大依次存储大数的低位到高位,数组里的元素每次存储大数9(由第3点的10^9为基来的)位数据.例如1234567894,我们用一个叫a的Long数组来存储的结果是a(0)=234567894,a(1)=1.需要注意的是,对应字符串"00002"这种数据的处理的时候,必须把前面的4个0处理掉而变成"2"再进行存储

一、大数加法

由说明里的第4点,写出源代码应该不难吧.就是同位数的对应想加,(下标一样的2个数组对应的数据是同位数的),这里注意加的时候是否存在进位的情况,也就是说加的结果如果大于(10^9-1)则存在进位.

看VB.NET源代码:大数加法

二、大数减法

在进行大数减法时,最好先判断大数的大小.首先是数组长度的判断,数组越长,这个数据肯定越大.如果数组长度一样,每对应的数组元素进行比较,直到被减数不小于减数,这样大数减法还必须返回一个判断返回值是否是负数的标志,我们这里返回的是其绝对值.当正负判断好之后,和大数加法一样,就是同位数的对应相减,这里注意减的时候是否存在借位的情况.

看VB.NET源代码:大数减法

三、大数乘法

大数乘法原理很简单.用第一个大数的每9位数据(也就是一个数组元素)去分别乘另一个大数的每9位数据(也就是一个数组元素),然后把结果存储到对应的位数(数组元素)里去.这里注意进位的问题.

这里以一个例子来讲解其原理:300000000|000000004*600000000|000000007(这里用|主要是加以区分)则大数运算时,首先我们初始化一个返回值内存ret=0,先进行4*7=28,ret=ret+28=28,然后用4*600000000=2|400000000,注意这里,因为600000000是存储在数组的第二个元素,也就是说这个数据是被缩小了(2-1)*10^9,也就是说那准确的值是2|400000000|000000000,则ret=ret+2|400000000|000000000=2|400000000|000000028,下面就是300000000*7=2|100000000,由于300000000原来是在数组的第2个元素,其也是被缩小了(2-1)*10^9,也就是说其准确值应该是2|100000000|000000000,则其加上ret后得ret=4|500000000|000000028……

其实上面例子看起来很复杂,但是用数组操作是很快的,因为要对应位我们只对应下标就足够了.

看VB.NET源代码:大数乘法

四、大数除法与求余

1、除法

( 大数的除法应该在大数的基本运算中算是最困难的了. 之前在网上看介绍,说的是大数的除法用大数的减法来实现,对于本文这种大数的算法格式,用大数减法来实现效率是相当慢的,因此我追求的是另一种方法)

首先我们这里考虑的大数除法是除数是不大于被除数的情况,如果除数大于被除数的除法,请先把被除数扩大N倍,然后再进行下面的运算,最后在结果里移动小数点即可.例如3/600,我们可以用30000/600=50,对结果向左移动4(3变成30000相当于3*10^4)个小数点就变成0.005

1、除数的位数不大于9时(代表大数的数组个数为1),被除数按高位依次对除数做除法,注意余数要加到下一次计算当中去.这个很简单

2、如果除数位数大于9位时(代表大数数组个数大于1),为了加快运算速度,把除数的位数扩大,扩大的其位数刚好为n*9位为止,扩大的同时注意被除数是否需要扩大,这个自己考虑即可,例如除数为1234567891,扩大后变成123456789|100000000.

2.1、假如除数的最高的9位数(数组的最后一个数据)为D,则用被除数除以(D+1),如例子的(123456789+1)得到一个商,然后用这个商乘以我们的除数,用被除数减去这个乘积得到一个余数,判断余数是否小于除数,小于就循环2.1的操作,最后把所有的商加起来即得结果(只要我们的商越接近我们的真实商,我们计算的成本也越小,所以我们选择除以D+1,而不是除以D+2或D+3……).

2.2这里需要注意的一个问题是:上面原理其实是试商法,但是试商的时候怎么才能达到最佳状态,其实也就是上面第2点说的扩大除数位数的问题.在上面的2.1里为什么我们除的是D+1而不是D或者D+2.我们假如设被除数为A,除数为B,因为B>D,所以A/D>A/B>A/(D+1)注意这里的D+1代表的是D最低位+1,如果是除以D的话就无法进行2.1的循环求值,而是D+2的话,其速度没有D+1那么快

看VB.NET源代码:大数除法


2、求余

其实上面的除法已经可以求余了。但是对于a^b%c这种情形的求余,建议使用大数求余这种方法


五、大数运算

大数加、减、乘、除、余数、公倍数、公约数、进制转换目录

六、大数取自然对数的算法,即我们如何快速算log(x)这类问题

(1)假设大数很大

其实这个问题很简单,先求大数以10为底取对数,然后在结果乘以Log(10)即得我们想要的结果.为什么是以10为底而不是其它值,因为以10为底只是移动小数点的问题,这个用计算机来实现很简单.这里需要理解的是log10(123456789.123456789)与log10(123456789.123456789123456)、log10(123456789.1234567894654564654)、log10(123456789.1234567893456343453453353453534534534535345)……这些结果几乎是一样的

具体步骤:

首先,比如A为一个大数,注意A可以是小数,但必须大于0,求log(A)时,先把A分成2部分A1与A2,其中A1代表A的整数部分,A2代表A的小数部分.

然后,我们先判断A1的长度:

1、如果A1长度小于18位,则我们直接返回VB.NET提供给我们的数学函数进行运算,即Math.Log(CDbl(A))即得我们的结果.

2、A1的长度大于17位时,我们利用移位操作计算Log10(A)的值.这里我们设A1的长度为n,则我们取A1的后n-9位数据加到A2上面去,A1本身只要A1其前9个数据,即A2=Mid(A1,10,n-9),A1=Mid(A1,1,9)那么大数的Log10(A)=n-9+Math.Log10(CDbl(A1+"."+A2));那么最后Log(A)=Log10(A)*Log(10)

(2)假设大数很小

这个原理和(1)的原理差不多,(1)是把小数点移位数相加,这里是相减即可.例如Log(0.000000000000000000000000000000123)等于多少的问题,那么我们求解的算法是Log(0.000000000000000000000000000000123)=log(10)*(log10(123)-33)=-71.173123713431


其实根据上面的方法,要算任意数为底的取大数的对数,方法就很简单了.需要注意的是,这样得到的结果只是近似值,而非准确值。

看VB.NE源代码:大数取对数


(3)参看另一种理论上可以精确到任意位数的取对数算法.

《大数求自然对数数值算法》


七、大数求任意次方


7.1:任意次方的n可为实数的时候


很多人写大数求n次方的n,不是限制n是自然数,就是限制n是整数,那我们有没有办法让n是小数呢?如对一个大数开平方,则n为0.5这类问题的求解.

我们这里只讨论大数进行正数次方的时候的情形,对n是负数我想只要把n是正数的问题解决了,n是负数很简单了吧.这个留给你们自己思考!还有这里的大数是整数,请看文章开头的1、2点,有小数的情形是很容易解决的,这里不再多说.

原理:

1、设大数为a,我们这里求a^n的值,我们把n分解成n=n1+n2,n1是n的整数部分,n2是n的小数部分;例子n=12.368则n1=12,n2=0.368

2、则a^n=a^n1*a^n2,这个地方a^n1是很容易求到的(求大数的整数次方),这里不再多说,即我们现在的问题是如何求a^n2的值

3、根据公式a^n2=10^[log10(a^n2)]=10^[n2*log10(a)];这里设b=log10(a),根据上面的第六点,b是很容易求到的.即我们现在有a^n2=10^(n2*b),由大多数情况下n2*b的值都在我们的Double类型的可控范围之下,这里我们直接利用VB.NET提供给我们的函数Math.Pow(10,n2*b)即可得到a^n2的值,最后把a^n2的值带回第2点即可得到我们想要的解.

4、需要说明一点的是,这种算法算然简单,速度快,但它是一种取近似的算法,即结果一般只能保证前面几位数的准确性

看下面的一个编程计算的结果

1234567893698521477412369851.23698547123695^0.5=35136418339074.3

然而35136418339074.3^2=1234567893698436790686180920.49

当然如果是计算整数次方,结果想多精确就会多精确.

5、当然,在上面第3点的a^n2的数据的处理中,我们可以直接截取a的值来获得一个近似值b(b≈a^n2).例如a=123456789123456789123456789,则我们把a≈123456789×10^18来求解,则a^n2≈123456789^n2×10^(18×n2)后面的式子在VB.NET里很容易就可以用其提供的数学函数进行运算.然后把a^n2的值带回第2点即可得到我们想要的解.

看VB.NET源代码:大数求任意次方


7.2:任意次方的n可为指定数据的时候
1、当n为小数且反应为开方数的时候,比如对一个大数开4次方,这个时候n就是0.25这类问题。
针对一个大数开n次方,我所知道的就是网上的手工开n次方的算法.具体的算法摘录如下:转载来源
例子:对数据a= 1234567893698521477412369851开n(我们这里假如n=5)次方
1.1、把a由由到左每n位划分为一个区域(每个区域将产生一位开次方后的数据),例如上面划分后如下
123 45678 93698 52147 74123 69851
1.2、对a划分区域从最高位所在区域开始,对最高位区域对其开n次方得到整数位b,如上则b=pow(123,1/5)
假如我们要返回的开方得到数为x,则令x=b
1.3、然后我们求现在最高位区域产生的余数,我们设余数为y,则y=123-b^n
1.4、然后我们把余数与接下来那个区域的数合并,我们这个时候设合并的数为z,则z=y*10^n+ 45678(注意对这里的10^n次方的理解,因为y是比 45678高n位的数据里面 )
1.5、 然后计算t1=(10*x)^n,t2=t1+z,现在令i=9
1.6、计算t3=(10*x+i)^n,如果t3>t2,则i减1,即i=i-1,跳回到1.6步继续计算;否则进入下一步
1.7、1.6步得到的i就是下一位开次方数,则我们现在把其加到x上,则表达式为x=x*10+i,然后求得余数y=t2-t3,如果数据a的位数没有算完,转入1.4步进入下一个区域进行循环计算.否则打印输出x
需要说明的是,上面的1.5与1.6步,我思考的和网上参考的是不一样的,因为这个开方计算基本是涉及到大数的运算了,由于大数计算当中尽量避免除法运算是很有必要的,所以我没有采用用除法得到试商i,而是直接采用1到9进行尝试的办法,这样速度会更快.呵呵!
注意:上面是针对数据a是整数的情形,如果a包含小数,如对 64346346.1223154232开5次方,如果我们需要把计算结果保留到小数点后np位,则我们先把小数部分单独拿出来划分np个区域末位不足的补0,然后再把小数部分和整数部分合并,然后对这个组合后的数据按照上面的步骤求得开方结果,然后再移动np位小数点即可。比如 64346346.12231542324开5次方保留小数点后3位,则我们处理如下
原数据:643 46346.12231 54232 4
新数据:643 46346 12231 54232 40000
即我们对数据 64346346122315423240000进行开5次方后得36450,小数点向左移动3位后变成36.450即我们想要的结果.
看VB.NET源代码: 手工大数开n次方
2、当n=0.5的时候的另一种算法.

相当于是对大数进行开方运算.这个是直接使用数值分析书上的算法.由于日志中的原理已经说得很清楚,这里不再重复.但是对数据的开方,还是建议使用第1点的方法,毕竟那个是很准确的。

看VB.NET源代码: 大数开方运算BigNumberSqrt
注意:对于上面的算法,当次方数不是整数的时候,得到的结果的有效位数很少(主要是7.1的算法),在这里建议使用《高精度求任意次方》中的算法,其理论上可以精确到任意位.


八、10进制大数与16进制大数互转的问题

1、10进制大数转16进制大数.

例如请把10进制的数据1234562345234545436545345664346564346346.1223154232155234465234423142233142341转换成16进制表示的数据(结果保留到小数点后90位其值大概是3A0C80F136A7DB8944217AAAEE1E041EA.1F50104681CF7E8CF40104C5A256682E58027824EEAE83546D9966732FFA04FBABB48F85DB4CFBF9BFA74B0081E).假如我们把上面的数据表示成A.B,A表示10进制数据的整数部分(如上面小数点前的数据A=1234562345234545436545345664346564346346),B表示10进制的小数部分(如上面小数点后的数据B=1223154232155234465234423142233142341).

(1)、10进制转16进制对于整数部分A就是辗转相除法,我们一般的方法都是除16来一位一位的获取,我们这里由于是大数的运算,我们除的是16^7(我们这里为什么用16^7,请仔细想一下,注意16^7=268435456),然后对每次除得到的余数代表的是7位的16进制数据.

(2)、10进制转16进制小数部分B是不停的乘16,通过进位来一位一位的得到小数点后的数据,我们不这样干,同样,我们每次乘16^7,也就是说每乘1次必定产生7位小数点后的数据,这个数据可以通过比较B乘之前的位数与乘得的结果的位数来截取那些位,进而转换成16进制数据.

(3)、对于上面的(1)(2)点注意不足7位时高位补0的问题.

看VB.NET源代码:10进制转16进制

2、16进制大数转10进制大数

和10进制转16进制一样,这个也得把16进制分成整数部分与小数部分.

(1)对于整数部分的转换.

把16进制从低位到高位每7位划分为一个区域,对每个区域转换成10进制(这个很容易就可以办到),然后从最低区域开始每个乘16^7,每上升一个区域均多乘一次16^7,这样最后把每个区域的值用大数加法加起来即得我们想要的结果.注意,当中如何尽量少的进行乘法的运算,这里不再多说.

(2)对于小数部分的转换.

在大数运算中,尽量避免除法运算是一种较好的选择.本方法就只需要进行一次除法即得我们想要的结果.假如我们的小数部分为B,这里注意,请把B变成位数为7n的数据.例如B=1234567FE那么变换后成为B=1234567FE00000,小数点后数据末尾添加0不影响数值大小.那么按照第(1)点对于整数部分的转换,假如把B转换成了一个大整数BB,那么我们这个时候小数部分的精确值=BB/(16^7)^n,现在再利用大数精确的除法来获得我们想要的结果是轻而易举的事情.

3、同样的道理,如果10进制与2、8、32进制进行转换时,先把2、8、32转换成16进制,然后再通过16进制与10进制的转换方法获得转换结果,最后再代入相应的结果里面即可.2、8、32进制与16进制的转换是不会丢失数据的转换关系,其转换方法非常简单,这里不再多说.


九、求高精度的三角函数sin、cos、tan、cot、asin、acos、atan、acot

通过上面的我们已经可以对任意数据的,进行常规的加、减、乘、除、次方、取对数,那么求双曲函数已经不成问题,在我们常见的数学运算当中,就只剩下sin与cos函数了,其它的三角函数可以由他们推出来,即我们这里主要讨论大数级别的正弦函数与余弦函数的求解。

原理:《求任意精度的正弦函数值》、《求任意精度的余弦函数值》、《高精度求反正弦、反余弦、反正切、反余切数值算法


十、求高精度(反)双曲函数值sinh、cosh、tanh、coth、asinh、acosh、atanh、acoth

详情:《大数级别(反)双曲函数计算》


总结:上面的文字写得有点乱,但是总算把大数在初等数学领域内的计算问题解决.剩下的事情就是查找更好的算法了.在这里拭目以待吧!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读