Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MISZKI_Lektsii.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
2.88 Mб
Скачать

Простые числа

Простым называется целое число, большее единицы, единственными множителями которого является 1 и оно само: оно не делится ни на одно другое число. Два - это простое число. Простыми являются и 73, 2521, 2365347734339 и 275б839-1. Существует бесконечно много простых чисел. Криптография, особенно криптография с открытыми ключами, часто использует большие простые числа (512 бит и даже больше).

Евангелос Кранакис (Evangelos Rranakis) написал отличную книгу по теории чисел, простым числам и их применению в криптографии [896]. Паула Рибенбойм (Paula Ribenboim) написала две отличных справочных работы по простым числам вообще [1307, 1308].

Наибольший общий делитель

Два числа называются взаимно простыми, если у них нет общих множителей кроме 1. Иными словами, если наибольший общий делитель а и n равен 1. Это записывается как:

НОД(а,n)=1

Взаимно просты числа 15 и 28. 15 и 27 не являются взаимно простыми, а 13 и 500 - являются. Простое число взаимно просто со всеми другими числами, кроме ч исел, кратных данному простому числу.

Одним из способов вычислить наибольший общий делитель двух чисел является алгоритм Эвклида. Эвклид описал этот алгоритм в своей книге, Элементы, написанной в 300 году до нашей эры. Он не изобрел его. Историки считают, что этот алгоритм лет на 200 старше. Это самый древний нетривиальный алгоритм, который дошел до наших дней, и он все еще хорош. Кнут описал алгоритм и его современные модификации в [863]. На языке С:

/* возвращает НОД (gcd) x и у */

int gcd (int x, int у)

{

int g;

if (x < 0) x = - x;

if (У < 0) у = -у;

if (x + у == 0 ) ERROR ;

g = у;

while (x > 0) {

g = x;

x = у % x;

у = g; }

return g;

}

Этот алгоритм можно обобщить для получения НОД массива m чисел:

/* возвращает НОД (gcd) xl, x2...xm */

int multiple_gcd (int m, int *x)

{

slze_t i; int g;

if (m < 1) return 0;

g = x[0];

for (i=l; i<m; ++i)

{

g = gcd(g, x[i]) ;

/* оптимизация, так как для случайных x[i], g==l в 60% случаев: */

if (g == 1) return 1;

}

return g;

}

Обратные значения по модулю

Помните, что такое обратные значения? Обратное значение для 4 - 1/4, потому что 4*1/4 =1. В мире вычетов проблема усложняется:

4*х = 1 (mod 7)

Это уравнение эквивалентно обнаружению х и к, таких что

4x = 7k + 1

где х и к - целые числа. Общая задача состоит в нахождении х, такого что

1 = (а*х) mod n

Это также можно записать как

а-1 = х (mod n)

Проблему обратных значений по модулю решить нелегко. Иногда у нее есть решение, иногда нет. Например, обратное значение 5 по модулю 14 равно 3. С другой стороны у числа 2 нет обратного значения по модулю 14.

В общем случае у уравнения а-1 = х (mod n) существует единственное решение, если а и n взаимно просты. Если а и п не являются взаимно простыми, то а-1 (mod n) не имеет решений. Если n является простым числом, то любое число от 1 до n -1 взаимно просто с n и имеет в точности одно обратное значение по модулю п.

Так, хорошо. А теперь как вы собираетесь искать обратное значение а по модулю n? Существует два пути. Обратное значение а по модулю п можно вычислить с помощью алгоритма Эвклида. Иногда это называется расширенным алгоритмом Эвклида.

Вот этот алгоритм на языке C++:

#define isEven(x) ((x & 0x01) == 0)

#define isOdd(x) (x& 0x01)

#define swap(x,у) (хл= у, у"= х, хл= у)

void ExtBinEuclid(int *u, int *v, int *ul, int *u2, int *u3) {

// предупреждение: и и v будут переставлены, если u<v int k, tl, t2, t3;

if (*u < *v) swap(*u<,*v);

for (k = 0; isEven(*u) && isEven(*v); ++k) {

*u»=l; *v »1;

}

*ul = 1; *u2 = 0; *u3 = *u; tl = *v; t2 = *u - 1; t3 = *v;

do {

do {

if (isEven(*u3)) {

if (isOdd(*ul) || isOdd(*u2)) {

*ul += *v; *u2 += *u;

} *ul »* 1; *u2 »= 1; *u3 »= 1;

}

if (isEven(t3) || *u3 < t3) { swap(*ul,tl); smap(*u2,t2); smap(*u3,t3);

}

} while (isEven(*u3)); while (*ul < tl I I *u2 < t2) { *ul += *v; *u2 += *u; }

ul -= tl; *u2 -= t2; *u3 -= t3; } while (t3 > 0) ;

while (*ul >= *v && *u2 >= *u) { *ul>l -= *v; *u2 -= *u;

}

*u «= k; *v «= k; *u3 << k; }

main(int argc, char **argv) { int a, b, gcd; if (argc < 3) {

cerr << "как использовать: xeuclid u v" << endl; return -1;

}

int u = atoi(argv[1]); int v = atoi(argv[2]); if (u <= 0 I I v <= 0 ) {

cerr << "Аргумент должен быть положителен!" << endl; return -2;

}

// предупреждение: u и v будут переставлены если u < v ExtBinEuclid(Su, &v, &a, &b, Sgcd) ; cout « a «" * " « и « " + (-" « b « ") * " « v « " = " « gcd « endl; if (gcd == 1)

cout << "Обратное значение " << v << " mod " << u << " is: « u - b « endl; return 0;

Я не собираюсь доказывать, что это работает, или приводить теоретическое обоснование. Подробности можно найти в [863] или в любой из приведенных ранее работ по теории чисел.

Алгоритм итеративен и для больших чисел может работать медленно. Кнут показал, что среднее число выполняемых алгоритмом делений равно: 0.843*log2(n)+1.47

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]