zoukankan      html  css  js  c++  java
  • Pollard-Rho

    算法简介

    Pollard-Rho 主要用于质因数分解,是一个随机化算法。

    前置算法

    Miller-Rabin

    伪随机函数

    此算法用 (f_x=f_{x-1}^2+c mod n) 生成随机数用于寻找质因数。
    (其中,(f_1)(c) 可以随机生成, (n) 为正在分解的数)
    但是,这个函数会产生环,所以叫伪随机函数。

    判环

    考虑用两个指针,一个每次走一步,另一个每次走两步。
    如果两个指针的值在某一时刻相等,则找到了环。
    因为两个指针位置的差每次增加1,所以不用担心找不到环。

    算法主体

    如果当前的数是质数,记录一下,直接返回。
    判质数可用 Miller-Rabin 实现。
    用上面的伪随机函数生成 (f) 数组。
    根据生日悖论,随机两个数 (x)(y) ,使得 (gcd(|x-y|,n)>1) 的概率比只随机一个数的概率要大。
    所以,用判环使用的两个指针的值作为 (x)(y)
    (d=gcd(|x-y|,n))
    (d>1)(d<n),则递归处理 (d)(frac{n}{d}) 两个部分。
    同时,如果找到了环,且还没有找到合法的因数,就重新随机 (f_1)(c)

    现在的代码

    #include<iostream>
    #include<cstdlib>
    #include<cstdio>
    #include<ctime>
    #define int long long
    #define LD long double
    #define ull unsigned long long
    using namespace std;
    int T,n,ans;
    int MUL(int a,int b,int p) //a*b%p
    {
    	int x=(LD)a/p*b;
    	return ((ull)a*b-(ull)x*p+p)%p;
    }
    int POW(int a,int b,int p) //a^b%p
    {
    	if(!b) return 1;
    	if(b==1) return a;
    	int sum=POW(a,b/2,p);
    	if(b%2) return MUL(MUL(sum,sum,p),a,p);
    	return MUL(sum,sum,p);
    }
    int MAX(int x,int y)
    {
    	return x>y?x:y;
    }
    int ABS(int x)
    {
    	return x>0?x:-x;
    }
    int gcd(int x,int y)
    {
    	if(!y) return x;
    	return gcd(y,x%y);
    }
    int f(int x,int c,int p)
    {
    	return (MUL(x,x,p)+c)%p;
    }
    bool MR(int x)
    {
    	if(x==0||x==1) return false;
    	if(x==2) return true;
    	if(x%2==0) return false;
    	int p=x-1,q=0;
    	while(p%2==0) q++,p/=2;
    	for(int i=1;i<=10;i++)
    	{
    		int a=rand()%(x-1)+1;
    		if(POW(a,x-1,x)!=1) return false;
    		int lst=1;
    		for(int j=0;j<q;j++)
    		{
    			int t=POW(a,(1ll<<j)*p,x);
    			if(t==1&&lst!=1&&lst!=x-1) return false;
    			lst=t;
    		}
    		if(lst!=1&&lst!=x-1) return false;
    	}
    	return true;
    }
    int find(int x)
    {
    	if(x%2==0) return 2;
    	if(MR(x)) return x;
    	int t=rand()%(x+1);
    	int a=t,b=t;
    	int c=rand()%(x+1);
    	while(1)
    	{
    		a=f(a,c,x),b=f(f(b,c,x),c,x);
    		int d=gcd(ABS(a-b),x);
    		if(d>1&&d<x) return d;
    		if(a==b) return find(x);
    	}
    }
    void PR(int x)
    {
    	if(x<=1) return;
    	if(MR(x))
    	{
    		ans=MAX(ans,x);
    		return;
    	}
    	int y=find(x);
    	PR(y),PR(x/y);
    }
    signed main()
    {
    	srand(time(0));
    	scanf("%lld",&T);
    	while(T--)
    	{
    		scanf("%lld",&n);
    		if(MR(n)) puts("Prime");
    		else
    		{
    			ans=0,PR(n);
    			printf("%lld
    ",ans);
    		}
    	}
    	return 0;
    }
    

    黑科技 gcd

    上面的代码因为效率过慢过不了洛谷上的 模板题
    观察到代码中会使用很多次 gcd 函数,所以考虑优化计算 gcd 的方法。

    新算法
    设现在要计算 (a)(b) 两数的 gcd。
    (a)(b) 都是偶数,则递归计算 (2 imes gcd(frac{a}{2},frac{b}{2}))
    (a) 是偶数,且 (b) 是奇数,则递归计算 (gcd(frac{a}{2},b))
    (a) 是奇数,(b) 是偶数同理。
    (a)(b) 都是奇数,则递归计算 (gcd(|a-b|,a))
    证明:
    设原来 (a)(b) 的 gcd 为 (k)
    (a=kA)(b=kB)
    (|a-b|=k|A-B|)
    因为 (A)(B) 必然互质,
    所以,现在要证明的就是 (A)(|A-B|) 互质。
    为了方便,先假设 (A>B) ,去掉绝对值。相反的情况的证明也是类似的。
    考虑反证法。
    (A)(A-B) 不互质,
    (A=qx)(A-B=qy)
    (B=A-(A-B)=qx-qy=q(x-y)) ,所以 (A)(B) 不互质,不成立。
    所以前面的递归是成立的。

    把原算法中的 gcd 部分换成这种写法,并加上一些比较常见的卡常技巧即可轻松通过本题。
    贴一个用迭代实现的板子。

    //__builtin_ctzll(x) 函数用于求x在二进制中末尾0的个数
    int gcd(int x,int y)
    {
    	if(!x) return y;
    	if(!y) return x;
    	int t=__builtin_ctzll(x|y);
    	x>>=__builtin_ctzll(x);
    	do
    	{
    		y>>=__builtin_ctzll(y);
    		if(x>y) swap(x,y);
    		y-=x;
    	}while(y);
    	return x<<t;
    }
    
  • 相关阅读:
    JS案例
    JS案例--Tab栏切换
    currentBackgroundImage:获取按钮背景图片
    笔记:UITextView内容垂直居中方法
    笔记:载入viewcontroller的几种方式
    沙盒文件的创建(简单举例)
    笔记:iOS随机数与随机数据集
    四种传值方法(通知、block、属性、NSUserDefaults)
    笔记:沙盒文件的拷贝
    笔记:iOS字符串的各种用法(字符串插入、字符串覆盖、字符串截取、分割字符串)(别人的代码直接复制过来的,我脸皮有点厚)
  • 原文地址:https://www.cnblogs.com/zhs1/p/14075926.html
Copyright © 2011-2022 走看看