mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-11-10 11:09:43 +08:00
Merge 13d2020d69
into 9046d80bdb
This commit is contained in:
commit
bfac369aae
96
src/algorithms/cryptography/hill-cipher/README.ru-RU.md
Normal file
96
src/algorithms/cryptography/hill-cipher/README.ru-RU.md
Normal file
@ -0,0 +1,96 @@
|
||||
# Шифр Хилла
|
||||
|
||||
**Шифр Хилла** - это [полиграфическая замена](https://en.wikipedia.org/wiki/Polygraphic_substitution) шифр, основанный на линейной алгебре.
|
||||
|
||||
Каждая буква представлена числом [по модулю](https://en.wikipedia.org/wiki/Modular_arithmetic) `26`. Хотя это не является существенной особенностью шифра, эта простая схема часто используется:
|
||||
|
||||
| **Буква** | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
|
||||
| ------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| **Число** | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
|
||||
|
||||
## Шифрование
|
||||
|
||||
Чтобы зашифровать сообщение, каждый блок из `n` букв (рассматриваемый как вектор, состоящий из `n` компонентов) умножается на обратимую матрицу `n × n` по модулю `26`.
|
||||
|
||||
Матрица, используемая для шифрования, называется _ключом шифра_, и она должна быть выбрана случайным образом из набора обратимых матриц `n × n` (по модулю `26`). Шифр, конечно, может быть адаптирован к алфавиту с любым количеством букв; все арифметические действия просто должны выполняться по модулю количества букв, а не по модулю `26`.
|
||||
|
||||
Рассмотрим сообщение `ACT` и приведенный ниже ключ (или `GYB/NQK/URP` буквами):
|
||||
|
||||
```
|
||||
| 6 24 1 |
|
||||
| 13 16 10 |
|
||||
| 20 17 15 |
|
||||
```
|
||||
|
||||
Поскольку `A` равно `0`, `C` равно `2`, а `T` равно `19`, сообщение является вектором:
|
||||
|
||||
```
|
||||
| 0 |
|
||||
| 2 |
|
||||
| 19 |
|
||||
```
|
||||
|
||||
Таким образом, зашифрованный вектор задается формулой:
|
||||
|
||||
```
|
||||
| 6 24 1 | | 0 | | 67 | | 15 |
|
||||
| 13 16 10 | | 2 | = | 222 | ≡ | 14 | (mod 26)
|
||||
| 20 17 15 | | 19 | | 319 | | 7 |
|
||||
```
|
||||
|
||||
что соответствует зашифрованному тексту `POH`.
|
||||
|
||||
Теперь предположим, что наше сообщение вместо этого `CAT` (обратите внимание, что здесь мы используем те же буквы, что и в `ACT`), или:
|
||||
|
||||
```
|
||||
| 2 |
|
||||
| 0 |
|
||||
| 19 |
|
||||
```
|
||||
|
||||
На этот раз зашифрованный вектор задается формулой:
|
||||
|
||||
```
|
||||
| 6 24 1 | | 2 | | 31 | | 5 |
|
||||
| 13 16 10 | | 0 | = | 216 | ≡ | 8 | (mod 26)
|
||||
| 20 17 15 | | 19 | | 325 | | 13 |
|
||||
```
|
||||
|
||||
который соответствует зашифрованному тексту `FIN`. Каждая буква была изменена.
|
||||
|
||||
## Расшифровка
|
||||
|
||||
Для расшифровки сообщения каждый блок умножается на величину, обратную матрице, используемой для шифрования. Мы преобразуем зашифрованный текст обратно в вектор, а затем просто умножаем на обратную матрицу ключевой матрицы (`IFK/VIV/VMI` буквами). (Смотрите [обращение матрицы](https://en.wikipedia.org/wiki/Matrix_inversion) о методах вычисления обратной матрицы.) Мы находим, что по модулю 26 обратная матрица, используемая в предыдущем примере, равна:
|
||||
|
||||
```
|
||||
-1
|
||||
| 6 24 1 | | 8 5 10 |
|
||||
| 13 16 10 | (mod 26) ≡ | 21 8 21 |
|
||||
| 20 17 15 | | 21 12 8 |
|
||||
```
|
||||
|
||||
Взяв предыдущий пример зашифрованного текста `POH`, мы получаем:
|
||||
|
||||
```
|
||||
| 8 5 10 | | 15 | | 260 | | 0 |
|
||||
| 21 8 21 | | 14 | = | 574 | ≡ | 2 | (mod 26)
|
||||
| 21 12 8 | | 7 | | 539 | | 19 |
|
||||
```
|
||||
|
||||
что возвращает нас к `ACT`, как и ожидалось.
|
||||
|
||||
## Определение матрицы шифрования
|
||||
|
||||
При выборе матрицы шифрования возникают две сложности:
|
||||
|
||||
1. Не все матрицы имеют обратное значение. Матрица будет иметь обратное значение тогда и только тогда, когда ее [определитель](https://en.wikipedia.org/wiki/Determinant) не равен нулю.
|
||||
2. Определитель шифрующей матрицы не должен иметь каких-либо общих множителей с модульной базой.
|
||||
|
||||
Таким образом, если мы работаем по модулю `26`, как указано выше, определитель должен быть отличным от нуля и не должен делиться на `2` или `13`. Если определитель равен `0` или имеет общие множители с модульной базой, то матрица не может быть использована в шифре Хилла, и необходимо выбрать другую матрицу (в противном случае ее невозможно будет расшифровать). К счастью, матрицы, которые удовлетворяют условиям, используемым в шифре Хилла, довольно распространены.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Шифр Хилла в Википедии](https://en.wikipedia.org/wiki/Hill_cipher)
|
||||
- [Инверсия матрицы на MathIsFun](https://www.mathsisfun.com/algebra/matrix-inverse.html)
|
||||
- [GeeksForGeeks](https://www.geeksforgeeks.org/hill-cipher/)
|
||||
|
97
src/algorithms/cryptography/polynomial-hash/README.ru-RU.md
Normal file
97
src/algorithms/cryptography/polynomial-hash/README.ru-RU.md
Normal file
@ -0,0 +1,97 @@
|
||||
# Полиномиальный скользящий хэш
|
||||
|
||||
## Хэш-функция
|
||||
|
||||
**Хэш-функции** используются для сопоставления больших наборов данных, состоящих из элементов произвольной длины (*ключей*), с меньшими наборами данных, состоящими из элементов фиксированной длины (*хэш-суммой*).
|
||||
|
||||
Основным применением хеширования является эффективная проверка равенства ключей путем
|
||||
сравнения их хеш-суммой.
|
||||
|
||||
*Коллизия* возникает, когда два разных ключа имеют одинаковый отпечаток. Способ обработки коллизий имеет решающее значение в большинстве приложений хэширования.
|
||||
Хеширование особенно полезно при построении эффективных практических алгоритмов.
|
||||
|
||||
## Скользящий хеш
|
||||
|
||||
**Скользящий хэш** (также известный как рекурсивное хэширование или скользящая контрольная сумма) - это хэш
|
||||
-функция, при которой входные данные хэшируются в окне, которое перемещается по входным данным.
|
||||
|
||||
Несколько хеш—функций позволяют очень быстро вычислять переходящий хеш-код - новое значение хеш-кода быстро вычисляется, учитывая только следующие данные:
|
||||
|
||||
- старое значение хеш-кода,
|
||||
- старое значение, удаленное из окна,
|
||||
- и новое значение, добавленное в окно.
|
||||
|
||||
## Полиномиальное хеширование строк
|
||||
|
||||
Идеальная хэш-функция для строк, очевидно, должна зависеть как от "мультимножества" символов, присутствующих в ключе, так и от "порядка" символов. Наиболее распространенное семейство таких хэш-функций обрабатывает символы строки как коэффициенты *многочлена* с целочисленной переменной `p` и вычисляет ее значение по модулю целой константы `M`:
|
||||
|
||||
*Алгоритм поиска строк Рабина–Карпа* часто объясняется с помощью очень простой скользящей хэш-функции, которая использует только умножение и сложение - **полиномиальный скользящий хэш**:
|
||||
|
||||
> H(s<sub>0</sub>, s<sub>1</sub>, ..., s<sub>k</sub>) = s<sub>0</sub> * p<sup>k-1</sup> + s<sub>1</sub> * p<sup>k-2</sup> + ... + s<sub>k</sub> * p<sup>0</sup>
|
||||
|
||||
где `p` - константа, а *(s<sub>1</sub>, ... , s<sub>k</sub>)* - входные
|
||||
символы.
|
||||
|
||||
Например, мы можем преобразовать короткие строки в ключевые числа, умножив цифровые коды на степени константы. Трехбуквенное слово `ace` можно преобразовать в число, вычислив:
|
||||
|
||||
> ключ = 1 * 26<sup>2</sup> + 3 * 26<sup>1</sup> + 5 * 26<sup>0</sup>
|
||||
|
||||
Чтобы избежать манипулирования огромными значениями `H`, вся математика выполняется по модулю `M`.
|
||||
|
||||
> H(s<sub>0</sub>, s<sub>1</sub>, ..., s<sub>k</sub>) = (s<sub>0</sub> * p<sup>k-1</sup> + s<sub>1</sub> * p<sup>k-2</sup> + ... + s<sub>k</sub> * p<sup>0</sup>) mod M
|
||||
|
||||
Тщательный выбор параметров `M`, `p` важен для получения “хороших” свойств хэш-функции, т.е. низкой частоты коллизий.
|
||||
|
||||
Желательным свойством этого подхода является использование всех символов во входной строке. Вычисленное значение ключа затем может быть хэшировано в индекс массива
|
||||
обычным способом:
|
||||
|
||||
```javascript
|
||||
function hash(key, arraySize) {
|
||||
const base = 13;
|
||||
|
||||
let hash = 0;
|
||||
for (let charIndex = 0; charIndex < key.length; charIndex += 1) {
|
||||
const charCode = key.charCodeAt(charIndex);
|
||||
hash += charCode * (base ** (key.length - charIndex - 1));
|
||||
}
|
||||
|
||||
return hash % arraySize;
|
||||
}
|
||||
```
|
||||
|
||||
Метод `hash()` не так эффективен, как мог бы быть. Помимо преобразования символов, в цикле есть два умножения и сложение. Мы можем исключить одно умножение, используя *метод Хорнера*:
|
||||
|
||||
> a<sub>4</sub> * x<sup>4</sup> + a<sub>3</sub> * x<sup>3</sup> + a<sub>2</sub> * x<sup>2</sup> + a<sub>1</sub> * x<sup>1</sup> + a<sub>0</sub> = (((a<sub>4</sub> * x + a<sub>3</sub>) * x + a<sub>2</sub>) * x + a<sub>1</sub>) * x + a<sub>0</sub>
|
||||
|
||||
Другими словами:
|
||||
|
||||
> H<sub>i</sub> = (P * H<sub>i-1</sub> + S<sub>i</sub>) mod M
|
||||
|
||||
Функция `hash()` не может обрабатывать длинные строки, поскольку значение hashVal превышает размер int. Обратите внимание, что размер ключа всегда оказывается меньше размера массива.
|
||||
В методе Хорнера мы можем применять оператор по модулю (%) на каждом шаге вычисления. Это дает тот же результат, что и однократное применение оператора по модулю в
|
||||
конце, но позволяет избежать переполнения.
|
||||
|
||||
```javascript
|
||||
function hash(key, arraySize) {
|
||||
const base = 13;
|
||||
|
||||
let hash = 0;
|
||||
for (let charIndex = 0; charIndex < key.length; charIndex += 1) {
|
||||
const charCode = key.charCodeAt(charIndex);
|
||||
hash = (hash * base + charCode) % arraySize;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
```
|
||||
|
||||
Полиномиальное хеширование обладает свойством плавного перехода: хэш-сумма может эффективно обновляться при добавлении или удалении символов в концах строки
|
||||
(при условии, что сохранен массив степеней p по модулю M достаточной длины).
|
||||
Популярный алгоритм сопоставления Рабина–Карпа основан на этом свойстве
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Где использовать полиномиальное хеширование строк](https://www.mii.lt/olympiads_in_informatics/pdf/INFOL119.pdf)
|
||||
- [Хеширование на uTexas](https://www.cs.utexas.edu/~mitra/csSpring2017/cs313/lectures/hash.html)
|
||||
- [Хэш-функция в Википедии](https://en.wikipedia.org/wiki/Hash_function)
|
||||
- [Скользящий хэш в Википедии](https://en.wikipedia.org/wiki/Rolling_hash)
|
@ -0,0 +1,28 @@
|
||||
# Шифр ограждения рельсов
|
||||
|
||||
**Шифр ограждения рельсов** (также называемый зигзагообразным шифром) - это [транспозиционный шифр](https://en.wikipedia.org/wiki/Transposition_cipher), в котором сообщение разбивается на несколько рельсов для кодирования. Ограждение заполняется символами сообщения, начиная с верхнего левого угла и добавляя по символу в каждую позицию, пересекая их по диагонали до самого низа. Дойдя до последней рельсы, поверните по диагонали и зигзагообразными движениями направьте надпись вверх до самой первой рельсы. Повторяйте, пока надпись полностью не расположится поперек ограждения. Закодированное сообщение является результатом объединения текста на каждом рельсе сверху вниз.
|
||||
|
||||
Из [Википедии](https://en.wikipedia.org/wiki/Rail_fence_cipher) вот как выглядит сообщение `WE ARE DISCOVERED. FLEE AT ONCE` на ограждении из `3` рельсов:
|
||||
|
||||
```
|
||||
W . . . E . . . C . . . R . . . L . . . T . . . E
|
||||
. E . R . D . S . O . E . E . F . E . A . O . C .
|
||||
. . A . . . I . . . V . . . D . . . E . . . N . .
|
||||
-------------------------------------------------
|
||||
WECRLTEERDSOEEFEAOCAIVDEN
|
||||
```
|
||||
|
||||
Затем сообщение может быть расшифровано путем повторного создания закодированного ограждения с той же схемой обхода, за исключением того, что символы следует добавлять только к одному рельсу за раз. Чтобы проиллюстрировать это, можно добавить прочерк на рельсах, которые еще не должны быть заполнены. Вот как будет выглядеть забор после заполнения первого ограждения, черточки обозначают позиции, которые были посещены, но не заполнены.
|
||||
|
||||
```
|
||||
W . . . E . . . C . . . R . . . L . . . T . . . E
|
||||
. - . - . - . - . - . - . - . - . - . - . - . - .
|
||||
. . - . . . - . . . - . . . - . . . - . . . - . .
|
||||
```
|
||||
|
||||
Как только количество посещенных позиций ограждения сравняется с количеством символов в сообщении, можно приступать к заполнению следующего рельса.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Шифр ограждения рельсов в Википедии](https://en.wikipedia.org/wiki/Rail_fence_cipher)
|
||||
- [Калькулятор шифа ограждения рельсов](https://crypto.interactive-maths.com/rail-fence-cipher.html)
|
16
src/algorithms/graph/bellman-ford/README.ru-RU.md
Normal file
16
src/algorithms/graph/bellman-ford/README.ru-RU.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Алгоритм Беллмана–Форда
|
||||
|
||||
Алгоритм Беллмана–Форда - это алгоритм, который вычисляет кратчайшие пути от одной исходной вершины ко всем остальным вершинам взвешенного орграфа. Он медленнее алгоритма Дейкстры для решения той же задачи, но более универсален, поскольку способен обрабатывать графики, в которых некоторые веса ребер являются отрицательными числами.
|
||||
|
||||
![Bellman-Ford](https://upload.wikimedia.org/wikipedia/commons/2/2e/Shortest_path_Dijkstra_vs_BellmanFord.gif)
|
||||
|
||||
## Сложность
|
||||
|
||||
Наихудшая производительность `O(|V||E|)`
|
||||
Наилучшая производительность `O(|E|)`
|
||||
Наихудшое использование памяти `O(|V|)`
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Википедия](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm)
|
||||
- [Michael Sambol на YouTube](https://www.youtube.com/watch?v=obWXjtg0L64&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
|
Loading…
Reference in New Issue
Block a user