POJ 1811 *** Prime Test(详解Miiler_Rabin算法与Pollard_Rho算法)

2024-04-22 00:08

本文主要是介绍POJ 1811 *** Prime Test(详解Miiler_Rabin算法与Pollard_Rho算法),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题意:对于一个给定的数n,判断n是否为质数,n如果为质数,输出“Prime”,如果为合数,则输出其最小的素因子。


分析:其实这道题就是对于Miller_Rabin算法和Pollard_Rho算法的应用,具体的详解如下(刚买的椰子味身体乳香香的,感觉自己变成了一颗大椰子哈哈)


Miller_Rabin随机性素数测试方法:

先给出费马定理的定义:

如果p是素数,则a^(p-1)==1 mod p 对所有a∈Zp*都成立;Zp*为1到p-1中与p互质的数的集合。


法利用费马定理,试验了多个随机选取的基值a,来判断a^(p-1) ?= 1 mod p。
算法在实现的过程中会用到下面一些辅助过程,贴一下伪代码吧。//伪代码基本来自于算导

辅助过程1(求a^b mod n)
pow_mod(a,b,n)
1   d=1
2   while(b)
3      if b&1 
4         d=d*a mod n
5      a<<=1
5      b>>=1
6   return d


辅助 过程2(求a^(n-1) mod n)
辅助过程2与辅助过程1不同在于,过程2中间在寻找一个模n为1的非平凡平方根。贴了伪代码之后细说。
Witness(a,n)
1   let t and u be such that t>=1,u is odd,and n-1=u*2^t
2   Xo=pow_mod(a,u,n)
3   for i=1 to t
4      Xi=Xi-1^2 mod n
5      if Xi==1 and Xi-1 != 1 and Xi-1 != n-1
6         return true //一定是合数
7   if Xi != 1
8      return true //一定是合数
9   return false  //不一定是合数</span>
上面代码是将a^(n-1) mod n 转换为 (a^u)^(2^t) mod n(第一行),然后第二行求出 Xo=a^u mod n的值之后继续进行计算。但是在计算过程中,5、6行代码利用了下面的定理:
如果p是一个奇素数且e>=1,则方程x^2==1 mod p^e
仅有两个解,即 x==1和x==-1

上述定理的证明如下:

x^2==1 mod p^e 等价于 p^e|(x-1)(x+1),但是他们不同时成立,否则p也能整除他们的差(x+1)-(x-1)=2。
如果gcd(p,x-1)==1,则 p^e|(x+1),则x==-1 mod p^e。
如果gcd(p,x+1)==1,则p^e|(x-1),则x=1 mod p^e。


但是如果一个数x,满足方程x^2==1 mod n,然而x却不等于1 或者 -1 ,则称x是一个以模n为1的非平凡平方根。


上述定理的逆否命题为:

如果存在模n为1的非平凡平方根,那么n不可能是奇素数或者奇素数的幂。即n为合数。

过程2中的5、6行代码,就是在判断X i 是否是模n为1的非平凡平方根,如果是,那么n为合数。


有了上面的基础之后再看Miller_Rabin的算法就比较轻松了,伪代码如下:

Miller_Rabin(n,s)
1   for i=1 to s
2      a=random(1,n-1)
3      if Witness(a,n)
4         return COMPOSITE //   definitely
5   return PRIME //   almost surely



Pollard_Rho算法:

这个算法主要是基于Birthday Trick来提高概率的。从头讲起。

对于一个很大的奇数n,如果想要知道他的因子,那么我们可以进行试除,从3一直试除到n-1,传统的试除法很暴力。

那么我们可以变得不一样,那就是从我们random(3,n-1),这样的试除法只做一次的话,成功找到n的因子的概率为1/(n-3),概率仍然很小,但是如果通过Birthday Trick,那么概率会大大提高。


Birthday Trick(生日悖论)如果一个房间里有23个或23以上的人,那么至少有两个人的生日相同的概率大于50%。



在[1,10000]中选一个数,选中50的概率为 1/10000
在[1,10000]中选两个数i、j,i-j==50的概率约为1/5000
那么在[1,10000]中选k个数,这k个数两两相减得到一个数为50的概率是多少呢?


嗯。。。事实上这个概率是随着k的增加而增加的,总之最后会近乎为1。

于是我们可以这样子想,我们随机的选取k个数,然后判断Xi-Xj是否为n的因子,但是其实这样做并没有什么用。。

但是如果我们把要求降低,比如,判断gcd(Xi-Xj,n)是否等于1,这样的话如果n=p*q(p、q都为素数)那么如果Xi-Xj=2p(3p,4p...)都是可以判断的了。


