Числа Кармайкла и псевдопростые числа
Мотивация и контекст
Проверка простоты числа — одна из самых фундаментальных задач в программировании. Наивный алгоритм пробных делений работает за и непрактичен при . Решето Эратосфена ограничено малыми . Для соревновательных задач требуется эффективная проверка простоты одиночных чисел до .
Исторически первой эффективной идеей стала малая теорема Ферма: если простое, то для любого , не кратного . Это даёт быстрый вероятностный тест: если , то составное. Но обратное неверно!
Числа, "обманывающие" тест Ферма для всех оснований , называются числами Кармайкла. Их существование разрушает наивную идею "тест Ферма = тест простоты". В этой главе мы изучим природу этих чисел и построим надёжный детерминированный тест простоты.
Числа Кармайкла на практике:
- — наименьшее.
- — их бесконечно много (теорема Алфорда–Гранвилля–Померанса, 1994).
- До их — редки, но существуют в любом диапазоне.
Что мы построим:
- Критерий Корселта — характеристика чисел Кармайкла.
- Тест Миллера–Рабина — надёжный вероятностный тест.
- Детерминированный тест для через фиксированные основания.
- Тест Бейли–Псевдо–Простых (BPSW) — практически надёжный.
Теория
Малая теорема Ферма и псевдопростые
Теорема (Малая теорема Ферма). Если — простое, , то .
Доказательство. Числа образуют перестановку по модулю (т.к. обратимо). Перемножая: , откуда .
Определение. Составное называется псевдопростым по основанию (base- Fermat pseudoprime), если .
Пример. . Проверим: . Действительно, , и , поэтому . Число — псевдопростое по основанию 2.
Числа Кармайкла
Определение. Составное называется числом Кармайкла, если для всех с .
Иначе говоря, числа Кармайкла — "абсолютные" псевдопростые Ферма.
Теорема Корселта (1899). является числом Кармайкла тогда и только тогда, когда:
- squarefree (не делится на ни для какого простого ).
- Для каждого простого : .
Доказательство. () Пусть — число Кармайкла.
Squarefree: Предположим . Возьмём с (можно найти с ). Тогда . Если и , то при ... точнее, возьмём такое, что и . Но и означает — противоречие. Значит, для выполнено , что не допускается. Поэтому рассмотрим другой ...
Строгое доказательство: при , если , то . По CRT можно взять (если ). Тогда . Биномом: . Для этого быть нужно . Но значит , значит iff — невозможно. Значит — противоречие с определением. Значит squarefree.
(p-1) | (n-1): Пусть . Так как squarefree, . Возьмём — примитивный корень по модулю . По CRT с и . Тогда , т.е. , т.е. .
() Пусть squarefree и для всех . Для любого с : по каждому имеем (т.к. , ). По КТО: .
Следствие. Число Кармайкла имеет не менее трёх различных простых делителей.
Доказательство. Пусть с простыми. Тогда . Но . Если , то . Значит , т.е. — противоречие с .
Наименьшее число Кармайкла
. Проверим критерий Корселта:
- Squarefree: — различные простые. ✓
- : . ✓
- : . ✓ ()
- : . ✓ ()
Сильно псевдопростые числа (SPRP)
Тест Ферма недостаточен. Усилим его, используя свойство квадратных корней из 1.
Лемма. Если — нечётное простое и , то .
Доказательство. , и — простое, значит или .
Алгоритм Миллера–Рабина. Пусть , где нечётно. Число проходит тест Миллера–Рабина по основанию (является strong pseudoprime to base ), если выполнено одно из:
- , или
- для некоторого .
Теорема. Если — нечётное простое и , то всегда проходит тест Миллера–Рабина по основанию .
Доказательство. (Ферма). Разложим: . Рассмотрим последовательность . Первый член : условие выполнено. Иначе первый член : условие выполнено. Иначе : но . Тогда , но — противоречие с леммой.
Теорема (Рабин, 1980). Если составное, то доля оснований , по которым проходит тест МР, не превосходит .
Следствие: тест МР с случайными основаниями ошибается с вероятностью .
Детерминированный тест Миллера–Рабина
Для детерминированного теста нужно знать фиксированный набор оснований, гарантирующий правильный ответ для всех .
Теорема (Payam Pourali, 2020; сводная таблица):
| Граница | Необходимые основания |
|---|---|
Для задач соревновательного программирования (числа до ) достаточно оснований или просто (достаточно для ).
BPSW тест
Тест Бейли–Псевдо–Простых–Числа–Вайсса (BPSW) — комбинация:
- Тест МР по основанию 2.
- Тест Лукаса с сильным условием (strong Lucas pseudoprime test).
Ни одного BPSW-псевдопростого числа до не известно. Предположительно, их нет до и выше. На практике BPSW считается надёжным для любых в пределах вычислительных возможностей.
Числа Кармайкла и тест МР
Числа Кармайкла проходят тест Ферма по любому основанию, взаимно простому с . Но они не проходят тест Миллера–Рабина по большинству оснований.
Пример. , .
Тест МР по основанию : : . . и (). . . . . . , но ни на каком предыдущем шаге не было .
По условию теста МР: и ни одно не равно (хотя последнее равно ). Значит, проваливает тест МР по основанию 2 — правильно выявлен как составное!
Ключевые формулы (сводная таблица)
| Понятие | Формулировка |
|---|---|
| Малая теорема Ферма | простое |
| Критерий Корселта | — Кармайкла squarefree и |
| SPRP определение | или , где |
| Оценка Рабина | |
| Детерм. тест, | Основания: |
| BPSW | МР(2) + Сильный Лукас; нет контрпримеров |
Реализация на C++20 (с анализом сложности)
#include <bits/stdc++.h> using namespace std; using ll = long long; using ull = unsigned long long; using u128 = unsigned __int128; // ============================================================ // Модульное умножение без переполнения (n до 2^63) // ============================================================ ll mulmod(ll a, ll b, ll m){ return (__int128)a * b % m; } // Быстрое возведение в степень по модулю ll powmod(ll a, ll b, ll m){ ll res = 1; a %= m; if(a < 0) a += m; for(; b > 0; b >>= 1){ if(b & 1) res = mulmod(res, a, m); a = mulmod(a, a, m); } return res; } // ============================================================ // Тест Миллера–Рабина: проверяет n по основанию a // Возвращает true если n проходит тест (возможно простое) // ============================================================ bool miller_rabin_test(ll n, ll a){ if(n % a == 0) return n == a; // n - 1 = 2^s * d ll d = n - 1; int s = 0; while(d % 2 == 0){ d /= 2; s++; } ll x = powmod(a, d, n); if(x == 1 || x == n - 1) return true; for(int r = 1; r < s; r++){ x = mulmod(x, x, n); if(x == n - 1) return true; } return false; } // ============================================================ // Детерминированный тест простоты // Корректен для n < 3,317,044,064,679,887,385,961,981 // (покрывает все числа, встречающиеся в задачах) // Время: O(log^2 n) — 12 итераций MR // ============================================================ bool is_prime(ll n){ if(n < 2) return false; if(n == 2 || n == 3 || n == 5 || n == 7) return true; if(n % 2 == 0 || n % 3 == 0 || n % 5 == 0) return false; // Для n < 3,215,031,751 достаточно {2, 3, 5, 7} // Для n < 3,317,044,064,679,887,385,961,981 — следующий набор: for(ll a : {2LL, 3LL, 5LL, 7LL, 11LL, 13LL, 17LL, 19LL, 23LL, 29LL, 31LL, 37LL}){ if(n == a) return true; if(!miller_rabin_test(n, a)) return false; } return true; } // Компактная версия для n < 3.2 * 10^18 (достаточно для long long) bool is_prime_fast(ll n){ if(n < 2) return false; for(ll a : {2LL, 3LL, 5LL, 7LL, 11LL, 13LL, 17LL, 19LL, 23LL, 29LL, 31LL, 37LL}){ if(n == a) return true; if(n % a == 0) return false; } for(ll a : {2LL, 3LL, 5LL, 7LL, 11LL, 13LL, 17LL, 19LL, 23LL, 29LL, 31LL, 37LL}){ if(!miller_rabin_test(n, a)) return false; } return true; } // ============================================================ // Алгоритм Полларда–Ро для факторизации // Время: O(n^{1/4}) в среднем // ============================================================ ll pollard_rho(ll n){ if(n % 2 == 0) return 2; if(n % 3 == 0) return 3; // Случайные значения для нескольких попыток static mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count()); while(true){ ll x = rng() % (n - 2) + 2; ll y = x; ll c = rng() % (n - 1) + 1; ll d = 1; while(d == 1){ x = (mulmod(x, x, n) + c) % n; y = (mulmod(y, y, n) + c) % n; y = (mulmod(y, y, n) + c) % n; d = __gcd(abs(x - y), n); } if(d != n) return d; } } // Полная факторизация n void factorize(ll n, map<ll, int>& factors){ if(n == 1) return; if(is_prime(n)){ factors[n]++; return; } ll d = pollard_rho(n); factorize(d, factors); factorize(n / d, factors); } // ============================================================ // Проверка: является ли n числом Кармайкла? // ============================================================ bool is_carmichael(ll n){ if(n < 2 || is_prime(n)) return false; map<ll, int> f; factorize(n, f); // Проверка squarefree for(auto [p, e] : f) if(e > 1) return false; // Проверка (p-1) | (n-1) for(auto [p, e] : f) if((n - 1) % (p - 1) != 0) return false; return true; } // ============================================================ // Нахождение k-го числа Кармайкла // ============================================================ ll kth_carmichael(int k){ ll n = 3; int cnt = 0; while(true){ if(is_carmichael(n)) { cnt++; if(cnt == k) return n; } n += 2; // все числа Кармайкла нечётны } } // ============================================================ // Оптимизированный is_prime с предварительной проверкой // малых делителей (даёт ускорение в 2-5 раз на практике) // ============================================================ bool is_prime_optimized(ll n){ if(n < 2) return false; if(n < 4) return true; if(n % 2 == 0 || n % 3 == 0) return false; // Пробные деления до min(sqrt(n), 1000) for(ll i = 5; i <= min((ll)1000, (ll)sqrtl((long double)n)); i += (i % 6 == 5) ? 2 : 4){ if(n % i == 0) return false; } if(n <= 1000000LL) { // Уже проверили все делители до 1000 >= sqrt(n) return true; } // Тест МР for(ll a : {2LL, 3LL, 5LL, 7LL, 11LL, 13LL, 17LL, 19LL, 23LL, 29LL, 31LL, 37LL}){ if(n == a) return true; if(n % a == 0) return false; if(!miller_rabin_test(n, a)) return false; } return true; } // ============================================================ // Сильный тест Лукаса (для BPSW) // ============================================================ // Символ Якоби (a/n) ll jacobi(ll a, ll n){ a %= n; ll res = 1; while(a != 0){ while(a % 2 == 0){ a /= 2; if(n % 8 == 3 || n % 8 == 5) res = -res; } swap(a, n); if(a % 4 == 3 && n % 4 == 3) res = -res; a %= n; } return (n == 1) ? res : 0; } // Выбор параметра D для теста Лукаса (метод Сельфриджа) ll find_D(ll n){ ll D = 5; while(true){ ll j = jacobi(D, n); if(j == 0 && abs(D) < n) return 0; // n составное (общий делитель) if(j == -1) return D; D = (D > 0) ? -(D + 2) : -(D - 2); } } // Последовательности Лукаса U_k(P, Q), V_k(P, Q) по модулю n // P, Q определяются из D: P = 1, Q = (1 - D) / 4 struct Lucas { ll U, V, Qk; }; // Вычисляет U_{k} и V_{k} по модулю n через удвоение Lucas lucas_sequence(ll P, ll Q, ll k, ll n){ // Используем алгоритм удвоения: // U_{2m} = U_m * V_m (mod n) // V_{2m} = V_m^2 - 2*Q^m (mod n) ll U = 1, V = P, Qk = Q; ll U2 = 0, V2 = 2; // U_0 = 0, V_0 = 2 // Бит за битом k: ll D = mulmod(P, P, n) - 4 * Q % n; D = ((D % n) + n) % n; ll Um = 0, Vm = 2, Qm = 1; ll Uk = 1, Vk = P, Qkk = Q % n; int bits = 63 - __builtin_clzll(k); for(int i = bits; i >= 0; i--){ // Удвоение: (Um, Vm, Qm) -> (U_{2m}, V_{2m}, Q^{2m}) ll new_U = mulmod(Um, Vm, n); ll new_V = (mulmod(Vm, Vm, n) - 2 * Qm % n + 2*n) % n; ll new_Q = mulmod(Qm, Qm, n); Um = new_U; Vm = new_V; Qm = new_Q; if((k >> i) & 1){ // Шаг: (Um, Vm) -> (U_{m+1}, V_{m+1}) ll nu = (mulmod(P, Um, n) + Vm) % n; if(nu % 2 != 0) nu += n; nu /= 2; ll nv = (mulmod(D % n, Um, n) + mulmod(P, Vm, n)) % n; if(nv % 2 != 0) nv += n; nv /= 2; Um = nu; Vm = nv; Qm = mulmod(Qm, Q, n); } } return {Um, Vm, Qm}; } // Сильный тест Лукаса bool strong_lucas_test(ll n){ if(n < 2) return false; if(n == 2) return true; if(n % 2 == 0) return false; ll D = find_D(n); if(D == 0) return false; // gcd(D, n) > 1 ll P = 1; ll Q = (1 - D) / 4; // n + 1 = 2^s * d ll m = n + 1; int s = 0; while(m % 2 == 0){ m /= 2; s++; } // Вычислить U_d, V_d, Q^d auto [Ud, Vd, Qd] = lucas_sequence(P, Q, m, n); if(Ud == 0 || Vd == 0) return true; ll Qk = Qd; for(int r = 1; r < s; r++){ // V_{2^r d} = V_{2^{r-1} d}^2 - 2 * Q^{2^{r-1} d} Vd = (mulmod(Vd, Vd, n) - 2 * Qk % n + 2*n) % n; Qk = mulmod(Qk, Qk, n); if(Vd == 0) return true; } return false; } // BPSW тест: практически безошибочный для n < 10^{30} bool is_prime_bpsw(ll n){ if(n < 2) return false; if(n == 2 || n == 3 || n == 5) return true; if(n % 2 == 0 || n % 3 == 0 || n % 5 == 0) return false; // Шаг 1: МР по основанию 2 if(!miller_rabin_test(n, 2)) return false; // Шаг 2: сильный тест Лукаса return strong_lucas_test(n); } int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); // Тесты cout << boolalpha; cout << "is_prime(561) = " << is_prime(561) << "\n"; // false (Кармайкла) cout << "is_prime(1729) = " << is_prime(1729) << "\n"; // false cout << "is_prime(998244353) = " << is_prime(998244353) << "\n"; // true cout << "is_prime(1000000007) = " << is_prime(1000000007) << "\n"; // true // Числа Кармайкла cout << "\nЧисла Кармайкла до 10000:\n"; for(ll n = 3; n <= 10000; n += 2) if(is_carmichael(n)) cout << n << " "; cout << "\n"; // Ожидается: 561 1105 1729 2465 2821 6601 8911 // Факторизация map<ll,int> f; factorize(561LL, f); cout << "\n561 = "; for(auto [p, e] : f) cout << p << "^" << e << " * "; cout << "\n"; // Проверка для n = 10^18 + 9 (простое) ll big_prime = 1000000000000000003LL; cout << "is_prime(" << big_prime << ") = " << is_prime(big_prime) << "\n"; return 0; }
Анализ сложности:
| Операция | Время | Примечание |
|---|---|---|
miller_rabin_test(n, a) | итераций, каждая mulmod | |
is_prime(n) (12 оснований) | Детерминированно для | |
pollard_rho(n) | В среднем | |
factorize(n) | Рекурсивно | |
is_carmichael(n) | Факторизация + проверка |
Для : is_prime работает за наносекунды (12 · 60 = 720 умножений).
Разбор задачи 1 (средняя): Подсчёт простых в массиве
Условие. Дан массив из чисел . Найти количество простых.
Решение. Применяем is_prime к каждому элементу.
#include <bits/stdc++.h> using namespace std; using ll = long long; // ... (код is_prime из выше) int main(){ int n; cin >> n; int cnt = 0; for(int i = 0; i < n; i++){ ll x; cin >> x; if(is_prime(x)) cnt++; } cout << cnt << "\n"; // Сложность: O(n * log^2(max_a)) = O(10^5 * 60^2) ~ 3.6 * 10^8 mulmod // Но mulmod быстрая (~3 нс), итого ~1 сек }
Оптимизация: сначала проверить делимость на малые простые (2, 3, 5, ..., 997). Это отсеивает составных числел без дорогостоящего МР.
Разбор задачи 2 (сложная): Факторизация и мультипликативная функция
Условие. Дано запросов. Для каждого найти .
Решение. Факторизуем через Полларда–Ро и вычисляем .
#include <bits/stdc++.h> using namespace std; using ll = long long; // ... (код is_prime, pollard_rho, factorize) ll euler_phi(ll n){ map<ll,int> f; factorize(n, f); ll result = n; for(auto [p, e] : f){ result -= result / p; } return result; } int main(){ int Q; cin >> Q; while(Q--){ ll n; cin >> n; cout << euler_phi(n) << "\n"; } }
Тест: . Проверка критерия Корселта: : — всё верно.
Сложность: в среднем. При , : — слишком медленно.
Оптимизация: для каждого разбиваем: если после пробных делений до остаток , то готово; если простое (тест МР) — готово; иначе один вызов Полларда–Ро. Реально для большинства пробные деления дают один–два делителя быстро.
Практически для и : сек.
Разбор задачи 3 (очень сложная — CF Div.1 E / ICPC уровень): Нахождение числа Кармайкла в диапазоне
Условие. Дано запросов. Для каждого найти наименьшее число Кармайкла для .
Анализ. Прямой перебор всех нечётных чисел от до первого числа Кармайкла — неэффективен (расстояние между числами Кармайкла может быть велико при ).
Метод: используем критерий Корселта для генерации чисел Кармайкла.
Числа Кармайкла вида с тремя простыми делителями строятся следующим образом. Зафиксируем , тогда для некоторого , и .
Конструктивный алгоритм Эрдёша для трёхпростых Кармайкла:
// Генерация всех чисел Кармайкла вида p*q*r до N // за O(N^{2/3} / log^2 N) при наивном подходе vector<ll> generate_carmichael_3primes(ll N) { vector<ll> result; // Перебираем p < q, затем находим r // Из (p-1)|(n-1), (q-1)|(n-1), (r-1)|(n-1) // n = pqr, n-1 = k*lcm(p-1,q-1,r-1) // Упрощение: p*(p-1) | (n-1), фиксируем p, q // l = lcm(p-1, q-1), n-1 = l*k для некоторого k // n = pqr = l*k + 1 // r = (l*k+1)/(p*q) // Простые до cbrt(N) int limit = (int)cbrtl((long double)N) + 1; vector<int> primes; vector<bool> isp(limit+1, true); for(int i=2; i<=limit; i++){ if(isp[i]) primes.push_back(i); for(int j=0; j<(int)primes.size()&&(ll)i*primes[j]<=limit; j++){ isp[i*primes[j]]=false; if(i%primes[j]==0) break; } } for(int pi = 0; pi < (int)primes.size(); pi++){ ll p = primes[pi]; for(int qi = pi+1; qi < (int)primes.size(); qi++){ ll q = primes[qi]; ll L = lcm(p-1, q-1); // Ищем k: r = (k*L+1)/(p*q) должно быть целым и простым // k*L + 1 ≡ 0 (mod p*q) // k ≡ -1/L (mod p*q/gcd(L, p*q)) ll pq = p * q; // Хотим k*L ≡ -1 (mod pq), т.е. k ≡ -L^{-1} mod pq // Проверяем: gcd(L, pq) должен делить 1 => gcd(L, pq) = 1 ll g = __gcd(L, pq); if(g != 1) continue; // нет решений // k0 = обратное к L по модулю pq, умноженное на -1 // Используем расширенный Евклид // ... (реализация inv_mod) // k ≡ k0 (mod pq), k = k0 + pq*t, t >= 0 // r = (k*L+1)/(pq), k должно быть >= 1 // Для краткости: наивный перебор k ll k = 1; while(true){ ll num = k * L + 1; if(num > N) break; if(num % pq == 0){ ll r = num / pq; if(r > q && is_prime(r)){ // Проверяем (r-1) | (n-1) = (k*L) if(k * L % (r-1) == 0){ result.push_back(num); } } } k++; } } } sort(result.begin(), result.end()); result.erase(unique(result.begin(), result.end()), result.end()); return result; }
Более эффективный подход для соревнований: предгенерировать все числа Кармайкла до (их ) и отвечать на запросы бинарным поиском. Генерация через алгоритм Эрдёша занимает при грамотной реализации.
// Для соревнования: предподсчёт числе Кармайкла vector<ll> carmichael_list; void precompute(ll limit){ // Используем generate_carmichael_3primes (основная часть) // + числа с 4+ простыми делителями (их значительно меньше) carmichael_list = generate_carmichael_3primes(limit); sort(carmichael_list.begin(), carmichael_list.end()); } ll next_carmichael(ll L){ auto it = lower_bound(carmichael_list.begin(), carmichael_list.end(), L); if(it == carmichael_list.end()) return -1; // нет в диапазоне return *it; }
Тест: первые числа Кармайкла: 561, 1105, 1729, 2465, 2821, 6601, 8911, 10585, 15841, 29341.
Задачи для самостоятельного решения (таблица)
| # | Задача | Источник | Метод | Сложность |
|---|---|---|---|---|
| 1 | Определить простое ли | Classic | МР | Лёгкая |
| 2 | Факторизация | Classic | Полларда–Ро | Средняя |
| 3 | для | — | Факторизация | Средняя |
| 4 | Наименьший простой делитель | — | Полларда–Ро | Средняя |
| 5 | Подсчёт простых чисел Мерсенна до | — | МР + специфика | Сложная |
| 6 | Числа Кармайкла в , | — | Критерий Корселта | Очень сложная |
| 7 | Первые чисел Кармайкла | — | Генерация | Очень сложная |
Типичные ошибки
1. Применение теста Ферма вместо Миллера–Рабина.
Числа Кармайкла проходят тест Ферма по всем основаниям. Если вы пишете if(pow(a, n-1, n) == 1) return true — ваш код неверен для чисел Кармайкла.
2. Переполнение в mulmod.
При произведение может переполнить long long. Всегда используйте (__int128)a * b % m или аналог.
3. Неправильный выбор оснований. Набор не достаточен для . Конкретный контрпример: проходит МР по основаниям 2 и 3, но составное. Всегда используйте проверенный набор оснований из таблицы.
4. Граничный случай .
Некоторые реализации МР возвращают false для , т.к. — нечётное, и . Добавьте явную проверку маленьких простых.
5. Алгоритм Полларда зацикливается. При неудачном выборе (например, или ) алгоритм может зациклиться. Добавьте проверку или используйте несколько итераций с разными .
6. Неверная проверка чисел Кармайкла для чисел с . Критерий Корселта требует squarefree. Если или , то , и условие может выполняться для некоторых , но не Кармайкла. Всегда проверяйте squarefree первым.
Совет профессионала
В реальных соревнованиях для проверки простоты одиночного числа используйте детерминированный тест МР с основаниями — он гарантированно верен и работает за наносекунды. Не используйте случайные основания: в соревновательных условиях жюри могут специально подобрать "тяжёлые" входные данные.
Для факторизации комбинируйте: (1) пробные деления до (отсекает большинство случаев за итераций); (2) тест простоты МР для остатка; (3) Полларда–Ро для составного остатка. Такая комбинация работает мс для любого .
Итог
Числа Кармайкла — составные числа, "обманывающие" тест Ферма. Их характеризует критерий Корселта: squarefree и для каждого простого делителя .
Тест Миллера–Рабина — усиление теста Ферма через проверку квадратных корней из 1. Доля "обманных" оснований для составного не превосходит .
Детерминированный тест с основаниями корректен для всех — более чем достаточно для любых задач в long long и __int128.
Практические рекомендации:
- Одиночная проверка простоты : детерминированный МР, .
- Факторизация : Полларда–Ро + МР, .
- Никогда не используйте тест Ферма без МР — числа Кармайкла его обходят.
- BPSW = МР(2) + Лукас — практически надёжен, используется в Python's
isprime.