找回密码
 欢迎注册
楼主: gxqcn

[讨论] 不用64位乘除法,求 uint64_t 对 10^9 的余数

[复制链接]
发表于 2017-1-14 12:17:02 | 显示全部楼层
64比特的数除以\(10^9\)的商最多35比特
若硬件不支持64位数,只能做两个32位数的乘积且乘积也最多保留最低32位,那么就只能表示为\(2^{16}\)进制数再做除法了,具体做法

1.将被乘数看成4位(\(2^{16}\)进制)数,并在高位补零,变成5位数
2.将乘数看成2位(\(2^{16}\)进制)数,这样就将原问题转化为一个5位数除以2位数的问题。
3.在这里,5位数除以2位数的商为3位数,故需要做3次“3位数除以2位数”的运算。下面我们重点分析一下这个“3位数除以2位数”的子运算
  
3.1  我们能够证明,这个3位数(48比特)最高2个比特为总是0。因为在运算过程中,余数总是小于除数,故被除数总是小于\({10}^9*2^{16}<2^{46}\)
3.2 \( a/{10^9}\)= \((a * 2^{45} / 10^9) / 2^{45}\)= \( (a * 35184.372088832)/2^{45}\)
3.3 将48位被除数记为a,除数\({10^9}\)记为d,被除数最高18位记为a0,因为最高2位为0,故a0不超过16比特,做下列运算
    1. a0=a>>30,    即取a的最高18位。
    2. q=a0*35184  
    3. q=(q>>15)   
    4. a -= d *q
    5. 若a>d, 则q++, a-=d
    6. 若a>d, 则q++, a-=d

注: 步骤1到步骤3,相当于 \(q = (a/2^{30})*35184/2^{15}= a * 35184/2^{45} \)

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2017-1-14 12:31:07 | 显示全部楼层
uint64_t a, q;
q= a-((a>>30)<<30)

BBP算法是计算16进制的派,我觉得可以先弄成16进制的,在转化为10进制。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2017-1-14 13:37:31 | 显示全部楼层
说明:
1。若被除数的范围更小,小于\(2^{32}*{10}^9\), 则只需2次除法。
2. 由于第一次除法中,被除数远小于\2^46\), 故可将试商步骤改为如下序列,减少一次调整商步骤。
    1. a0=a>>16,    即取a的中16位。
    2. q=a0*35184  
    3. q=(q>>29)   
    4. a -= d *q
    5. 若a>d, 则q++, a-=d


毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2017-1-14 14:23:05 | 显示全部楼层
需要注意对方需求的特别之处,仅需返回一个布尔值即可。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2017-1-14 15:03:43 | 显示全部楼层
本帖最后由 happysxyf 于 2017-1-14 15:45 编辑

基本实现,只要允许使用while循环和数组,下面代码就成立。不知题目中的纳秒是什么数量级,是C#中的纳秒,还是10的-9次方秒。我只能做到0.0000001秒出结果。并且全程没用任何乘除。
  1. /*COPYRIGHT@2016~2018 BY HAPPY*/

  2. #include <stdio.h>

  3. typedef unsigned long long uint64_t;
  4. static const int mang[64]={1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 73741824, 147483648, 294967296, 589934592, 179869184, 359738368, 719476736, 438953472, 877906944, 755813888, 511627776, 23255552, 46511104, 93022208, 186044416, 372088832, 744177664, 488355328, 976710656, 953421312, 906842624, 813685248, 627370496, 254740992, 509481984, 18963968, 37927936, 75855872, 151711744, 303423488, 606846976, 213693952, 427387904, 854775808};

  5. //uint64_t取余函数
  6. int Uint64ModBillion(uint64_t a)
  7. {
  8.         int S=0, *p=(int*)mang;
  9.         while(a){
  10.                 if(a-((a>>1)<<1)){
  11.                         S+=*p, S=(S>=1000000000)?S-=1000000000:S;
  12.                 }
  13.                 a>>=1, p++;
  14.         }
  15.         return S;
  16. }

  17. //主函数入口
  18. int main()
  19. {
  20.         uint64_t a=8446744073709551616;
  21.         printf("%d\n", Uint64ModBillion(a));
  22.         return 0;
  23. }