所以我们可以在[2,n-1]范围内选取k个数,来进行判断,但是这样做需要的数据量可能很大,不方便进行存储。于是这个时候Circle Detection就上场了。
我们并不需要一次性生成k个数,我们可以利用一个函数来依次生成一个一个数,并且进行相减的检查。这个函数生成的数看上去就像随机的一样。。
这个函数是:
f(x)=(x^2+a) mod n

这个函数最后生成的数据形状大概类似罗马字ρ(rho),对于这个函数,重要的一点探测环的出现。
探测环出现的算法大概是这个意思:如果A在一个圆圈上面行走,那么A怎么知道自己已经走了一圈了呢?那么这个时候需要B了, 如果B的速度是A的两倍,那么当B第一次追赶上A的时候,A一定已经走完一圈了。
这大概就是Pollar_Rho我能理解到的程度了,以后如果有更深入的理解那么继续补充。

Pollar_Rho的伪代码如下:
Pollar_Rho(n)
1   i=1
2   X1=random(0,n-1)
3   Y=X1
4   k=2
5   while(true)
7      i=i+1
8      Xi=(Xi-1^2-1) mod n
6      d=gcd(Y-X,n)
7      if d != 1 and d != n
8         return d
9      if i==k
10       Y=Xi
11       k+=k



poj1811
题目的代码下也有一些解释,模板基本来自于kuangbin。

代码如下:

#pragma warning(disable:4996)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<stack>
#include<queue>
#include<cstring>
#include<sstream>
#include<set>
#include<string>
#include<iterator>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;//计算 (a*b)%c.   a,b都是long long的数,直接相乘可能溢出的
//这里相乘是利用b=2^n1+2^n2+...+2^n,然后代入(a*b)%c中展开相乘
ll multi_mod(ll a, ll b, ll n) {a %= n;b %= n;ll res = 0;while (b) {if (b & 1) {res += a;res %= n;}a <<= 1;if (a >= n)a %= n;b >>= 1;}return res;
}//计算  n^a % mod
ll pow_mod(ll n, ll a, ll mod)
{if (a == 1)return n%mod;n %= mod;ll res = 1;while (a) {if (a & 1)res=multi_mod(res, n, mod);n = multi_mod(n, n, mod);a >>= 1;}return res;
}//以a为基,n-1=x*2^t      a^(n-1)==1(mod n)  验证n是不是合数
//一定是合数返回true,不一定返回false
bool check(ll a, ll n,ll x, ll t)
{ll res = pow_mod(a, x, n);ll last = res;for (int i = 1; i <= t; i++){res = multi_mod(res, res, n);if (res == 1 && last != 1 && last != n - 1)return 1;last = res;}if (res != 1)return 1;return false;
}// Miller_Rabin()算法素数判定
//是素数返回true.(可能是伪素数,但概率极小)
//合数返回false;bool Miller_Rabin(long long n)
{if (n<2)return false;if (n == 2)return true;if ((n & 1) == 0) return false;//偶数ll x = n - 1, t=0;while ((x&1)==0) { t++, x /= 2; }for (int i = 0; i < 20; ++i) {ll a = rand() % (n - 1) + 1;if (check(a, n, x, t))return 0;//合数}return 1;return true;
}//最大公约数的判断
ll gcd(ll a,ll b){if (a < 0)return gcd(-a, b);if (a == 0)return 1;while (b) {ll t = a%b;a = b;b = t;}return a;
}//寻找其中一个因子
ll pollard_rho(ll n,ll c) {ll i = 1, k = 2, x0, y;x0 = rand() % (n - 1) + 1;y = x0;while (1) {i++;x0 = (multi_mod(x0, x0, n) + c) % n;ll d = gcd(y - x0, n);if (d != 1 && d != n)return d;if (x0 == y)return n;if (i == k) {y = x0;k += k;}}
}//寻找素因子
ll factor[100];
int tot;
void findp(ll n) {if (Miller_Rabin(n)) {factor[tot++] = n;return;}ll p=n;while (p >= n)p = pollard_rho(n, rand() % (n - 1) + 1);findp(p);findp(n / p);
}int main(void) {int t;ll n;cin >> t;while (t--) {cin >> n;if (Miller_Rabin(n)) {cout << "Prime" << endl;continue;}tot = 0;findp(n);ll ans = factor[0];for (int i = 1; i < tot; ++i)if (factor[i]<ans)ans = factor[i];cout << ans << endl;}return 0;
}


这篇关于POJ 1811 *** Prime Test(详解Miiler_Rabin算法与Pollard_Rho算法)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/924379

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Prime Cryptarithm(简单哈希表暴搜剪枝)

思路: 1. 用一个 hash[ ] 数组存放输入的数字,令 hash[ tmp ]=1 。 2. 一个自定义函数 check( ) ,检查各位是否为输入的数字。 3. 暴搜。第一行数从 100到999,第二行数从 10到99。 4. 剪枝。 代码: /*ID: who jayLANG: C++TASK: crypt1*/#include<stdio.h>bool h

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s