· 博主语文体育老师教的.
· 本文年龄限定 16+
· 吐槽上面 2条的都是⑨
事情要从昨天考试说起.
Pro 2 : 给定n, 求n最少能分成多少个完全平方数.
那啥做法就不扯淡了, 各种特判.
核心算法的素数判定以及大数分解完全就被一大堆特判淹没了.
推荐:
1、《64位以内Rabin-Miller+强伪素数测试和Pollard+rho+因数分解算法的实现》 (里面的伪代码非常奇葩.... 传说中的PC语言 ?!)
2、CSDN Fisher_jiang《Miller_Rabin素数测试》- http://blog.csdn.net/fisher_jiang/article/details/986654
3、《算法导论》自己翻去.
素数判定 (Miller_Rabin)
个人就不吐槽了.
总之就是用费马小定理 a^(p - 1) ≡ 1 (mod p) (其中 a, p 互质且 p 为质数) 确定 p 是否为质数的较高正确性.
证明不管 (确切地说没有精力去看), 只剽算法=. =
只要多选取几个 a , 就可以使得算法正确率非常高. 目前有以下二种方法:
1、随机取.
2、选取前 k 个质数.
第一种出错率 1 / 4^s ( s为随机选取 a 的个数), 第二种在 [1] 中有详细介绍, 取前 10 个质数就可以满足在int64范围内100%正解.
那啥怎么求a^(p - 1) ? 快速幂...... ( 不可能有人都看到这种东西了还不会快速幂吧......)
大数分解 (Pollard_Pho)
极度吐槽. 让我悲剧了一下午的东西.
总之就是随机选 2 个数a, b (b < a < n), 判断 gcd(a - b, n) 是否大于 1 就可以了.
也许会说这种随机怎么可能那么快出解啊! 什么的.
但是若构造一个循环节 (a1, a2, a3, a4, a5 ... ak) (在模n意义下循环), 并且使 b 也有一定规律的话.
根据各种论文, 能在O(√p) 内找出 n 的一个因子 p (注意是因子, 不是质因子, 所以找出 p 后要递归处理)
一般用 f(x) = x² + c 来构造循环节. 其余证明见 [1] (个人认为应该是算导以外最好的了)
以下是伪代码 :
Function Pho(n)
if N IS A PRIME then // n 是个质数就直接退出
return error;
x = y = x0; c = random[1, n); // 初始化
k = 0; i = 1; d = 1;
while (true) do
++k;
x = f(x), d = gcd(x - y, n); // 构造 x
if d ∈ (1, n) then return d; // 找到一个因子
if d = n then c = random[1, n); // x - y = 0, 表示 c 这个常数因子有点萎... 换一个. (悲剧一下午, Orz 屏屏哥)
if k = i then y = x, i <<= 1; // 更新 y
End Function
以上是Pollard 的 Brent优化代码, 同样, 具体见 [1].
下面是完全平方数分解的 Code ( 里面自带素数判定以及大数的质因数分解 )
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define error -1
#define ll long long
const int po[11] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
using namespace std;
int test;
ll n, p[10010];
ll gcd(ll a, ll b)
{
ll x;
while (x = a, b) a = b, b = x % b;
return a;
}
ll mul(ll a, ll b, ll c)
{
ll plas = 0;
for (ll i = 1; i <= b; i <<= 1, a = (a + a) % c)
if (b & i) plas = (plas + a) % c;
return plas;
}
ll Rollard_Brent(ll n)
{
ll x = 1, y = 1, d = 1; y = x;
int k = 0, i = 1, z = rand() + 1;
while (true)
{ ++k;
x = (mul(x, x, n) + z) % n;
d = gcd((x - y + n) % n, n);
if (d > 1 && d < n) return d;
if (k == i) y = x, i <<= 1;
if (d == n) z = rand() + 1;
}
}
bool Miller_Rabin(ll n)
{
ll a, w, sec;
for (int i = 1; i <= 10; ++i)
{ sec = 1; if (n == po[i]) continue;
for (a = po[i], w = 1; w < n; w <<= 1, a = a * a % n)
if ((n - 1) & w) sec = sec * a % n;
if (sec != 1) return false;
}
return true;
}
int tap(ll n)
{
ll plas = n;
int head = 1, tail = 1, top = 0;
p[tail] = n;
while (head <= tail && n != 1)
if (Miller_Rabin(p[head])) { bool wis = 0;
while (n % p[head] == 0) wis = !wis, n /= p[head];
if (wis && (p[head] & 3) == 3) goto Compare; ++head;
}
else {
ll vec = Rollard_Brent(p[head]);
p[++tail] = p[head] / vec; p[++tail] = vec;
++head; }
return 2;
Compare:
while (!(plas & 3)) plas >>= 2;
if (((plas - 7) & 7) == 0) return 4;
else return 3;
}
int main()
{
freopen("p2.in", "r", stdin);
freopen("p2.out", "w", stdout);
srand((unsigned)time(NULL));
scanf("%d", &test);
for (; test--; )
{
scanf("%I64d", &n);
int w = (int) sqrt((double) n); if ((ll) w * w == n) { printf("1\n"); continue; }
printf("%d\n", tap(n));
}
}