• 整数分解及素性判断学习笔记


    整数分解

        整数分解事实上有三个子问题:

        1. 合性判断。即判断一个数是否为合数。

        2. 素性判断。即确切判断一个数是否为素数。

        3. 寻找可能的质因子。  

    一个大概率的素性判断——Miller-Rabin算法

    费马小定理

        若p为质数,那么

        当我们要判断整数p是否为素数时,若,则p为合数。

        但若找不到满足条件的x,p只有99.999745%的概率为素数。因为存在一种数——Carmichael卡迈克尔数。

    卡迈克尔数

        若p为合数,且,则称p为卡迈克尔数。

        卡迈克尔数有至少三个不同素因子。

        1e8以内只有255个卡迈克尔数,前三个为561,1105,1729。

        此时考虑一个新的定理:欧几里得引理。

    欧几里得引理

        若p为素数,且,,则

        证明:

           

        首先将p写成的形式,那么对于已选择的x,首先运算,然后将这个数平方,得,重复此步骤k-1次,得,即

        那么在这k步中,若出现了1,而上一次的结果不是1或-1,则p为合数;若最后结果不为1,则p为合数。

        那么当p通过一次x的测试,则p为合数的概率为1/4;那么若p通过5次测试,素性判断正确的概率为99.9%。

        如果选择x=2,3,5,7,那么在2.5*10^13内仅有3215031751会判断错误。

    一个复杂度并不太好的素性判断

        除了2,所有素数都为奇数。对于一个奇数p,若它为合数,则可以写成的形式。令,则。若为完全平方数,则p为合数。

        但这个算法十分不优秀..

    优化掉这个不太优秀的算法——Pollard Rho

        考虑这样一个问题:有没有办法快速且高概率的找到一个小因子呢。

        很容易发现一个不高速低概率的方式:随机试除。

        这个算法概率极低,基本相当于从[1,N]随机选择一个数,这个数=x的概率——1/N。

        但考虑这样一个算法:从[1,N]随机选择两个数a和b,|a-b|=x的概率。显然是2/N。

        这个优化非常小,但我们再优化一下这个算法:从[1,N]随机选择k个数x1...xk,存在一对i和j使得|xi-xj|=x的概率。

        此时考虑一个经典问题——Birthday paradox生日悖论。

    生日悖论

        随机选择N个人,设事件P为这N个人中存在两个人生日相同。求P发生的概率。

        考虑没有两人生日相同的概率。将人一个一个加入,第二个人和第一个人生日不同的概率为,第三个人和前两个人生日不同的概率为...,第n个人和前面所有人生日不同的概率为

       

        当时,P发生的概率大于50%。

        考虑推广生日悖论。生日悖论等价于N个数中随机选k个数,存在一对数差为0的概率。可以将其推广为:N个数中随机选k个数,存在一对数差为x的概率。即上方的问题。

        也就是说,如果我们选择个数,就可以50%的概率找到N的一个小因子。但是可能太过巨大,无法存在内存中,需要特殊处理。

        考虑一种流水线的随机方式和floyd判环方法。

    Pollard推荐的随机数生成方法

        定义。a可以随机生成或直接用a=2。那么随机选取后,,每次和前一个值作差即可。

        但当伪随机数出现循环节,后面的伪随机数就没有意义了,我们需要判断掉这种情况。

        这里用到的判断方法为floyd算法。

        考虑跑道上的两个人,一个人的速度为另一个人的两倍。那么当跑道是环形,第二个人一定可以追上第一个人。这样就可以不消耗空间的判断是否出现了循环节。

     1 int find_factorplus(int N) {
     2     a = 2;
     3     b = a;
     4     do {
     5         a = f(a);//a runs once
     6         b = f(f(b));//b runs twice as fast
     7         p = GCD( abs( b - a ) , N);
     8         if( p > 1 ) return p;//Found factor: p
     9     } while( b != a );
    10     return 0;//Failed. :-(
    11 }

        这样我们就得到了Pollard Rhd算法。

    Pollard Rho与Miller-Rabin的结合——一个优秀的整数分解算法

        Pollard Rho算法的优势是可以在的复杂度内得到一个因子p;而当N的因子少而大时,我们可以使用M_R算法来验证下去。这样就得到了一个大概率且高效的整数分解算法。

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 
      4 typedef long long LL;
      5 
      6 const int ps[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
      7 const int pcnt = sizeof(ps) / sizeof(int);
      8 
      9 inline LL quick_mul(LL a, LL b, LL m)
     10 {
     11     LL d = ((long double)a / m * b + 1e-8);
     12     LL r = a * b - d * m;
     13     return r < 0 ? r + m : r;
     14 }
     15 
     16 inline LL quick_pow(LL a,LL b,LL m)
     17 {
     18     LL res = 1;
     19     for(; b; b >>= 1, a = quick_mul(a, a, m))
     20         if (b & 1)
     21             res = quick_mul(res, a, m);
     22     return res;
     23 }
     24 
     25 inline LL gcd(LL a, LL b)
     26 {
     27     if (!a || !b)
     28         return a + b;
     29     int t = __builtin_ctzll(a | b);
     30     a >>= __builtin_ctzll(a);
     31     do
     32     {
     33         b >>= __builtin_ctzll(b);
     34         if (a > b)
     35         {
     36             LL t = b;
     37             b = a;
     38             a = t;
     39         }
     40         b -= a;
     41     }
     42     while (b != 0);
     43     return a << t;
     44 }
     45 
     46 inline int Miller_Rabin(LL n)
     47 {
     48     if (n == 1)
     49         return 0;
     50     if (n == 2 || n == 3 || n == 5)
     51         return 1;
     52     if (!(n & 1) || (n % 3 == 0) || (n % 5 == 0))
     53         return 0;
     54     LL m = n - 1;
     55     int k = 0;
     56     while (!(m & 1))
     57         m >>= 1, ++k;
     58     for (int i = 0; i < pcnt && ps[i] < n; ++i)
     59     {
     60         LL x = quick_pow(ps[i], m, n), y = x;
     61         for (int i = 0; i < k; ++i)
     62         {
     63             x = quick_mul(x, x, n);
     64             if (x == 1 && y != 1 && y != n - 1)
     65                 return 0;
     66             y = x;
     67         }
     68         if (x != 1)
     69             return 0;
     70     }
     71     return 1;
     72 }
     73 
     74 inline LL next_rand(LL x, LL n, LL a)
     75 {
     76     LL t = quick_mul(x, x, n) + a;
     77     return t < n ? t : t - n;
     78 }
     79 
     80 const int M = (1 << 7) - 1;
     81 inline LL Pollard_Rho(LL n)
     82 {
     83     if (n % 2 == 0)
     84         return 2;
     85     if (n % 3 == 0)
     86         return 3;
     87     LL x = 0, y = 0, t = 1, q = 1, a = (rand() % (n - 1)) + 1;
     88     for (int k = 2; true; k <<= 1, y = x, q = 1)
     89     {
     90         for (int i = 1; i < k; ++i)
     91         {
     92             x = next_rand(x, n, a);
     93             q = quick_mul(q, abs(x - y), n);
     94             if (!(i & M))
     95             {
     96                 t = gcd(q, n);
     97                 if (t > 1)
     98                     break;
     99             }
    100         }
    101         if (t > 1 || (t = gcd(q, n)) > 1)
    102             break;
    103     }
    104     if (t == n)
    105         for (t = 1; t == 1; t = gcd(abs((x = next_rand(x, n, a)) - y), n));
    106     return t;
    107 }
    108 
    109 LL f[105];
    110 int cnt;
    111 
    112 void solve(LL n)
    113 {
    114     if (n == 1)
    115         return;
    116     if (Miller_Rabin(n))
    117     {
    118         f[cnt++] = n;
    119         return;
    120     }
    121     LL t = n;
    122     while (t == n)
    123         t = Pollard_Rho(n);
    124     solve(t);
    125     solve(n / t);
    126 }
    127 
    128 int main()
    129 {
    130     LL n;
    131     while (~scanf("%lld", &n))
    132     {
    133         cnt = 0;
    134         solve(n);
    135         sort(f, f + cnt);
    136         for (int i = 0; i < cnt; ++i)
    137             printf("%lld ", f[i]);
    138         putchar('
    ');
    139     }
    140     return 0;
    141 }

    原根的存在性及个数证明

       https://www.cnblogs.com/zhixingr/p/6822370.html

  • 相关阅读:
    python之元组
    python之dict
    python之list
    python之str字符串
    python之for循环
    Python的基本语法2
    Python的基本语法1
    初识python
    JS获取当天是周几
    EXCLE导入数据库
  • 原文地址:https://www.cnblogs.com/aseer/p/9627434.html
Copyright © 2020-2023  润新知