Campo modulare#12317

Campo modulare (Editoriale)

Il numero \sqrt{c} è irrazionale, quindi calcolare l'espressione in numeri reali non ha senso: per fino a 10^{18} ciò è impossibile per questioni di precisione. Notiamo, tuttavia, che qualsiasi potenza intera del numero a + b\sqrt{c} ha ancora la forma x + y\sqrt{c}, poiché \sqrt{c} \cdot \sqrt{c} = c. Pertanto, i calcoli vanno eseguiti nel campo esteso:

Z_p (\sqrt{c}) = \{ x + y \sqrt{c}~|~x,y \in Z_p\}

Regole delle operazioni:

(x_1 + y_1 \sqrt{c}) + (x_2 + y_2 \sqrt{c}) = (x_1 + x_2 ) + (y_1 + y_2) \sqrt{c}
(x_1 + y_1 \sqrt{c}) \cdot (x_2 + y_2 \sqrt{c}) = (x_1 x_2 + cy_1 y_2) + (x_1 y_2 + x_2 y_1 ) \sqrt{c}

Dopo aver definito l'operazione di moltiplicazione, gli elementi del campo Z_p (\sqrt{c}) possono essere elevati a potenza con il metodo standard dell'elevamento a potenza veloce in tempo O(log_2n). Questo permette di calcolare (a + b \sqrt{c})^n~mod~p.

Esempio

Nel primo esempio

(2 + \sqrt{5})^2~mod~17 = (4 + 4 \sqrt{5} + 5)~mod~17 = 9 + 4 \sqrt{5}

Consideriamo il secondo esempio:

(3 + 2 \sqrt{5})^{10}~mod~17

Sia x = 3 + 2 \sqrt{5}. Calcoliamo le sue potenze:

x^2 = (3 + 2 \sqrt{5})^2~mod~17 = (9 + 12 \sqrt{5} + 20)~mod~17 = 12 + 12 \sqrt{5} \\ x^4 = (12 + 12 \sqrt{5})^2~mod~17 = (144 + 288 \sqrt{5} + 720)~mod~17 = 14 + 16 \sqrt{5} \\ x^8 = (14 + 16 \sqrt{5})^2~mod~17 = (196 + 448 \sqrt{5} + 1280)~mod~17 = 14 + 6 \sqrt{5}

Ora possiamo calcolare il risultato:

x^{10} = x^8 \cdot x^2 = (14 + 6 \sqrt{5})(12 + 12 \sqrt{5})~mod~17 = \\ (168 + 72 \sqrt{5} + 168 \sqrt{5} + 360)~mod~17 = 1 + 2 \sqrt{5}

Implementazione dell'algoritmo

Dichiariamo la struttura per memorizzare un numero della forma x + y \sqrt{c}.

struct Num
{
  long long x, y; // x + y*sqrt(c)
};

Implementiamo la moltiplicazione nel campo Z_p (\sqrt{c}). La funzione mult restituisce il prodotto dei numeri e .

Num mult(Num
{
  Num res;
  res.x = (a.x * b.x + c * a.y % p * b.y) % p;
  res.y = (a.x * b.y + a.y * b.x) % p;
  return res;
}

La funzione pow implementa l'elevamento a potenza base^n, dove — è un numero nel campo Z_p (\sqrt{c}).

Num pow(Num base, long long n)
{
  Num res = { 1, 0 }; // unità del campo
  while (n > 0)
  {
    if (n & 1)
      res = mult(res, base);
    base = mult(base, base);
    n >>= 1;
  }
  return res;
}

Parte principale del programma. Leggiamo i dati in ingresso.

scanf("%lld %lld %lld %lld %lld", &a, &b, &c, &n, &p);

Inizializziamo il numero iniziale start = a + b \sqrt{c}.

Num start = { a % p, b % p };

Calcoliamo e stampiamo la risposta ans = start^n~mod~p = (a + b \sqrt{c})^n~mod~p.

Num ans = pow(start, n);
printf("%lld %lld\n", ans.x, ans.y);