【转载】学习总结:初等数论(3)——原根、指标及其应用
【转载】学习总结:初等数论(3)——原根、指标及其应用
写得太好了。。忍不住转载啊。。
未授权,侵权删。
原博文链接:http://blog.163.com/gc_chdch@126/blog/static/172279052201641935828402/
---------------------------------------------------------------------------------
学习总结:初等数论(3)——原根、指标及其应用
最近知道了一本书叫《数论概论(第3版)》(A Friendly Introduction to Number Theory),简单翻了翻,感觉这本书写的非常好。想起以前刚接触数论时,没有看这本书来入门,真是十分遗憾。这本书面向非数学专业读者,语言生动幽默而不失严谨性,注重对知识的感性认识(用了大量的例子),和对数学思想和方法的渗透(比如:大胆猜想规律,证明时用“计数”法等)。这里强烈推荐这本书!
下面总结一下这几天学习的内容。
一、整数的阶
a与n是互质的两个数。可以发现,总存在某个数x,满足 ax ≡ 1(mod n)。
使ax ≡ 1(mod n)满足的最小的正整数x叫做a模n的阶(或次数),记做ena(有的地方记做ordna)。
怎么理解呢?你要不断计算a,a2,a3,…,(注意要模n)我们知道,根据鸽巢原理,一定会有循环。而a和n互质时,总会在某个时刻出现了1,下一个时刻又是a,……于是就有了循环。这个出现1的最小的指数,叫做a的阶。
比如:我们要找3模7的阶,计算31,32,…,36模7的值依次为:
3,2,6,4,5,1,
所以我们得到3模7的阶为6。
a与n互质时,根据欧拉定理有a?(n)≡1(mod n)。这么说来,刚才我们不断计算a的幂的循环节的长度(即a的阶)“扩增”若干次后应该为?(n)。所以,
ena | ?(n)。
同样的道理,为了使方程 ax≡1(mod n)有解,应该把循环节长度“扩增”若干次后得到x,所以
ena | x
二、原根
设a与n是互质的整数,当a模n的阶为?(n)时,把a叫做n的原根。
怎么理解呢?我们计算a的幂的序列,第一次得到1的时候,得到了完整的一个循环节。我们上面已经说了,?(n)一定是(a模n的阶)的倍数。如果这个循环节的长度恰好就是?(n)(循环节的长度已经达到最大了),那么a就是n的原根。
(1)原根的存在性
一个数可能有很多个原根,也可能没有原根。可以证明:
正整数n存在原根,当且仅当
n = 2, 4, pt或2pt(其中p是奇素数,t是正整数)。
举例:5的原根有2和3;7的原根有3和5。
(2)原根的一个性质
原根为什么这么重要?
设g是n的一个原根,那么:g,g2,g3,…,g?(n)(即g0≡1(mod n))一定两两不同。(如果有相同,比如说存在0≤i<j<?(n)满足gi≡gj(mod n),那么就会有gj-i≡1(mod n),而j – i<?(n),这时?(n)就不是g的阶了)
看看方程ax≡1(mod n),如果x是存在的,那么a一定是与n互质的数(如果a和n有某个大于1的公因子,无论a怎么取幂再模n,这个公因子都“消不掉”,不会有ax mod n = 1)。所以“合法”的a有?(n)个。而我们找到一个原根g后,g的幂(g,g2,g3,…,g?(n))一定就是这些合法的a(即与n互质的?(n)个数。因为g与n互质,g的幂也与n互质,而这些幂又两两不同,一定能把“与n互质的数”取尽)。
一句话总结上面的内容:对于n的一个原根g,满足
{ 1,g,g2,g3,…,g?(n) – 1 } = { x | x与n互质,1≤x<n }
特别地,对于奇质数p的一个原根g,满足
{ 1,g,g2,g3,…,gp – 2 } = { 1, 2, 3, …, p – 1 }
(3)原根的个数
一个数n,如果存在原根,就一定?(?(n))个。怎么证明呢?为此我们先讨论下面这个问题:
已知a模n的阶ena,怎么求au的模n的阶?
设au模n的阶为t,那么t应该是最小的正整数,满足(au)t≡aut≡1 (mod n)。
由之前的讨论,可以知道:u×t应该是ena的倍数,并且t最小。
由数论的基本知识可以得出,enau = t = ena / gcd(u, ena)。
如果我们找出一个原根g,怎么得出其他的原根?
首先,其他的原根一定是g的若干次幂。
哪些“g的若干次幂”可以成为原根?
用原根的定义!
对于某个“g的若干次幂”,比如gi (0≤i<?(n)),由上面的结论得出,它模n的阶为
?(n) / gcd(i, ?(n))
欲使它的阶也为?(n),即
?(n) / gcd(i, ?(n)) = ?(n)
需要使gcd(i, ?(n)) = 1,也就是i和?(n)互质。
有多少个i满足它和和?(n)互质?换句话说,在0 ~ ?(n)–1的数中,有多少个与?(n)互质?答案应该是?(?(n))。所以,一个数如果有原根,那么它有?(?(n))个原根。
下面举个例子:
7的原根有?(?(7)) = ?(6) = 2个,最小的一个是3。
在32,33,…,36中,哪个指数与6互质?只有5,所以另一个原根是35≡5(mod 7)。
怎么找原根?
通常最小的原根都比较小,所以暴力从1开始枚举就可以了。判断一个数a是不是n的原根,需要判断?(n)是否是a的阶,直接的判断方法是枚举?(n)的每一个因子d(除去它本身),判断是否ad≡1(mod n)。但这样做了很多重复的判断。
比如我们要判断a模n=37的阶是否为36,那么只需找出36的两个质因子2和3,只需判断36/2和36/3作为a的幂的指数时,(a的幂) mod n是否为1。如果a的阶为36/4,36/9,36/6,36/12,…,其实情况已经包含在上面的判断中了。(比如,如果a36/9≡a4≡1(mod n),那么一定也会满足a36/2≡3618≡1(modn))。
三、指标(离散对数)
求原根有什么作用?为了计算指标!
对于n的一个原根g,满足
{ 1,g,g2,g3,…,g?(n) – 1 } = { x | x与n互质,1≤x<n }
再具体一些,可以定义一种“求幂”的运算,这种运算揭示了两个集合的一一对应关系:
i → gi (1≤i≤?(n))
为什么是一一对应关系?因为gi一定两两不同。这一点已经讨论过了。
那么,是否有一种“求幂”运算的逆运算?有!
求出n的一个原根g,知道了某个与n互质的数a,n是g的多少次方?
我们用I(a)来表示这个“次方”数,叫做以g为底a模n的指标。(有的地方记做indga)
也就是,依照定义,应该有gI(a)≡a(mod n)(a与n互质)。
显然,指标的范围是0≤I(a)<?(n),当指标超过?(n)时,出现了循环,可以把指标mod ?(n)进行简化。
有点像我们以前学过的对数?(不严谨的说,有点像logga?)也许这就是为什么指标也叫离散对数了。
当n为质数时,原根g一定存在,而且?(n) = n – 1,这样,每个在1~n–1范围内的数都有指标!
我们可以根据幂的运算法则,对应得出指标的运算法则:
(1) I(ab) = I(a) + I(b) (mod ?(n)) (类比:logaNM = logaN + logbM)
(2) I(ak) = k×I(a) (类比:logaNM = M×logaN)
指标把乘法变加法,把幂变乘法,这一点与对数的运算法则多么相似!
如果有一个指标表,我们可以十分简便地进行运算。举例:
n = 37,它的一个原根是a = 2。
要计算23×19 mod 17的值,可以计算
I(23×19) ≡ I(23) + I(19) ≡ 15 + 35 ≡ 50 ≡ 14 (mod 36)
然后,查表可以得出,指标为14的数是30,就是要求的答案了。
很麻烦?
再看一个例子:
I(2914) ≡ 14 × I(29) ≡ 294 ≡ 6 (mod 36)
由表得:I(27) = 6,所以2914 ≡ 27 (mod 37)
你也许会说:有快速幂!在前两个例子中,似乎指标的优势没有体现出来。不过,在解方程的时候,指标就很有用了。
解同余式:
扩展欧几里得?好像也能解。不过,像下面这样的同余式呢?
只能用指标来解。两边同时求离散对数:
可以解出:
事实上,最后一个例子是指标最重要的运用之一。等会儿我们会详细讨论。
但是,之前的计算都是在指标表已经给了的情况下进行的。没有指标表怎么办呢?如何求某个数指标(离散对数)?
更准确地说,给出g, a, p,如何求gk ≡ a(mod p)的最小的k?为了简化问题,这里规定p为质数。
这里使用一种叫做大步小步(gaint-step baby-step)的算法。算法的核心思想是分块。取m = [ sqrt(p – 1) ] + 1,然后把k表示成xm + y (0 ≤ y < m)的形式。这样,x和y的范围都是0~m(y不含m)。于是gk ≡ (gm)x × gy,可以求出所有的gy(m个取值);然后枚举x,计算出(gm)x,查找:是否存在某个gy满足(gm)x × gy ≡ a(mod p)?也就是说:
是否存在某个gy,满足gy ≡ a × (gmx) –1 (mod p)?
用费马小定理和快速幂求出逆元(gmx) –1,然后求出a × (gmx) –1。检查是否有“匹配”的gy。如果我们先把gy放在一个哈希表(或者C++的map)中,那么这一步的查询就是O(1)(或O(log2m)=O(log2p))的。算法的核心步骤仍然是枚举,但分块使时间复杂度变成O(sqrt(P) × log2P)(注意算上求逆元的时间)。
四、N次剩余
这里要解决一个这样的问题:
给出N, a, p,求满足xN ≡ a (mod p)(p为质数)的所有解x。
可以形象地理解成求a在模p意义下的N次方根。
刚才我们已经借助例子,初步了解了做法:
xN ≡ a (mod p),找出p的一个原根g,用“大步小步”算法求出以g为底a模p的指标I(a)。
同余式变成:
N × I(x) ≡ a (mod p – 1)
由一次同余方程的知识可以知道,有解的条件是
gcd(N, p – 1) | a
而且,解有gcd(N, p – 1)个。
解出所有的可能的I(x),那么x = gI(x)。这些x中重复的要去掉。
代码:
1 #include <cstdio> 2 3 #include <cstring> 4 5 #include <algorithm> 6 7 #include <cmath> 8 9 #include <vector> 10 11 #include <map> 12 13 using namespace std; 14 15 16 17 typedef long long LL; 18 19 20 21 int gcd(int a, int b) 22 23 { 24 25 return b == 0 ? a : gcd(b, a % b); 26 27 } 28 29 30 31 void _gcd(int a, int b, LL &x, LL &y) 32 33 { 34 35 if (b == 0) 36 37 { 38 39 x = 1; y = 0; 40 41 return ; 42 43 } 44 45 _gcd(b, a%b, y, x); 46 47 y -= (a/b) * x; 48 49 } 50 51 52 53 int extend_gcd(int a, int b, int c, LL &x, LL &y, int &dx, int &dy) 54 55 { 56 57 int g = gcd(a, b); 58 59 if (c % g) return 0; 60 61 _gcd(a, b, x, y); 62 63 x *= (c/g); 64 65 y *= (c/g); 66 67 dx = b / g; 68 69 dy = a / g; 70 71 return g; 72 73 } 74 75 76 77 // Ax = B (mod N) 78 79 // 设Ax = -yN + B 80 81 // 则Ax + Ny = B 82 83 bool line_mod_equ(int A, int B, int N, int &x, int &k) 84 85 { 86 87 LL x0, y0; 88 89 int dx, dy; 90 91 if (!extend_gcd(A, N, B, x0, y0, dx, dy)) return false; 92 93 x0 %= dx; 94 95 if (x0 < 0) x0 += dx; 96 97 x = (int)x0; 98 99 k = dx; 100 101 return true; 102 103 } 104 105 106 107 LL pow_mod(LL a, LL b, LL p) 108 109 { 110 111 if (b == 0) return 1; 112 113 LL tmp = pow_mod(a, (b>>1), p); 114 115 if (b & 1) return tmp * tmp % p * a % p; 116 117 else return tmp * tmp % p; 118 119 } 120 121 122 123 //分解质因数 124 125 void factor(int x, vector<int> &divs) 126 127 { 128 129 divs.clear(); 130 131 for (int i = 2; i * i <= x; ++ i) 132 133 if (x % i == 0) 134 135 { 136 137 divs.push_back(i); 138 139 while (x % i == 0) x /= i; 140 141 } 142 143 if (x > 1) divs.push_back(x); 144 145 } 146 147 148 149 bool g_test(int g, vector<int> &divs, int P) 150 151 { 152 153 for (int i = 0; i < (int) divs.size(); ++ i) 154 155 if (pow_mod(g, (P-1) / divs[i], P) == 1) return false; 156 157 return true; 158 159 } 160 161 162 163 //找原根,p为质数,保证有原根 164 165 int primitive_root(int P) 166 167 { 168 169 static vector<int> divs; 170 171 factor(P-1, divs); 172 173 int g = 1; 174 175 while (!g_test(g, divs, P)) ++ g; 176 177 return g; 178 179 } 180 181 182 183 // 求解在模P意义下,以a为底N的离散对数b (P为质数) 184 185 // 即 a ^ b = N (mod P) 186 187 // 大步小步算法(分块) 188 189 // 取s = sqrt(P), 设b = x * s + y 190 191 // 则 a ^ (x*s + y) = (a^s)^x * a^y = N (mod P) 192 193 // 求出y = 0~s-1时, a^y的取值;然后枚举s,算出(a^s)^x,查找是否有匹配的y 194 195 int discrete_log(int a, int N, int P) 196 197 { 198 199 map<int, int> rec; 200 201 int s = (int)sqrt(P + 0.5); 202 203 while (s * s <= P) ++ s; 204 205 LL cur = 1; 206 207 for (int y = 0; y < s; ++ y) 208 209 { 210 211 rec[ cur ] = y; 212 213 cur = cur * a % P; 214 215 } 216 217 LL a_s = cur; // a^s 218 219 cur = 1; 220 221 for (int x = 0; x < s; ++ x) 222 223 { 224 225 LL a_y = pow_mod(cur, P-2, P) * LL(N) % P; 226 227 map<int,int> :: iterator it = rec.find( a_y ); 228 229 if (it != rec.end()) return x * s + it -> second; 230 231 cur = cur * a_s % P; 232 233 } 234 235 return -1; 236 237 } 238 239 240 241 // x ^ K = A (mod P) (where P is a prime) 242 243 // 找p的一个原根g,求出指标 244 245 // K I(x) = I(A) (mod P-1) 246 247 // 有解的条件 gcd( I(x), P-1 ) | I(A) 248 249 void discrete_root(int K, int A, int P, vector<int> &x) 250 251 { 252 253 x.clear(); 254 255 if (A == 0) { x.push_back(0); return ; } 256 257 int g = primitive_root(P); 258 259 int IA = discrete_log(g, A, P); 260 261 int Ix, delta; 262 263 if (!line_mod_equ(K, IA, P-1, Ix, delta)) return ; 264 265 while (Ix < P) 266 267 { 268 269 x.push_back( pow_mod(g, Ix, P) ); 270 271 Ix += delta; 272 273 } 274 275 sort(x.begin(), x.end()); 276 277 x.erase(unique(x.begin(), x.end()), x.end()); 278 279 } 280 281 282 283 int main() 284 285 { 286 287 int P, K, A; 288 289 scanf("%d%d%d", &P, &K, &A); 290 291 static vector<int> x; 292 293 discrete_root(K, A, P, x); 294 295 printf("%u\n", x.size()); 296 297 for (int i = 0; i < (int) x.size(); ++ i) printf("%d\n", x[i]); 298 299 return 0; 300 301 }
【转载】学习总结:初等数论(3)——原根、指标及其应用相关推荐
- 奥卡姆剃刀是什么?机器学习实践中那些学习模型或者那些评估指标践行了这一理论?
奥卡姆剃刀是什么?机器学习实践中那些学习模型或者那些评估指标践行了这一理论? 奥卡姆剃刀:无无必要,勿增实体. 奥卡姆剃刀原理应用于模型选择时变为以下想法:在所有可能选择的模型中,能够很好地解释已知数 ...
- 深度学习分类任务常用评估指标
摘要:这篇文章主要向大家介绍深度学习分类任务评价指标,主要内容包括基础应用.实用技巧.原理机制等方面,希望对大家有所帮助. 本文分享自华为云社区<深度学习分类任务常用评估指标>,原文作者: ...
- ElasticSearch学习笔记之二十一 指标聚合
ElasticSearch学习笔记之二十一 指标聚合 指标聚合 Avg Aggregation Script Value Script Missing value Weighted Avg Aggre ...
- 转载学习笔记:c++atoi
c++中的atoi()和stoi()函数的用法和区别 鸡啄米的时光机 2018-08-27 20:58:41 28304 收藏 28 最后发布:2018-08-27 20:58:41首发:2018-0 ...
- [转载]学习java30条基本的概念
原文地址:学习java30条基本的概念作者:简单爱 学习java30条基本的概念 在我们学习Java的过程中,掌握其中的基本概念对我们的学习无论是J2SE,J2EE,J2ME都是很重要的,J2SE是J ...
- (转载)学习Javascript闭包(Closure)
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 下面就是我的学习笔记,对于Javascript初学者应该是很有用的. 一.变量的作用域 要理解 ...
- Linux常用命令 实用命令万字总结(转载学习)
原文链接 最高使用频率的命令之一. 命令格式: ls [OPTION]- [FILE]- 单纯的输入: [root@iz2ze76ybn73dvwmdij06zz /]# ls bin dev hom ...
- 【深度学习-机器学习】分类度量指标 : 正确率、召回率、灵敏度、特异度,ROC曲线、AUC等
来源于博文:https://blog.csdn.net/tanzuozhev/article/details/79109311 在分类任务中,人们总是喜欢基于错误率来衡量分类器任务的成功程度.错误率指 ...
- 数字IC设计工程师的知识结构(转载学习)
I. 技能清单 作为一个真正合格的数字IC设计工程师,你永远都需要去不断学习更加先进的知识和技术.因此,这里列出来的技能永远都不会是完整的.我尽量每年都对这个列表进行一次更新.如果你觉得这个清单不全面 ...
- SQLalchemy 转载学习(学习了中间relationship内部cascade参数的部分)
关于relationship里面cascade参数,一直没理解 看了这篇文章,有些感觉了 转载一下这位前辈的文章 http://www.cnblogs.com/booolee/archive/2009 ...
最新文章
- 【青少年编程(第32周)】李老师太给力了!
- 阿里带火的中台到底是什么?白话中台战略
- 注意python函数参数的可变变量的传递
- [原创]教你如何最快写出酷炫的dialog对话框
- python 选择排序算法
- 扩展方法where方法查询不到数据,不会抛异常,也不是返回的null
- mysql模糊匹配like 之 %
- ASP.NET MVC 学习第三天
- SANGFOR SCSA——网络基础(下)
- weblogic开发EJB
- 亲自体验了vscode网页版开发代码并提交代码到github代码库,发现出奇的流畅,手机和平板也可以很愉快的编写代码了
- telegram设置中文
- linux手机版模拟电脑,在你的PC上体验Firefox OS 1.3 模拟器
- ob2222mcp非隔离电源芯片
- 科学松鼠会压缩感知科普文章两篇:“压缩感知与单像素相机(陶哲轩)”“填补空白:用数学方法将低分辨率图像变成高分辨率图像(Jordan Ellenberg)
- 什么是多方计算multi-party computation (MPC)
- iPad mini Retina越狱小结
- 自己动手用python写豆瓣FM
- JVM(七) - Jvm内存模型
- spring-data-mongodb依赖的spring版本