高级数论
cancan123456 · · 个人记录
Miller-Rabin
如何判定一个数
我们考虑费马小定理:设
那么它的逆定理是否成立呢?答案是错误的,最小反例
然后我们考虑另一个定理:二次探测定理,设
的解为
我们用这个定理的逆否定理:设
有不满足
那么我们就可以设计出这样的算法:
首先把
正确性:
若
Pollard-Rho
Lucas & exLucas
求
按照惯例,我们先考虑
如果
然后我们又有一个定理:
证明不会。
那么我们考虑
先把
考虑如何求出
显然无法直接求,因为
我们考虑求出
那么现在问题就是求出:
考虑把
我们发现因为
所以:
然后我们发现,
边界条件
现在的问题就是怎么求
边界条件
CRT & exCRT
求解以下同余方程组:
首先考虑
我只说最普遍的解法:设
现在考虑
变形:
然后用 exgcd 求出
然后显然就有:
然后把
BSGS & exBSGS
BSGS 用于求解以下方程:
其中
考虑根号分治,设
那么考虑求出所有
具体实现可以把 map 或者哈希表里。
原根与 N 次剩余
原根是个非常有用的东西。
首先我们定义阶:由欧拉定理可知如果
定义原根:我们称一个数
一些定理:
-
-
-
若已知
m 的一个原根g ,那么对于所有\gcd(i,\varphi(m))=1 ,都有g^i 是原根。 -
原根存在定理:若
m 可以被表示为以下形式:①
m=2 或m=4 。② 存在任意奇素数
p 和一正整数a 使得m=p^a 或m=2p^a 。则
m 有原根。 -
原根判定定理:若
g^{\varphi(m)}\equiv1\pmod m 且对于\varphi(m) 的每个质因子p ,g 都满足g^{\frac{\varphi(m)}p} \not\equiv1\pmod m ,则g 是m 的一个原根。
现在我们要求 N 次剩余,即:找出
我们先讲一下
1. p 为任意奇素数
先判断是否有解,由费马小定理可知
假设
假设
综上所述,我们证明了
ps:其实上面这个
还有个定理:模
然后就是神仙构造了。
我们先随机找到一个
证明:
引理 1
证明:
引理 2
考虑二项式定理展开,因为有个阶乘,所以除了
现在证明原定理:
那么
算一个简单的期望可以知道期望
然后考虑
2.p=p_0^a ,其中 p_0 为任意奇素数
设
若
如果
代码
// Miller-Rabin
#include <cstdio>
using namespace std;
typedef __int128 ll;
ll pow(ll a, ll b, ll p) {
ll ans = 1;
while (b != 0) {
if ((b & 1) == 1) {
ans = ans * a % p;
}
a = a * a % p;
b = b / 2;
}
return ans;
}
const int pcnt = 12;
const ll prime[pcnt] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
ll check(ll a, ll p) {
if (p % a == 0) {
return false;
}
if (pow(a, p - 1, p) != 1) {
return false;
}
ll d = p - 1;
while ((d & 1) == 0) {
d /= 2;
ll temp = pow(a, d, p);
if (temp == p - 1) {
return true;
} else if (temp != 1) {
return false;
}
}
return true;
}
bool miller_rabin(ll n) {
if (n <= 40) {
for (int i = 0; i < pcnt; i++) {
if (n == prime[i]) {
return true;
}
}
return false;
} else {
for (int i = 0; i < pcnt; i++) {
if (n == prime[i]) {
return true;
}
if (n % prime[i] == 0) {
return false;
}
if (!check(prime[i], n)) {
return false;
}
}
return true;
}
}
int main() {
long long n;
while (scanf("%lld", &n) != EOF) {
if (miller_rabin(n)) {
printf("Y\n");
} else {
printf("N\n");
}
}
return 0;
}
// Lucas
#include <cstdio>
using namespace std;
typedef long long ll;
const int P = 100005;
ll fac[P], invfac[P];
ll pow(ll a, ll b, ll p) {
ll ans = 1;
while (b != 0) {
if (b & 1 == 1) {
ans = (ans * a) % p;
}
a = a * a % p;
b = b / 2;
}
return ans;
}
ll C(ll n, ll m, ll p) {
if (m > n) {
return 0;
} else {
return (fac[n] * invfac[m]) % p * invfac[n - m] % p;
}
}
ll Lucas(ll n, ll m, ll p) {
if (m == 0) {
return 1;
} else {
return C(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}
}
void inline solve() {
ll n, m, p;
scanf("%lld %lld %lld", &n, &m, &p);
fac[0] = 1;
for (int i = 1; i < p; i++) {
fac[i] = (fac[i - 1] * i) % p;
}
invfac[p - 1] = pow(fac[p - 1], p - 2, p);
for (int i = p - 2; i >= 0; i--) {
invfac[i] = invfac[i + 1] * (i + 1) % p;
}
printf("%lld\n", Lucas(n + m, n, p));
}
int main() {
int t;
scanf("%d", &t);
for (int i = 1; i <= t; i++) {
solve();
}
return 0;
}
// BSGS
#include <cstdio>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;
ll BSGS(ll a, ll b, ll p) {
ll t = sqrt(p);
map < ll , ll > hash;
for (ll bpowerai = b, i = 0; i < t; i++, bpowerai = bpowerai * a % p) {
hash[bpowerai] = i;
}
ll powerat = 1;
for (ll i = 0; i < t; i++) {
powerat = powerat * a % p;
}
for (ll powerati = 1, i = 0; i <= t; i++) {
ll j = hash.find(powerati) == hash.end() ? -1 : hash[powerati];
if (j != -1) {
return i * t - j;
}
powerati = powerati * powerat % p;
}
return -1;
}
int main() {
ll p, a, b;
scanf("%lld %lld %lld", &p, &a, &b);
ll ans = BSGS(a, b, p);
if (ans == -1) {
printf("no solution");
} else {
printf("%lld", ans);
}
return 0;
}
// CRT
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 100005;
int n;
ll a[N], b[N];
ll ans;
void exgcd(ll a, ll b, ll & x0, ll & y0) {
if (b == 0) {
x0 = 1;
y0 = 0;
} else {
exgcd(b, a % b, y0, x0);
y0 -= a / b * x0;
}
}
ll inv(ll a, ll b) {
ll x0, y0;
exgcd(a, b, x0, y0);
x0 = (x0 + b) % b;
return x0;
}
void CRT() {
ll M = 1;
for (int i = 1; i <= n; i++) {
M *= b[i];
}
for (int i = 1; i <= n; i++) {
ll Mi = M / b[i];
ll ti = inv(Mi, b[i]);
ll xi = Mi * ti;
ans = (ans + a[i] * xi) % M;
}
}
signed main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld %lld", b + i, a + i); // 题目里是 x = a (mod b)
a[i] %= b[i];
}
CRT();
printf("%lld", ans);
return 0;
}
// exCRT
#include <cstdio>
using namespace std;
typedef __int128 ll;
const int N = 100005;
ll mult(ll n, ll k, ll mod){
ll ans = 0;
while (k != 0) {
if ((k & 1) == 1) {
ans = (ans + n) % mod;
}
k >>= 1;
n = (n + n) % mod;
}
return ans;
}
ll inline fix(ll & a, ll mod) {
return a = ((a % mod) + mod) % mod;
}
ll gcd(ll a, ll b) {
if (b == 0) return a;
else return gcd(b, a % b);
}
ll lcm(ll a, ll b) {
return a / gcd(a, b) * b;
}
// 解方程 ax + by = gcd(a, b)
void exgcd(ll a, ll b, ll & x0, ll & y0) {
if (b == 0) {
x0 = 1;
y0 = 0;
} else {
exgcd(b, a % b, y0, x0);
y0 -= a / b * x0;
}
}
void merge(ll & a0, ll & b0, ll a1, ll b1) {
ll k0, k1;
exgcd(b0, b1, k0, k1);
k0 *= a1 - a0;
k0 /= gcd(b0, b1); // 只需要用一个
a0 = a0 + b0 * k0; // 一个特解
b0 = lcm(b0, b1);
fix(a0, b0);
}
int n;
ll a[N], b[N];
ll ansa, ansb;
ll ans;
void excrt() {
// 解方程组
// x = a1 (mod b1)
// x = a2 (mod b2)
// ......
// x = an (mod bn)
ansa = a[0];
ansb = b[0];
for (int i = 1; i < n; i++) {
merge(ansa, ansb, a[i], b[i]);
}
ans = ansa;
}
signed main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%lld %lld", b + i, a + i); // 题目里是 x = a (mod b)
a[i] %= b[i];
}
excrt();
printf("%lld", ans);
return 0;
}
// 原根
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll N = 1000005;
bool isnp[N], have_g[N];
ll prime[N], pcnt;
ll phi[N];
void init() {
isnp[1] = false;
phi[1] = 1;
for (ll i = 2; i < N; i++) {
if (!isnp[i]) {
prime[++pcnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= pcnt && i * prime[j] < N; j++) {
isnp[i * prime[j]] = true;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
have_g[2] = have_g[4] = true;
for (ll i = 2; i <= pcnt; i++) {
for (ll j = 1; j * prime[i] < N; j *= prime[i]) {
have_g[j * prime[i]] = true;
}
for (ll j = 2; j * prime[i] < N; j *= prime[i]) {
have_g[j * prime[i]] = true;
}
}
}
ll factor[N], fcnt;
void get_factor(int n) {
fcnt = 0;
for (ll i = 2; i * i <= n; i++) {
if (n % i == 0) {
factor[++fcnt] = i;
while (n % i == 0) {
n /= i;
}
}
}
if (n > 1) {
factor[++fcnt] = n;
}
}
ll pow(ll a, ll b, ll p) {
ll ans = 1;
while (b != 0) {
if ((b & 1) == 1) {
ans = ans * a % p;
}
a = a * a % p;
b = b / 2;
}
return ans;
}
bool check(ll g, ll n) {
if (pow(g, phi[n], n) != 1) {
return false;
}
for (ll i = 1; i <= fcnt; i++) {
if (pow(g, phi[n] / factor[i], n) == 1) {
return false;
}
}
return true;
}
ll get_min_g(ll n) {
get_factor(phi[n]);
for (ll i = 1; i < n; i++) {
if (check(i, n)) {
return i;
}
}
return 0;
}
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
ll all_g[N], gcnt;
void get_all_g(ll n) {
gcnt = 1;
ll prod = get_min_g(n);
all_g[gcnt] = prod;
for (int i = 2; i < phi[n]; i++) {
prod = prod * all_g[1] % n;
if (gcd(i, phi[n]) == 1) {
all_g[++gcnt] = prod;
}
}
sort(all_g + 1, all_g + gcnt + 1);
}
int main() {
init();
int t;
scanf("%d", &t);
for (ll n, d; t != 0; t--) {
scanf("%lld %lld", &n, &d);
if (have_g[n]) {
get_all_g(n);
printf("%lld\n", gcnt);
for (int i = 1; i <= gcnt / d; i++) {
printf("%lld ", all_g[i * d]);
}
printf("\n");
} else {
printf("0\n\n");
}
}
return 0;
}
// Cipolla 算法
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long ll;
ll n, p;
ll w;
struct Complex {
// a + b sqrt(w) 其中 w 为模 p 意义下的非二次剩余
ll a, b;
Complex() {
a = b = 0;
}
};
Complex operator * (const Complex & a, const Complex & b) {
Complex c;
c.a = (a.a * b.a % p + w * a.b % p * b.b % p) % p;
c.b = (a.b * b.a % p + a.a * b.b % p) % p;
return c;
}
Complex pow(Complex a, ll b) {
Complex ans;
ans.a = 1;
while (b != 0) {
if ((b & 1) == 1) {
ans = ans * a;
}
b /= 2;
a = a * a;
}
return ans;
}
ll pow(ll a, ll b, ll p) {
int ans = 1;
while (b != 0) {
if ((b & 1) == 1) {
ans = ans * a % p;
}
b /= 2;
a = a * a % p;
}
return ans;
}
ll Legendre(ll n, ll p) {
return pow(n, (p - 1) / 2, p);
}
ll min(ll a, ll b) {
return a < b ? a : b;
}
ll max(ll a, ll b) {
return a > b ? a : b;
}
void inline solve() {
scanf("%lld %lld", &n, &p);
if (n == 0) {
printf("0\n");
} else if (Legendre(n, p) == p - 1) {
printf("Hola!\n");
} else {
ll a;
while (true) {
a = rand() % p;
w = (a * a % p - n + p) % p;
if (Legendre(w, p) == p - 1) {
break;
}
}
Complex res;
res.a = a;
res.b = 1;
res = pow(res, (p + 1) / 2);
ll ans = res.a;
printf("%lld %lld\n", min(ans, p - ans), max(ans, p - ans));
}
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}