• 《信息安全数学基础一》第一章笔记


    《信息安全数学基础一》第一章笔记

    整除

    • 定义
      在整数域内,若 (a = qcdot b) ,则 (b) 整除 (a) ,记作 (a | b)
    • 性质
      • (a | b, b | c) ,则 (a | c)
      • (c | a_{i}, i = 1, 2, .., n)(c) 也整除 (a_{i}) 的线性组合

    素数,合数

    • 素数定义
      除了 (1) 和自身外,没有因数的数,称之为素数,也称为质数,其他数被称为合数。
    • (2) 是最小的素数

    素数判别

    • 一个定理
      对于合数 (n) ,必然存在不超过 (sqrt{n}) 的质因子。

      (n = pq),设 (p) 是最小因子,则 (pleq q)
      (n = pq geq p^{2})
      所以 (pleq sqrt{n})
      假设 (p) 是合数,则可以继续进行分解,与题设不符,所以 (p) 是质数。

    • 定理推论
      对于一个数 (n) ,若其不存在不超过 (sqrt{n}) 的质因子,那么其为素数。
    • 素数平凡判别
      枚举 (2-sqrt{n}) 内的素数,若不存在 (n) 的因子,那么 (n) 是素数,否则是合数。

    筛法

    筛法用来求 (2-n) 内的所有素数。

    埃式筛

    • 算法思想
      先假设所有数均为素数。
      对于数 (x) ,若其为素数,则筛去其所有倍数,并标记该值为合数;若其为合数,则不做改动。

    • 算法优化

      • 对于素数 (x) ,从 (x) 倍开始枚举。因为对于倍数 (x * y, y < x) ,已经在判断 (y) 时筛去了。
        例如 (35 = 5 * 7) ,在判断素数 (5) 时筛去了一次,在判断素数 (7) 时就可以直接从 (49 = 7 * 7) 开始筛。
      • (n) 以内的素数判断到 (sqrt{n}) 即可。
    • 时间复杂度为 (O(nlglgn))

    • (code)

      int n, vis[N];
      void getPrime()
      {
          for(int i = 2; i <= n; ++i) vis[i] = 1;
          for(int i = 2; i * i <= n; ++i){
              if(vis[i]){
                  for(int j = i * i; j <= n; j += i)
                      vis[j] = 0;
              }
          }
      }
      

    欧拉筛

    • 埃式筛的困境及改善
      有些合数会被筛去多次,例如 (63 = 7 * 9 = 3 * 21)

    • 优化
      让每个合数只被其最小的质因子筛去一遍,就可以控制复杂度到 (O(n))

    • 细节

      • 先默认所有数都是素数,下面考虑内循环。
      • 若当前数是素数,将其所有的素数倍全部筛去,这保证了那些合数是被其最小素因子筛去的。
      • 若当前数是合数,筛去其素数倍 (x) ,若 (x) 能整除当前合数,则退出循环,因为继续循环得到的新倍数完全可以在之后被 (x) 整除。
    • (code)

      int n, prime[N], vis[N];
      int Euler_sieve()
      {
          int cnt = 0;//length of prime table
          for(int i = 2; i <= n; ++i)
              vis[i] = 1;
          for(int i = 2; i <= n; ++i){
              if(vis[i]) prime[++cnt] = i;
              for(int j = 1; j <= cnt && prime[j] * i <= n; ++j){
                  vis[prime[j] * i] = 0;
                  if(i % prime[j] == 0) break;
              }
          }
          return cnt;
      }
      

    进制转换

    考虑 (k) 进制,则一个数位 (digit) 的取值范围为 ([0, k))

    • 向十进制转换
      对于一个 (n)(k) 进制数,化为十进制数即为

    [sum_{i = 0}^{n - 1}a_{i}k^{i} = a_{n - 1}k^{n - 1} + a_{n - 2}k^{n - 2} + ... +a_{0} ]

    • 十进制数转为 (k) 进制
      先模 (k) 得到低位在 (k) 进制下的值,然后除 (k) 舍去低位。

      //k 进制的字符串向十进制转换
      int num = 0;
      for(int i = 0; i < s.length(); ++i)
          num *= 10, num += s[i] - '0';
      
      //十进制转换为 k 进制
      stack<int> sta;
      while(num) {
          sta.push(num % k);
          num /= k;
      }
      while(!sta.empty())
          cout<<sta.top()<<endl, sta.pop();
      

    最大公因数与最小公倍数

    • 二者定义
      字如其名。前者是最大的公因数,后者是最小的公倍数。

    • 关系

      [[a, b] = frac{ab}{(a, b)} ]

      [[a_{0}, a_{1}, ... ,a_{n}] = frac{prod_{i = 0}^{n}a_{i}}{(a_{0}, a_{1}, ... ,a_{n})} ]

      通过证明 ((frac{a}{(a, b)}, frac{b}{(a, b)}) = 1) ,以及 ((a, b) = 1, [a, b] = ab) 可以证明上式。

    欧几里得算法

    欧几里得算法即为辗转相除法计算最大公因数。

    • 定理 (1)
      (a = qcdot b + c) 中,((a, b) = (b, c))

      ((a, b) = d_{1}, (b, c) = d_{2})
      (d_{1}|a, d_{1}|b, d_{1}|c) ,所以 (d_{1}leq d_{2})
      同理 (d_{2}|b, d_{2}|c, d_{2}|a) ,所以 (d_{2}leq d_{1})
      所以 (d_{1} = d_{2})

    • 定理 (2)
      ((a, 0) = a)

      (a)(a) 的最大因数,(a)(0) 的因数,所以 ((a, 0) = a)

    • 欧几里得算法
      (r_{0} = a, r_{1} = b, r_{n + 1} = 0), 则有下列式子

      [r_{0} = r_{1}q_{1} + r_{2} \r_{1} = r_{2}q_{2} + r_{3} \... \r_{i - 1} = r_{i}q_{i} + r_{i + 1} \... \r_{n - 1} = r_{n}q_{n} + r_{n + 1} ]

      依据定理 (1) 和定理 (2) ,辗转相除便可以得到最大公因数

    贝祖等式

    • (scdot a + tcdot b = (a, b))
    • 猜想 (r_{i} = s_{i}a + t_{i}b, iin [0, n]) ,利用数学归纳法可以证明此猜想。

    拓展欧几里得算法

    ((a, b)) 的同时求出 (scdot a + tcdot b = (a, b)) 的一个特解 (s_{0}, t_{0})

    • 递推式算法
      考虑已经由数学归纳法证明猜想

      [r_{i} = s_{i}a + t_{i}b, iin [0, n] ]

      联立

      [r_{i} = r_{i - 2} - q_{i - 1}r_{i - 1} ]

      得到递推式

      [s_{i} = s_{i - 2} - s_{i - 1}q_{i - 1} ]

      [t_{i} = t_{i - 2} - t_{i - 1}q_{i - 1} ]

      其中, (i)(2) 开始计数,即 (iin [2, n])

      [q_{i} = left [frac{r_{i - 1}}{r_{i}} ight ] ]

      [r_{i} = r_{i - 1} mod r_{i} = r_{i - 1} - q_{i}r_{i} ]

      其中, (i)(1) 开始计数,即 (iin [1, n - 1])

      考虑初值,易得

      [s_{0} = 1, s_{1} = 0 ]

      [t_{0} = 0, t_{1} = 1 ]

      [r_{0} = a, r_{1} = b ]

      [q_{1} = left[frac{a}{b} ight] ]

      如此一路递推,便可以得到最终的特解。

      考虑空间优化,可以只用 (s_{0}, s_{1}, t_{0}, t_{1}, r_{0}, r_{1}, q_{0}) 几个变量来进行递推,赋值的时候需要再引入中间变量来保存值,最终的空间复杂度为一个常数。

      int s0, s1, s2, t0, t1, t2, r0, r1, r2, q1, cnt;
      pair<int, int> exgcd(int a, int b)
      {
          s0 = 1, s1 = 0, t0 = 0, t1 = 1;
          r0 = b, r1 = a % b, q1 = a / b;
          while(r1) {
              s2 = s1, s1 = s0 - s1 * q1, s0 = s2;
              t2 = t1, t1 = t0 - t1 * q1, t0 = t2;
              q1 = r0 / r1;
              r2 = r1, r1 = r0 - q1 * r1, r0 = r2;
          }
          return pair<int, int> {s1, t1};
      }
      
    • 递归算法
      考虑递归终点,有 (b = 0, (a, b) = a) ,所以 (s = 1, t = 0)
      考虑已经求出了 ((b, a \% b)) 情况下的 (x', y') ,回溯求解 ((a, b)) 下的 (x, y)

      [bx'+ (a - left lfloor frac{a}{b} ight floor b)y' = ax + by ]

      解出

      [x = y', y = x' - left lfloor frac{a}{b} ight floor y' ]

      如此一路回溯,可以得到一组特解 (x_{0}, y_{0})

    • (code)

      int exgcd(int a, int b, int &x, int &y)
      {
          if(!b) {x = 1, y = 0; return a;}
          int r = exgcd(b, a % b, y, x);//y的值被修改为x',x的值被修改为y'
          y -= (a / b) * x;
          return r;
      }
      

    算术基本定理

    • 算术基本定理
      对于任意大于 (1) 的整数 (n) ,其可以拆分为质因子幂的乘积的形式,称之为 (n) 的标准分解式:

      [n = prod_{i = 1}^{k}p_{i}^{alpha_{i}} ]

      其中, (p_{i}) 是素数。
    • 因数个数
      由乘法原理知道, (n) 的因数的个数为

      [n = prod_{i = 1}^{k}(1 + alpha_{i}) ]

  • 相关阅读:
    cube.js 基于http 通道的数据实时更新bug 解决
    cube.js 基于http 通道的数据实时更新
    cube.js websocket 实时数据更新说明
    phpspy 进行php 项目性能分析
    使用groovy 下载maven依赖包
    java 几个不错的代码生成工具
    语言性能分析工具总结
    使用rbspy 分析ruby应该性能
    dremio v18.0 软件包可以使用了
    nginx rewrite查询字符串重写问题
  • 原文地址:https://www.cnblogs.com/ChenyangXu/p/12571937.html
Copyright © 2020-2023  润新知