BUG 记录:移位运算与扩展欧几里得算法
BUG 记录:移位运算与扩展欧几里得算法
起因
上个月就开始打算用C++写一个ECC的轮子(为什么?折磨自己呗!),奈何自己水平有点差,拖到现在才算写完底层的大数运算。在实现欧几里得算法的时候,我开始纠结了...
欧几里得算法的两种实现
耳熟能详的实现方案
这个实现只要了解过欧几里得算法的同学都很清楚,我把维基百科上的代码粘贴到这里,最开始我也是按照这样的方式写出来的代码,没过几个测试,bug就出来了。
def ext_euclid(a, b):
old_s,s=1,0
old_t,t=0,1
old_r,r=a,b
if b == 0:
return 1, 0, a
else:
while(r!=0):
q=old_r//r
old_r,r=r,old_r-q*r
old_s,s=s,old_s-q*s
old_t,t=t,old_t-q*t
return old_s, old_t, old_r
乍一看,这个实现真是天衣无缝,毫无破绽,但是要是实现大数运算的时候,如果要是这么想的话,就太naive了。
矩阵计算欧几里得算法
原理我就不抄了,刚学会的,链接在此
https://www.wikiwand.com/zh/輾轉相除法#/矩阵法
这两个方法看起来其实是等价的,第二个方法无非就是拿矩阵表示了一下,有同学会问了,这能有啥区别?
书本上的方法的问题所在
其实最开始的时候,我在我的大数系统实现的时候,我就意识到了有可能会有这样的问题:
注意上面代码中循环中的操作,减法?乘法?
q*s和q*t其实在a和b很大的时候有溢出的风险。
注意到这个问题之后,我用python实现了一下,果然!这个操作会溢出!又用python实现了一个matrix,这回没有这个问题。(我把代码放在最后)
结论
仔细看看这两种方法,第二种方法其实是一种推迟了减法的做法。矩阵上的元素始终在增长,且不会超过a和b的最高位,代价就是多了两个需要存储的元素。
移位运算坑死我这样的小白
我修改了上面的代码,又测试了一下,还是不对,我慌了,最终使用中间量对比大法,一步一步去对比,人肉检查,我发现了问题所在。
实现gcd免不了带余除法(欧几里得除法)的操作,对于除法,我本来还想优化优化,但是奈何水平有点捉急,最后写了个textbook division(列等式减法那种),里面的当然有移位运算了,就是这个移位运算搞我。
void NB::rshift(int bitnum){
/* calculate block num and remain num */
int blocknum = bitnum/64;
int remainnum = bitnum%64;
WORD tmp1 = 0;
WORD tmp2 = 0;
/* first step , move the blocknum */
for(int i=0;i<MAX_number_word;i++){
if(i-blocknum>=0){
this->val[i-blocknum] = this->val[i];
}
}
for(int i=MAX_number_word-1;i>=MAX_number_word-blocknum;i--){
this->val[i] = 0;
}
/* second step , move the remainnum */
for(int i=MAX_number_word-1;i>=0;i--){
tmp1 = this->val[i]<<(WORDSIZE-remainnum);
this->val[i] = this->val[i]>>remainnum;
this->val[i] += tmp2;
tmp2 = tmp1;
}
}
毫无破绽的代码(划掉,并不是)。注意第二步写移位运算的时候有一句:
tmp1 = this->val[i]<<(WORDSIZE-remainnum);
当remainnumd=0,就会出现移位WORDSIZE的情况,聪明的小伙伴都知道,这种情况在C++标准里是没有定义的,即
移位运算的右操作符的大小应该大于等于0小于左操作符的最大位数,其余情况是未定义的。
所以不太聪明但是有效的做法->
/* second step , move the remainnum */
for(int i=MAX_number_word-1;i>=0;i--){
if(remainnum==0){
tmp1 = 0;
}else{
tmp1 = this->val[i]<<(WORDSIZE-remainnum);
}
this->val[i] = this->val[i]>>remainnum;
this->val[i] += tmp2;
tmp2 = tmp1;
}
结束
表达了对眼前有码,心中无码的境界的期待。
BUG 记录:移位运算与扩展欧几里得算法的更多相关文章
- ****ural 1141. RSA Attack(RSA加密,扩展欧几里得算法)
1141. RSA Attack Time limit: 1.0 secondMemory limit: 64 MB The RSA problem is the following: given a ...
- 『扩展欧几里得算法 Extended Euclid』
Euclid算法(gcd) 在学习扩展欧几里得算法之前,当然要复习一下欧几里得算法啦. 众所周知,欧几里得算法又称gcd算法,辗转相除法,可以在\(O(log_2b)\)时间内求解\((a,b)\)( ...
- 扩展欧几里得算法详解(exgcd)
一.前言 本博客适合已经学会欧几里得算法的人食用~~~ 二.扩展欧几里得算法 为了更好的理解扩展欧几里得算法,首先你要知道一个叫做贝祖定理的玄学定理: 即如果a.b是整数,那么一定存在整数x.y使得$ ...
- 初等数论-Base-2(扩展欧几里得算法,同余,线性同余方程,(附:裴蜀定理的证明))
我们接着上面的欧几里得算法说 扩展欧几里得算法 扩展欧几里德算法是用来在已知a, b求解一组x,y,使它们满足贝祖等式\(^①\): ax+by = gcd(a, b) =d(解一定存在,根据数论中的 ...
- 扩展欧几里得算法(extgcd)
相信大家对欧几里得算法,即辗转相除法不陌生吧. 代码如下: int gcd(int a, int b){ return !b ? gcd(b, a % b) : a; } 而扩展欧几里得算法,顾名思义 ...
- noip知识点总结之--欧几里得算法和扩展欧几里得算法
一.欧几里得算法 名字非常高大上的不一定难,比如欧几里得算法...其实就是求两个正整数a, b的最大公约数(即gcd),亦称辗转相除法 需要先知道一个定理: gcd(a, b) = gcd(b, a ...
- 欧几里得算法与扩展欧几里得算法_C++
先感谢参考文献:http://www.cnblogs.com/frog112111/archive/2012/08/19/2646012.html 注:以下讨论的数均为整数 一.欧几里得算法(重点是证 ...
- vijos1009:扩展欧几里得算法
1009:数论 扩展欧几里得算法 其实自己对扩展欧几里得算法一直很不熟悉...应该是因为之前不太理解的缘故吧这次再次思考,回看了某位大神的推导以及某位大神的模板应该算是有所领悟了 首先根据题意:L1= ...
- 浅谈扩展欧几里得算法(exgcd)
在讲解扩展欧几里得之前我们先回顾下辗转相除法: \(gcd(a,b)=gcd(b,a\%b)\)当a%b==0的时候b即为所求最大公约数 好了切入正题: 简单地来说exgcd函数求解的是\(ax+by ...
随机推荐
- adjust, administer
adjust to just, exact. In measurement technology and metrology [度量衡学], calibration [校准] is the compa ...
- day06 视图层
day06 视图层 今日内容 视图层 小白必会三板斧 JsonResponse form表单发送文件 FBV与CBV FBV基于函数的视图 CBV基于类的视图 模板层 模板语法的传值 模板语法之过滤器 ...
- Docker快速上手入门
Docker 什么是Docker? Docker就是一种虚拟化的技术 可以通过Docker快速的下载使用第三方技术,方便搭建环境 目的:Securely build,share and run any ...
- Learning Spark中文版--第四章--使用键值对(2)
Actions Available on Pair RDDs (键值对RDD可用的action) 和transformation(转换)一样,键值对RDD也可以使用基础RDD上的action(开工 ...
- 从源码看RequestMappingHandlerMapping的注册与发现
1.问题的产生 日常开发中,大多数的API层中@Controller注解和@RequestMapping注解都会被使用在其中,但是为什么标注了@Controller和@RequestMapping注解 ...
- CR LF 的含义
可以参考: 转载于:https://www.cnblogs.com/babykick/archive/2011/03/25/1995977.html
- D3基础入门四-事件处理
6.5.0版 .on("mouseover", function(e,d) e: {"isTrusted":true} 第二个参考才是数据,但这在不同的环境可能 ...
- ES6常用的数值转换方法
<script type="text/javascript"> // Number常用方法 /* Number.isFinite() 用来检查一个数值是否为有限的(fi ...
- Java中利用正则表达式获取一个网页中的所有邮箱地址
package cn.tms.ui; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; im ...
- Mysql配置文件 innodb引擎
目录 innodb参数 innodb_buffer_pool_size innodb_read_io_threads|innodb_write_io_threads innodb_open_files ...