liangbch
发表于 2017-1-16 14:59:14
不错。使用模算术的方法,代码简洁。
我的算法仍然是普通的除法运算,很难化简到这么简单。
gxqcn
发表于 2017-1-16 17:27:25
经过变换后,问题的核心转化为计算一个 \(56\) 比特的整数对 \(2*5^9\) 的余数(最后再附加一次与 \(5^9\) 的比较运算)。
在 30# 里的 Algo_3 中,从低位到高位分成了五段(\(24|8|8|8|8\)),分别对 \(2*5^9\) 同余,最后累加后取余数。
依此原理,还可以考虑再精简一下,分成四段:\(29|9|9|9\),
即低 \(29\) 比特直接累加,而后每 \(9\) 比特依次乘以 \(1714662\)、\(2906944\)、\(74078\) 再累加(易证明, \(\sum \leqslant 2 936 365 435 \lt 2^{32}\),在 \(32\) 位下无溢出),
虽然减少了一次乘法运算,但提取数据比之前的变困难了,需要更多的位运算。
只是呼吸
发表于 2017-2-1 11:35:35
受到各位高手的刺激,加上本人对大数运算的爱好,经过深入的思考,不断的摸索,终于得到了我自己比较满意的结果。
由于站长已经把路堵死了,本人只有把站长的题目改动一下:
只用4次32位乘法,求 uint64_t 对 10^9 的余数及商。
(这里,我就不管LED灯的问题了)
程序中用到了管理员 liangbch 在 http://bbs.emath.ac.cn/thread-9245-1-1.html 中提供的“规约”技术。
我用的是vc++2008, 在debug环境下测试通过。
以下是程序的源码
#include <stdio.h>
#include <windows.h>
_declspec(naked)
int div_mod(UINT32 *dst,UINT32 *scr)
{
_asm
{
push esi
push edi
push ebx
push ebp
mov esi,dword ptr
mov ebp,dword ptr
mov eax,1152921504///-----这是10^(-9)的高32位
mul dword ptr//64位的低位 乘 10^(-9)的高32位
mov edi,edx //乘积的结果,暂时保存在 edi ebx 中。
mov ebx,eax
mov eax,2606387915 //-------这是10^(-9)的低32位
mul dword ptr// 64位的高位 乘 10^(-9)的低32位
add ebx,eax
adc edi,edx //edi 将作为进位,ediebx存储结果
mov eax,1152921504//-----这是10^(-9)的高32位
mul dword ptr// 64位的高位 乘 10^(-9)的高32位
add eax,edi //把edi加进来,丢弃ebx中的低32位
adc edx,0 //eaxedx 中包含了原始64位/10^9的商,其中 edx 存储高32位,eax 存储低32位。
shrd eax,edx,28//整体右移28位,右移方向 edx->eax->28,右移后 eax 中是商的低32位
//shr edx,28 //得到商的高4位
mov ecx,eax //保留商的低32位
//mov ebx,edx //保留商的高32位
mov eax,1000000000
mul ecx //ecx 商的低32位
//这一步执行后,我们将丢弃edx中的数,只取 eax 中的值。
mov esi,dword ptr
mov edi,dword ptr//edi 原始64位数的低32位,
sub edi,eax// a64 mov 10^9 的结果,保存在 edi 中
//以下语句对 edi 中的余数和 ebx ecx 中的商进行调整,这个方法由管理员 liangbch 在http://bbs.emath.ac.cn/thread-9245-1-1.html 中提供。
mov esi,1000000000
mov edx,0xffffffff
xor eax,eax
cmp edi,esi
SETB eax
add edx,eax
mov eax,edx//传送进位
and edx,esi
sub edi,edx
mov dword ptr,edi//得到 a64 mod 10^9 (余数)的准确值,返回主函数
//neg eax
//add ecx,eax
//adc ebx,0// 得到 a64/10^9 (商)的准确值,其中 ebx中存储商的高32位,ecx中存储商的低32位
//如果需要 a64/10^9 (商) 的值,把第 38、40、65、66、67 行的注释取消即可。
pop ebp
pop ebx
pop edi
pop esi
ret
}
}
int main()
{
UINT64 a64;
UINT32 scr,dst;
a64=30789000000000;
scr=(UINT64)a64/4294967296;
scr=(UINT64)a64%4294967296;
div_mod(dst,scr);/// dst[] 返回的是 a64 mod 10^9 (余数)。
/// 若有需要,可以同时返回 a64/10^9 (商)。
printf("dst=%d\n",dst);
system( "pause" );
return 0;
}