数论算法

数论是一个非常庞大的数学分支,这篇博客我会把学的数论知识,和算法慢慢分享出来(不定期更新),各种代码的正确性我会在代码注释里给出证明

最大公约数:
最大公约数是数论很重要的组成部分(其实是数论模运算的重要应用),再看证明之前各位必须了解一下公约数及最大公约数的重要性质,下面我就简要的提下我证明所用到的性质。
符号说明:d | b表示d能整除b,即 b % d = 0
1.若d | a且 d | b,则d | (a * x + b * y) (x,y为任意整数)
2.gcd(0,0) = 0
3.对于gcd(a,b),a为非负整数,b为整数,则gcd(a,b) = a * x + b * y
(x,y为满足gcd(a,b)为最小正整数的实数对)
注:算法导论中 a * x = b(mod n)的解不是只有d个,我们可以设定正确x’的值可以得到更多,即在execute_gcd算法中设定递归出来的y’值.
下面我们来实现代码及代码正确性的证明:

#include <iostream>
#include <vector>
#include <stdio.h>

using namespace std;
/*
**  证明: gcd(a,b) = gcd(b,a % b)
证:要证gcd(a,b) = gcd(b,a % b)只需证明
        gcd(a,b) | gcd(b,a % b)且
        gcd(b,a % b) | gcd(a,b)
   设:d = gcd(a,b),则 d | a 且 d | b,而我们
   知道 a % b = a - b * q (q = 向下取整(a / b))
   因此,a % b是a和b的线性集合的元素,故 d | a % b
   ,即: d | b 且 d | (a % b)
   进一步可得:   gcd(a,b) | gcd(b,a % b),
   设 d2 = gcd(b,a % b),则 d2 | b,d2 | (a % b),
   ,而 a = (a % b) - b * q (q = 向下取整(a / b))
   则a是(a % b)和b线性集合里的一个元素,故
   d | a,综上,d | a 且 d | b,即 d | gcd(a,b)
   综上,gcd(a,b) = gcd(b,a % b);
*/
int gcd(const int& a,const int& b) {
    if (b == 0) {
        return a;
    }
    return gcd(b, a % b);
}
/*
** 众所周知: gcd(a,b) = a * x + b * y(x,y为使gcd(a,b)是最小正整数的实数对集)
   而程序中是利用了 gcd(a,b) = gcd(b,a % b)的特性,则运行过程为
            gcd(a,b) = a * x + b * y;
            gcd(b,b % a) = b * x1 + (b % a) * y1;
            .......
            gcd(ai,bi) = ai * 1 + 0 * yi;
  根据递归终止条件可知我们bi = 0,xi = 0,
  而除最后一条递归,之前的x,y可能多解,而最后一条是因为条件限制
  xi = 0,yi任意,我们可以通过设定yi的值,得到x,y的可能解,对于
                gcd(x,y) = a * x + b * y;
  我们可以通过调整x或y的值,得到满足条件的(x,y)实数对,然而最后一种情况,
  已经固定   xi = 1,yi任意,所以调整(x和y为含yi的表达式会不失一般性和其任意性)
  而 gcd(a,b) = a * x + b * y;
     gcd(b,a % b) = b * x' + (a % b) * y'
                  = b * x' + (a - b * [a / b]) * y'
  即: a * x + b * y = b * x' + (a - b *[a / b]) * y'
  调整: a * x + b * y = a * y' + (x' - [a / b] * y') * b;
  令 x = x',则 y = x' - [a / b] * y';([a / b]为其向下取整)
  通过随意指定yi,而xi = 1,我们很好的维护了程序的正确性
*/
pair<int, pair<int, int>> execute_gcd(const int& a,const int
&b) {
    if (b == 0) {
        return make_pair(a, make_pair(1, 79));
    }
    pair<int, pair<int, int>> get;
    pair<int, pair<int, int>> ret;
    get = execute_gcd(b, a % b);
    ret.first = get.first;
    ret.second.first = get.second.second;
    ret.second.second = get.second.first - static_cast<int>(a / b) * get.second.second;
    printf("a:  %d\tb:  %d\tx:  %d\ty:  %d\n", a, b, ret.second.first, ret.second.second);
    return ret;
}
/*
**  模线性方程: a * x = b(mod n)
    这里 a,b,n为任意输入值,我们求解x
    不知各位发现没有其实这个方程就像gcd(a,n)的逆运算
    b = gcd(a,n) = a * x + n * y;此时我们输入
    a,n来求解b,与此同时能得到(x,y)的结果集,而
    模线性方程则是让我们输入一个我们a,n和猜想的a,n最大公约数b,
    如果b = gcd(a,n)则就说明有解,否则无解,但是当n = 0,我们就不能把
    a * x = b(mod n)改写成 b = gcd(a,n) = a * x + n * y了
    此时我们设d = gcd(a,n),来观察d 和 b的关系而不失一般性,
    当 n = 0时,   a * x = b(mod 0),即 b = a * x (x为整数)
    此时  d = gcd(a,n) = a * x = a(这里的x必须为1,不懂的去看gcd的性质),
    观察d = a,b = a * x,如果x有解,则必有 d | b
    所以,综上我们容易知道:对于 a * x = b(mod n),若d = gcd(a,n),d | a,则
    方程有解,否则方程无解.
    数学证明总是容易的(给定了条件(起点)和结果(终点),而我们给出过程(连上起点和终点)总是很容易的,但是真正
    困难的是我们总要弄清楚终点的重要意义及终点的存在(否则变成了矢量,哈哈),如果我们不弄懂终点存在的意义(我们知道终点的存在及其到达的方向,然而我们其更为重大的意义是我们把它当作起点,继续寻找终点及方向),
    而只是在起点和终点寻找各种各样的路径(事实更可怕的是,人们往往认为(数学家或教科书或自己的固有思维)给出的路线是一条"直线"(两点之间直线最短)),让这条"直线"经久不衰的连起来,其实这种想法也没错,人生那么短,我们何必把这种
    让自己感到折磨的事情强压在自己只有一次的人生之上,所以这种事情就交给感兴趣的人去做(他们觉得这样做能让自己的人生有意义,我们要尊重他们),当然,我们选择了与数学有关的事情(当然我们生活也需要数学,如果有人说数学没用,对于我身边出现这种人,我就有危机感了,因为我的水平太低,竟然让身边充斥着
    这种人(当然这句话不是讽刺,而是尊重自己,人是群居动物为什么不能自己想办法选择一个好一点,适合自己的群去居呢),既然如此,我们总是要凭借别人(站在巨人的肩膀上)找到直线和理解终点的存在,一条条错综复杂的直线和曲线构造了数学的长河,所以如果我们不懂得终点存在的意义,那么以这个终点为起点时,您会觉得
    更加抽象吧,人生亦是如此,我觉得人活着就是奔着死亡去的,如果我们让人生过的毫无意义(自己认为的,不是别人骂你的,哈哈),那么我们死亡时是无奈的叹息或是依依不舍(年轻时的英雄ol真好玩,开玩笑,哈哈),不管怎样,我们都要正确对待死亡,只有死亡,才能开启我们的第二人生啊,兄弟!
    那么我们怎么证明d | b时 a * x = b(mod n),这里就是群的应用了,首先介绍下群的概念:
    符号定义:(S,@),(S,@)表示集合S元素运用二元操作符@
    对于(S,@)需要满足下列条件才能称为群:
        1.封闭性:对于任意a属于S,b属于S,而a @ b属于S,则满足封闭性
        2.可交换性:对于任意a属于S,b属于S,c属于S,(a @ b) @ c = a @ (b @ c),则满足
        可交换性
        3.有单位元:对于任意元素a属于S,存在元素e属于S,使得 a @ e = e @ a = a,则表示(S,@)含单位元
        4.有逆元:对于任意元素a属于S,存在元素a'使得 a @ a' = a' @ a = e(单位元)
    满足以上条件的集合(S,@)称为群
    子群:对于群(S,@),其子集S'满足封闭性,则称S'为S的子群
    有种特殊的方法可以很方便产生子群:我们可以选取群(S,@)中的一个元素a
    对a进行k(k >= 1)次 @运算所产生的集合S' = {(a^k): k > 0)}是S的子群
    我们也很容易证明此处就略去了,下面我们来看看它的几个小性质:
        1.(a^k):k >= 1序列具有周期性,周期 T = | S'|
        ....
    对于模加法操作是一个群(S,+),选取其中一个元素a,生成子群
    模加法群是一个可结合群(阿贝尔群),故S' = {a * x mod n: x >= 1},
    而a * x = b(mod n),则表示若x有解,则b必须属于S',那么
    我们证明 d | b,b就属于S',不就证明当 d = gcd(a,n),d | b,
    a * x = b(mod n)有解
    证明:我们知道存在x'使得 d = (a * x')mod n,故d属于S',
    我们接着证明存在x''使得 k * d = (a * x'')mod n,即
    k * n + a * x'' = d,即d是a和n的一个公约数,然而d是a和n的最大公约数,显然满足条件,
    即当b = k * d即d | b有解,a * x mod n值在[0,n-1]之间通过上式我们还知道
    S'a = S'd = {0,d,2 * d,.....(n / d - 1) * d}
    即 |S| = n / d;
    而当b属于a * i mod n(i = 0,1,2,3....n - 1)说明,
    若 b = a * j mod n,根据周期性可知 b = a * (j + n / d)mod n
    ,以此类推我们可知x的解为 x = x0 + (n / d) * i (i = 0,1,2.....d)
    所以我们找到x0便找到了所有解.
    即    a * x0 = b(mod n)
          d = gcd(a,n) = a * x' + n * y'(x',y'为整数)
          d = a * x'(mod n)
    则:    (a * x0) mod n = b
          (a * x') mod n = d
          x0 = x' * (b / d) mod n
          因此
          x = x0 + (n / d) * i (i= 0,1,2,3..d)
            = x' * ( b / d) mod n + (n / d) * i
*/
//  a * x  = b(mod n)
vector<int> solve_line_mod(const int& a, const int& b,const int& n) {
    int x0;
    vector<int> vec(0);
    pair<int, pair<int, int>> p = execute_gcd(a,n);
    if ((static_cast<double>(b) / p.first) == (static_cast<double>(b / p.first))) {
        x0 = (p.second.first * (b / p.first)) % n;
        vec.push_back(x0);
    }else {
        return vec;
    }
    for (int i = 1; i < p.first; ++i) {
        int x = x0 + (n / p.first) * i;
        vec.push_back(x);
    }
    return vec;
}
int main() {
    //execute_gcd(99, 78);
    int a = 4;
    int b = 3;
    int n = 12;
    vector<int> vec;
    vec = solve_line_mod(a, b, 12);
    if (vec.size() == 0) {
        printf("无解\n");
    }else {
        for (auto v : vec) {
            cout << v << endl;
        }
    }
    system("pause");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介: 本书论述了算法数论的基本内容,其中包括:连分数、代数数域、椭圆曲线、素性检验、大整数因子分解算法、椭圆曲线上的离散对数、超椭圆曲线。本书的特点是内容涉及面广,在有限的篇幅内,包含了必要的预备知识和数学证明,尽可能形成一个完整的体系。并且本书的部分内容曾多次在中国科学院研究生院信息安全国家重点实验室和广州大学作为硕士研究生教材使用。本书可作为信息安全、数论等专业的研究生教材及相关专业的研究人员、高等学校的教师和高年级学生的参考。 目录: 序 前言 第一章 整数的因子分解 1.1 唯一分解定理 1.2 辗转相除法(欧氏除法) 1.3 Mersenne素数和Fermat素数 1.4 整系数多项式 1.5 环Z和Z[ω] 习题一 第二章 同余式 2.1 孙子定理 2.2 剩余类环 2.3 Euler函数ρ(m) 2.4 同余方程 2.5 原根 2.6 缩系的构造 习题二 第三章 二次剩余 3.1 定义及Euler判别条件 3.2 Legendre符号 3.3 Jacobi符号 习题三 第四章 特征 4.1 剩余系的表示 4.2 特征 4.3 原特征 4.4 特征和 4.5 Gauss和 习题四 第五章 连分数 5.1 简单连分数 5.2 用连分数表实数 5.3 最佳渐近分数 5.4 Legendre判别条件 习题五 第六章 代数数域 6.1 代数整数 6.2 Dedekind整环 6.3 阶的一些性质 第七章 椭圆曲线 7.1 椭圆曲线的群结构 7.2 除子类群 7.3 同种映射 7.4 Tate模和Weil对 7.5 有限域上的椭圆曲线 习题七 第八章 在密码学中的一些应用 8.1 RSA公钥密码 8.2 Uiffie-Hellman体制 8.3 ElGamal算法 8.4 基于背包问题的公钥密码 8.5 秘密共享 第九章 素性检验 9.1 Fermat小定理及伪素数 9.2 强伪素数及Miller-Rabin检验 9.3 利用n-1的因子分解的素性检验 9.4 利用n+1的因子分解的素性检验 9.5 分圆环素性检验 9.6 基于椭圆曲线的素性检验 第十章 大整数因子分解算法 10.1 连分数因子分解算法 10.2 二次筛法 10.3 Pollard的P-1因子分解算法 10.4 椭圆曲线因子分解算法 10.5 数域筛法 习题十 第十一章 椭圆曲线上的离散对数 11.1 椭圆曲线公钥密码 11.2 小步-大步法 11.3 家袋鼠和野袋鼠 11.4 MOV约化 11.5 FR约化 11.6 SSSA约化 11.7 有限域上离散对数的计算 第十二章 超椭圆曲线 12.1 超椭圆曲线的Jacobian 12.2 虚二次代数函数域 12.3 基于超椭圆曲线的公钥密码 附录 一些常用算法 A.1 不可约多项式的判别 A.2 有限域中平方根的求解 A.3 有限域上的分解 A.4 Hensel引理 A.5 格 A.6 Z[x]中多项式的分解 参考文献 免责申明:此书是我在网络上获取的,希望对大家有用。资源版权归作者及其公司所有,如果你喜欢,请购买正版。~~~

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值