Кодирование по Фано a b c d e f g h i j k 20 18 17 11 10 7 5 4 4 3 1 0 a b c 1 d e f g h i j k 55 20 18 17 45 11 10 7 5 4 4 3 1 00 a 01 b c 10 d e 11 f g h i j k 20 35 18 17 21 11 10 24 7 5 4 4 3 1 a b c d e f g h i j k 00 010 011 100 101 1100 1101 11100 11101 11110 11111 Программа на языке Java public class Code { public long code; public byte len; // Собственно код символа // Длина кода // Символы (в порядке убывания вероятностей появления) public static char[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}; // Вероятности появления символов public static int[] probs = { 20, 18, 17, 11, 10, 7, 5, 4, 4, 3, 1 }; // Формируемый массив кодов public static Code[] codes = new Code[chars.length]; } public class Fano { private static int mediana(int low, int high) { int sLow = 0; // Сумма первых элементов int sHigh = Code.probs[high]; // Сумма последних элементов for (int i = low; i < high; i++) { sLow += Code.probs[i]; } int m = high; // Перед поиском границы sLow = sum[low:(high-1)], sHigh = sum[high] int diff; do { m--; diff = Math.abs(sLow - sHigh); // Вычисляем предыдущую разность // Изменяем верхнюю и нижнюю суммы sLow -= Code.probs[m]; sHigh += Code.probs[m]; } while (Math.abs(sLow - sHigh) < diff); // Цикл заканчивается, когда разности перестанут уменьшаться return m; } public static void fano(int low, int high) { if (low < high) { // Определяем границу равных сумм вероятностей int m = mediana(low, high); for (int i = low; i <= high; i++) { // Сдвигаем коды и дописываем нули в первую половину и единицы во вторую Code.codes[i].code <<= 1; if (i > m) Code.codes[i].code++; Code.codes[i].len++; } // Рекурсивный вызов алгоритма для двух отдельных половин таблицы fano(low, m); fano(m+1, high); } } } Кодирование по Хаффмену a b c d e f g h i j k bcdfijk aegh 20 18 17 11 10 7 5 4 4 3 1 a b c d e f g h i jk aegh bc dfijk 20 18 17 11 10 7 5 4 4 4 39 35 26 a b c d e ijk f g h bc dfijk a egh 20 18 17 11 10 8 7 5 4 35 26 20 19 a b c d e gh ijk f dfijk a egh b c 20 18 17 11 10 9 8 7 26 20 19 18 17 a b c fijk d e gh a egh b c fijk d 20 18 17 15 11 10 9 20 19 18 17 15 11 61 39 a aegh 10 1 e egh 11 gh bc bcdfijk 110 g 1110 h 1111 111 b 000 c 001 00 0 i ijk fijk dfijk 01 d 0100 jk 010 f 011 01000 0101 j 010000 k 010001 01000 public class Huffman { // Рабочая копия массива вероятностей static int[] prs = new int[Code.probs.length]; private static int up(int n, int p) { int j = n-1; // Поиск начинаем с конца таблицы while (j >= 1) { if (prs[j-1] < p) { // Сдвиг по таблице prs[j] = prs[j-1]; j--; } else { // Место в таблице найдено break; } } // Вставка значения в таблицу и возврат prs[j] = p; return j; } public static void huffman(int n) { if (n == 2) { // Предельный случай - два символа в таблице Code.codes[0] = new Code((long)0, (byte)1); Code.codes[1] = new Code((long)1, (byte)1); } else { // Просуммируем две наименьшие вероятности и вставим в табицу на свое место int j = up(n, prs[n-2] + prs[n-1]); // Рекурсивный вызов алгоритма для уменьшенной таблицы huffman(n-1); // Приписывание кодов согласно сохраненному значению j down(n, j); } } private static void down(int n, int j) { Code c = new Code(Code.codes[j]); // Запомнили код for (int i = j; i < n-2; i++) { // Сдвигаем коды вниз по таблице Code.codes[i].assign(Code.codes[i+1]); } // Восстанавливаем два последних кода c.len++; c.code <<= 1; Code.codes[n-2] = new Code(c); c.code++; Code.codes[n-1] = new Code(c); } } Некоторые сведения из модульной арифметики a b (mod n) a mod n = b mod n Операции «по модулю n»: a +n b (a + b) mod n a *n b (a * b) mod n Малая теорема Ферма: 0 < a < p ap-1 1 (mod p) Следствие из «китайской теоремы об остатках»: n = p*q и x y (mod n) x y (mod p) и x y (mod q) Шифрование с открытым ключом Выбираем два простых числа p и q; Вычисляем n = p*q; Выбираем нечетное e, взаимно простое с (p-1)(q-1); Вычисляем d = e–1 mod (p-1)(q-1); Открытый ключ – пара <n, e>; Закрытый ключ – пара <n, d>. Исходное сообщение: S = S0S1…Sk, Шифрованное сообщение: C = C0C1…Ck. Шифрование: Ci = (Si)e mod n; Расшифровка: Pi = (Ci)d mod n. Утверждение: Pi = Si. Доказательство корректности Pi = (Si)ed mod n. Покажем, что при M < n Med M mod n Это очевидно верно при M = 0 При M > 0 по малой теореме Ферма: k(q-1) Med = M (Mp-1) , поскольку ed = 1 + k(p-1)(q-1); Med M 1k(q-1) M (mod p) Аналогично, Med M (mod q), следовательно, Med M (mod n) Пример кода Выберем p = 3, q = 11. Тогда n = 33, (p-1)(q-1) = 20, Выберем e = 7, тогда d = 3 (ed = 21; 21 1 mod 20) Фрагменты сообщения могут быть не длиннее 5 бит. Пусть S0 = 21, S1 = 13, S2 = 17 Тогда C0 = 217 mod 33 = 21, C1 = 137 mod 33 = 7, C2 = 177 mod 33 = 8, Проверка (расшифровка): P0 = 213 mod 33 = 21 = S0, P1 = 73 mod 33 = 13 = S1, P2 = 83 mod 33 = 17 = S2,