复制代码


下面是返回bool类型的函数

  1. /*COPYRIGHT@2016~2018 BY HAPPY*/

  2. #include <stdio.h>
  3. #include <stdbool.h>
  4. typedef unsigned long long   uint64_t;
  5. typedef unsigned long        uint32_t;
  6. static const uint32_t mang[64]={1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 73741824, 147483648, 294967296, 589934592, 179869184, 359738368, 719476736, 438953472, 877906944, 755813888, 511627776, 23255552, 46511104, 93022208, 186044416, 372088832, 744177664, 488355328, 976710656, 953421312, 906842624, 813685248, 627370496, 254740992, 509481984, 18963968, 37927936, 75855872, 151711744, 303423488, 606846976, 213693952, 427387904, 854775808};

  7. //uint64_t取余函数
  8. bool Uint64ModBillion(uint64_t a)
  9. {
  10.         uint32_t S=0, *p=(uint32_t*)mang;
  11.         while(a){
  12.                 if(a-((a>>1)<<1)){
  13.                         S+=*p, S=(S>=1000000000)?S-=1000000000:S;
  14.                 };
  15.                 a>>=1, p++;
  16.         }
  17.         return (S<500000000)?true:false;
  18. }

  19. //主函数入口
  20. void main()
  21. {
  22.         uint64_t a=8073709551616;
  23.         Uint64ModBillion(a)?fputs("TRUE\n", stdout):fputs("FALSE\n", stdout);
  24. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2017-1-15 10:42:06 | 显示全部楼层
楼上的代码可以进一步优化,实际上a最低的29bit是无需查表计算的,跳过最低的29比特,最大循环次数可从64减少到36.
  1. static const uint32_t mang[]=
  2. {
  3.         536870912, 73741824, 147483648,
  4.         294967296,         589934592, 179869184, 359738368, 719476736, 438953472, 877906944, 755813888,
  5.         511627776,         23255552, 46511104, 93022208, 186044416, 372088832, 744177664, 488355328,
  6.         976710656,        953421312, 906842624, 813685248, 627370496, 254740992, 509481984, 18963968,
  7.         37927936,         75855872, 151711744, 303423488, 606846976, 213693952, 427387904, 854775808
  8. };


  9. //uint64_t取余函数
  10. bool Uint64ModBillion2(uint64_t a)
  11. {
  12.         uint32_t S;
  13.         uint32_t *p=(uint32_t*)mang;
  14.        
  15.         S= a & ( (1<<29)-1);
  16.         a>>=29;

  17.         while (a)
  18.         {
  19.                 if (a & 1)
  20.                 {
  21.                         S+=*p, S=(S>=1000000000)?S-=1000000000:S;
  22.                 };
  23.                 a>>=1, p++;
  24.         }

  25.         return (S<500000000)?true:false;

  26. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2017-1-15 10:43:50 | 显示全部楼层
还有, 我把 ”if(a-((a>>1)<<1))“ 改成了 “if (a & 1)”
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2017-1-15 11:12:23 来自手机 | 显示全部楼层
应该考虑一下将除以常数转化为乘以常数的算法,而且最终我们只需要取其中一个比特的信息
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2017-1-15 11:16:01 | 显示全部楼层
mathe 发表于 2017-1-15 11:12
应该考虑一下将除以常数转化为乘以常数的算法,而且最终我们只需要取其中一个比特的信息

11楼就是这个方法呀,我想不出更快的算法。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2017-1-15 12:39:48 | 显示全部楼层
基本实现,只要允许使用while循环和数组,下面代码就成立。不知题目中的纳秒是什么数量级,是C#中的纳秒,还是10的-9次方秒。我只能做到0.0000001秒出结果。并且全程没用任何乘除。

happysxyf写的这个程序,我试了几个数,结果是对的。你们都厉害啊,乘法、除法都不用,就把除法做出来了。看来除法指令是给我们初学者安排的。

我猜测 liangbch 对这类问题有研究,会更好些。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-4-25 21:22 , Processed in 0.058410 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表