Оскільки число є простим, за малою теоремою Ферма виконується рівність
Цю рівність можна переписати у вигляді:
звідки випливає, що оберненим до числа є число j = i^{n-2}~mod~n.
Обернене число також можна знайти за допомогою розширеного алгоритму Евкліда. Розглянемо задачу пошуку числа , що задовольняє порівняння:
Це порівняння еквівалентне діофантовому рівнянню:
За допомогою розширеного алгоритму Евкліда знайдемо один з його розв'язків (x_0, y_0). Тоді:
Розглянемо цю рівність за модулем . Отримаємо:
Отже, число x_0 є оберненим до за модулем . Якщо значення x_0 від'ємне, необхідно додати до нього , щоб отримати число з діапазону [1, n - 1].
Таким чином, в обох алгоритмах для кожного числа i~(1 \le i < n) пошук оберненого числа займає час O(log_2n). Отже, загальна асимптотична складність алгоритму становить O(nlog_2n).
Розглянемо розв'язок з асимптотичною складністю .
Теорема. Якщо — число, обернене до за модулем , то
Доведення. Відомо, що n~mod~i = n - \left\lfloor \frac{n}{i} \right\rfloor \cdot i
Розглянемо цю рівність за модулем : n~mod~i = - \left\lfloor \frac{n}{i} \right\rfloor \cdot i~mod~n
Помножимо обидві частини рівності на i^{-1} \cdot~(n~mod~i)^{-1}:
Після спрощення отримаємо:
Згідно з формулою, наведеною в теоремі, значення для кожного i~(1 \le i < n) може бути обчислене за час . Отже, всі обернені елементи можна знайти за лінійний час .
Приклад
Нехай n = 5. Побудуємо таблицю обчислення обернених елементів:
Знайдемо обернені елементи, використовуючи формулу з теореми:
inv[1] = 1 (базовий випадок)
inv[2] = = (-2 \cdot inv[1])~mod~5 = -2~mod~5 = 3
inv[3] = = (-1 \cdot inv[2])~mod~5 = -3~mod~5 = 2
inv[4] = = (-1 \cdot inv[1])~mod~5 = -1~mod~5 = 4
Реалізація алгоритму
Функція Pow повертає значення x^n~mod~m.
long long Pow(long long x, long long n, long long m)
{
if (n == 0) return 1;
if (n % 2 == 0) return Pow((x * x) % m, n / 2, m);
return (x * Pow(x, n - 1, m)) % m;
}Основна частина програми. Читаємо вхідне значення .
scanf("%d",&n);Для кожного значення i~(1 \le i < n) виводимо обернений елемент за модулем , використовуючи формулу i^{n-2}~mod~n.
for(i = 1; i < n; i++)
printf("%lld ",Pow(i,n-2,n));
printf("\n");Реалізація алгоритму – розширений алгоритм Евкліда
#include <stdio.h>
int n, i, d, x, y;
void gcdext(int a, int b, int &d, int &x, int &y)
{
if (b == 0)
{
d = a; x = 1; y = 0;
return;
}
gcdext(b, a % b, d, x, y);
int s = y;
y = x - (a / b) * y;
x = s;
}
int main(void)
{
scanf("%d", &n);
for (i = 1; i < n; i++)
{
// i * x + n * y = 1
gcdext(i, n, d, x, y);
if (x < 0) x += n;
printf("%d ", x);
}
printf("\n");
return 0;
}Реалізація алгоритму – лінійний розв'язок
Оголосимо масив , в якому будемо зберігати обернені елементи:
#define MAX 1000001
int inv[MAX];Читаємо вхідне значення .
scanf("%d", &n);Обчислимо обернені елементи відповідно до формули з теореми.
inv[1] = 1;
for (i = 2; i <= n; i++)
inv[i] = n - 1LL * (n / i) * inv[n % i] % n;Виводимо відповідь.
for (i = 1; i < n; i++)
printf("%d ", inv[i]);Java реалізація
import java.util.*;
public class Main
{
static long PowMod(long x, long n, long m)
{
if (n == 0) return 1;
if (n % 2 == 0) return PowMod((x * x) % m, n / 2, m);
return (x * PowMod(x, n - 1, m)) % m;
}
public static void main(String[] args)
{
Scanner con = new Scanner(System.in);
long n = con.nextLong();
for(int i = 1; i < n; i++)
System.out.print(PowMod(i,n-2,n) + " ");
System.out.println();
con.close();
}
}Java реалізація – лінійний розв'язок
import java.util.*;
public class Main
{
public static void main(String[] args)
{
Scanner con = new Scanner(System.in);
int n = con.nextInt();
int inv[] = new int[n];
inv[1] = 1;
for (int i = 2; i < n; i++)
inv[i] = (int) (n - 1L * (n / i) * inv[n % i] % n);
for(int i = 1; i < n; i++)
System.out.print(inv[i] + " ");
System.out.println();
con.close();
}
}Python реалізація – лінійний розв'язок
Читаємо вхідне значення .
n = int(input())Оголосимо список , в якому будемо зберігати обернені елементи:
inv = [0 for _ in range(n + 1)]Обчислимо обернені елементи відповідно до формули з теореми.
inv[1] = 1;
for i in range(2,n):
inv[i] = n - (n // i) * inv[n % i] % n;Виводимо відповідь.
for i in range(1,n):
print(inv[i], end= " ")