大数相除算法
简介在实际的项目中,同事在移植一个算法时候碰到要进行64位整数的除法运算。找了一下一下,Linux内核中有支持该运算的函数 注:在以下公式以及代码中,名字的含义如下: 以下都使用 100/7 为例进行说明。 持续减法假设 m/n 的商为q,余数为r,那么可以得到这个公式: 比如100/7。 这是一个持续减法减去n的过程,直到满足 r < n 的条件,q就是商,也就是运算的次数。于是我们知道商为14,余数为2。这个算法的实现是 这个算法肯定是能够算出两个大数相除的结果,但是如果碰到 m 非常之大,n 又非常小的情况下,比如 m = 0xffffffffffffffff;n = 2,此时就悲剧了,总共要做 0x7fffffffffffffff 次减法。所以说这个算法不一定是高效的。 利用二进制性质持续减法前面是持续尝试 7*1,7*2, 7*3, … , 7*14做,但这样子效率比较低,我们能不能尝试换一种乘法。即: 二分法将除法操作转换为乘法操作。 7 * 100 = 700 > 100 只要满足当二分到为 1 的时候,且最后一次的结果大于 m,倒数第二次的结果小于 m,到此就要退出循环。 这个算法实际上不能称为大数的除法,因为这里面使用的是乘法操作。当 m 和 n 同时都是很大的数的话,比如 m 和 n 都是64位整数,那么两者相乘的结果肯定超过64位了。所以这个算法只适合较小的数进行相除。之所以列举在这边,是看重其运算的效率高。这个算法的实现是 实现代码/* 三个函数 div1()/div2()/div3() 的三个参数分别为: m : 被除数 n : 除数 *remain : 余数 函数的返回值为商。 注:测试代码中没有验证参数 n 为 0 值的异常情况。 */
#include <stdio.h>
typedef unsigned long uint64_t;
typedef long int64_t;
uint64_t div1(uint64_t m,uint64_t n,uint64_t *remain)
{
uint64_t quot = 0;
uint64_t value = 0;
if(m < n)
{
quot = 0;
*remain = m;
}
else if(m == n)
{
quot = 1;
*remain = 0;
}
else
{
value = m-n;
quot = 1;
while(value >= n)
{
quot++;
m = value;
value = m-n;
}
}
*remain = value;
return quot;
}
uint64_t div2(uint64_t m,uint64_t *remain)
{
uint64_t quot = 0;
uint64_t value = m;
uint64_t multi = 1;
if(m < n)
{
quot = 0;
*remain = m;
}
else if(m == n)
{
quot = 1;
*remain = 0;
}
else
{
while(value >= n)
{
multi = 1;
while((n*multi) <= (value>>1))
{
multi <<= 1; // 持续乘 2
}
quot += multi;
value -= (n*multi);
}
*remain = value;
}
return quot;
}
uint64_t div3(uint64_t m,uint64_t *remain)
{
uint64_t quot = 0;
uint64_t value = (m>>1);
while(1)
{
if((n*(quot+value)) == m)
{
quot = quot+value;
*remain = 0;
}
else if((n*(quot+value)) > m)
{
if(value == 1)
break;
else
value >>= 1;
}
else
{
quot += value;
if(value != 1)
value >>= 1;
}
}
*remain = m-(n*quot);
return quot;
}
// 测试代码
int main(void)
{
uint64_t m[4] = {0xffffffffffffffff,261535774000000000,60253400000,4LL};
uint64_t n[4] = {0xfffffffffffffffe,6912470141400,50698765,2LL};
int64_t quot = 0; // 结果
uint64_t remainder = 0; // 余数
int flag = 0; // 标记符号
int i = 0;
for(i=1; i<4; i++)
{
// 判断正负符号
if(m[i] < 0)
{
m[i] = -m[i];
flag = 1;
}
if(n[i] < 0)
{
n[i] = -n[i];
flag ^= 1;
}
quot = div1(m[i],n[i],&remainder); // 修改调用的函数名验证其他的函数
if(flag)
quot = -quot;
printf("%d: %lld / %lld = %lld.....%lldn",i,m[i],quot,remainder);
quot = 0;
remainder = 0;
}
return 0;
}
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |