-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4093d3c
commit 83db148
Showing
8 changed files
with
102 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Разделяй и властвуй | ||
|
||
## Описание | ||
|
||
**Разделяй и властвуй** (Divide and Conquer) - это метод решения задачи, который заключается в разбиении задачи | ||
на более мелкие подзадачи, а затем объединении результатов решения подзадач в решение исходной | ||
задачи. В общем случае алгоритм "разделяй и властвуй" состоит из трех шагов: | ||
|
||
1. **Разделение**. Задача разбивается на несколько подзадач, которые являются меньшими экземплярами исходной задачи. | ||
2. **Властвование**. Подзадачи решаются рекурсивно. Если подзадачи достаточно малы, то они решаются непосредственно. | ||
3. **Объединение**. Решения подзадач объединяются в решение исходной задачи. | ||
|
||
Важно, что подзадачи должны быть независимыми, то есть решение одной подзадачи не должно зависеть от решения другой. | ||
|
||
**Принцип уменьшай и властвуй** (Decrease and Conquer) - это вариация принципа разделяй и властвуй, в которой задача | ||
переходит в более маленькую задачу. | ||
|
||
## Применение | ||
|
||
Мы уже рассмотрели несколько алгоритмов, которые основаны на принципе разделяй и властвуй: | ||
|
||
- Наибольший общий делитель | ||
- Бинарный поиск | ||
- Сортировка слиянием | ||
- Поиск k-ой порядковой статистики |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,15 @@ | ||
## Разделяй и властвуй | ||
# Задачи по "разделяй и властвуй" | ||
|
||
### Принцип разделяй и властвуй | ||
## A. Возведение в степень | ||
|
||
**Принцип разделяй и властвуй** (Divide and Conquer) - это метод решения задачи, который заключается в разбиении задачи на | ||
более мелкие подзадачи, а затем объединении результатов решения подзадач в решение исходной | ||
задачи. В общем случае алгоритм "разделяй и властвуй" состоит из трех шагов: | ||
| Поле | Значение | | ||
|-----------|-------------------------------------------------| | ||
| Сложность | Средняя | | ||
| Источник | https://leetcode.com/problems/powx-n/solutions/ | | ||
|
||
1. **Разделение**. Задача разбивается на несколько подзадач, которые являются меньшими экземплярами исходной задачи. | ||
2. **Властвование**. Подзадачи решаются рекурсивно. Если подзадачи достаточно малы, то они решаются непосредственно. | ||
3. **Объединение**. Решения подзадач объединяются в решение исходной задачи. | ||
## B. Умножение больших чисел | ||
|
||
Важно, что подзадачи должны быть независимыми, то есть решение одной подзадачи не должно зависеть от решения другой. | ||
|
||
**Принцип уменьшай и властвуй** (Decrease and Conquer) - это вариация принципа разделяй и властвуй, в которой задача | ||
переходит в более маленькую задачу. | ||
|
||
### Применение | ||
|
||
- Наибольший общий делитель | ||
- Бинарный поиск | ||
- Сортировка слиянием | ||
- Поиск медианы | ||
- Поиск k-ой порядковой статистики | ||
- Быстрое возведение в степень | ||
- Умножение больших чисел | ||
|
||
### Задачи | ||
|
||
| Задача | Код | | ||
|-------------------------|------------| | ||
| Возведение в степень | `power` | | ||
| Умножение больших чисел | `multiply` | | ||
| Поле | Значение | | ||
|-----------|-------------------------------------------------| | ||
| Сложность | Средняя | | ||
| Источник | https://leetcode.com/problems/multiply-strings/ | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +0,0 @@ | ||
from .multiply import multiply_naive, multiply_dnc, karatsuba | ||
from .power import fast_power, power | ||
|
||
__all__ = [ | ||
"multiply_naive", | ||
"multiply_dnc", | ||
"karatsuba", | ||
"fast_power", | ||
"power", | ||
] | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,119 +1,107 @@ | ||
"""Быстрое умножение целых чисел.""" | ||
# O(n^2), где n - количество бит в максимальном числе | ||
import math | ||
|
||
|
||
def multiply_naive(x: int, y: int) -> int: | ||
""" | ||
Умножение двух больших чисел методом "в лоб". Сложность O(N^2), где N - количество бит в числе. | ||
def multiply(x: str, y: str) -> str: | ||
return str(mul_karatsuba(int(x), int(y))) | ||
|
||
|
||
# O(n^2), где n - количество бит в y | ||
def mul_naive(x: int, y: int) -> int: | ||
""" | ||
Принцип работы: | ||
y = { | ||
2 * (y // 2), если y четный | ||
1 + 2 * (y // 2), если y нечетный | ||
2 * (y // 2), если y - четное | ||
1 + 2 * (y // 2), если y - нечетное | ||
} | ||
x * y = { | ||
2 * (x * y // 2), если y четный | ||
x + 2 * (x * y // 2), если y нечетный | ||
2 * (x * y // 2), если y - четное | ||
x + 2 * (x * y // 2), если y - нечетное | ||
} | ||
""" | ||
if y == 0: | ||
return 0 | ||
|
||
# Всего будет сделано N = log2(y) рекурсивных вызовов. | ||
z = multiply_naive(x, y >> 1) | ||
|
||
# На каждом вызове будет сделано O(1) операций, если мы имеем дело с небольшими числами, потому что | ||
# используются побитовые операции. Однако, если мы имеем дело с большими числами, которые представляются | ||
# строками, то на каждом вызове будет | ||
# сделано O(N) операций. | ||
# Всего будет сделано n = log2(y) рекурсивных вызовов. | ||
# z = x * y // 2 | ||
z = mul_naive(x, y >> 1) | ||
# Если y - четное, то x * y = 2 * (x * y // 2) = 2 * z | ||
if y & 1 == 0: | ||
return z << 1 | ||
# Если y - нечетное, то x * y = x + 2 * (x * y // 2) = x + 2 * z | ||
else: | ||
return x + (z << 1) | ||
|
||
|
||
def length(x: int) -> int: | ||
"""Длина числа в двоичной системе счисления. Сложность O(log(X)).""" | ||
if x <= 1: | ||
return 1 | ||
|
||
result = 0 | ||
while x > 0: | ||
x >>= 1 | ||
result += 1 | ||
|
||
return result | ||
|
||
|
||
def two_halves(x: int, n: int) -> tuple[int, int]: | ||
"""Получение левой и правой частей числа. Сложность O(1).""" | ||
half_n = n >> 1 | ||
x_l = x >> half_n | ||
x_r = x & ((1 << half_n) - 1) | ||
|
||
return x_l, x_r | ||
|
||
|
||
def multiply_dnc(x: int, y: int) -> int: | ||
# O(n^2), где n - количество бит в максимальном числе | ||
def mul_dnc(x: int, y: int) -> int: | ||
""" | ||
Умножение двух больших чисел методом разделяй и властвуй. Сложность всё ещё O(N^2), где N - количество бит | ||
в числе. | ||
Принцип работы: | ||
Разобьём числа на их левую и правую части: | ||
Разобьём числа на левую и правую части: | ||
x = [x_l][x_r] = x_l * 2^(n // 2) + x_r | ||
y = [y_l][y_r] = y_l * 2^(n // 2) + y_r | ||
x * y = (x_l * 2^(n // 2) + x_r) * (y_l * 2^(n // 2) + y_r) = | ||
= 2^n * (x_l * y_l) + 2^(n // 2) * ((x_l * y_r) + (x_r * y_l)) + (x_r * y_r) | ||
Таким образом, задача разделяется на 4 подзадачи: | ||
Задача разделяется на 4 подзадачи: | ||
1) x_l * y_l | ||
2) x_l * y_r | ||
3) x_r * y_l | ||
4) x_r * y_r | ||
""" | ||
|
||
n = max(length(x), length(y)) | ||
|
||
n = max(bit_length(x), bit_length(y)) | ||
if n <= 16: | ||
return x * y | ||
|
||
half_n = n >> 1 | ||
x_l, x_r = two_halves(x, n) | ||
y_l, y_r = two_halves(y, n) | ||
|
||
# 2^n * (x_l * y_l) + 2^(n // 2) * ((x_l * y_r) + (x_r * y_l)) + (x_r * y_r) | ||
p1 = multiply_dnc(x_l, y_l) | ||
p2 = multiply_dnc(x_l, y_r) | ||
p3 = multiply_dnc(x_r, y_l) | ||
p4 = multiply_dnc(x_r, y_r) | ||
|
||
p1 = mul_dnc(x_l, y_l) | ||
p2 = mul_dnc(x_l, y_r) | ||
p3 = mul_dnc(x_r, y_l) | ||
p4 = mul_dnc(x_r, y_r) | ||
return (p1 << 2 * half_n) + ((p2 + p3) << half_n) + p4 | ||
|
||
|
||
def karatsuba(x: int, y: int) -> int: | ||
# O(n^log2(3)) = O(n^1.6), где n - количество бит в максимальном числе | ||
def mul_karatsuba(x: int, y: int) -> int: | ||
""" | ||
Умножение двух больших чисел методом Карацубы. Сложность O(N^log2(3)) = O(N^1.6), где N - количество | ||
бит в числе. | ||
Принцип работы: | ||
Вместо 4 рекурсивных вызовов будем делать 3 вызова: | ||
1) (x_l * y_l) | ||
2) (x_r * y_r) | ||
3) (x_l + x_r) * (y_l + y_r) | ||
Тогда слагаемое (x_l * y_r) + (x_r * y_l) можно выразить через слагаемые 1), 2) и 3): | ||
(x_l * y_r + x_r * y_l) = (x_l + x_r)*(y_l + y_r) - (x_l * y_l) - (x_r * y_r) | ||
""" | ||
n = max(length(x), length(y)) | ||
|
||
n = max(bit_length(x), bit_length(y)) | ||
if n <= 16: | ||
return x * y | ||
|
||
x_l, x_r = two_halves(x, n) | ||
y_l, y_r = two_halves(y, n) | ||
p1 = karatsuba(x_l, y_l) | ||
p2 = karatsuba(x_r, y_r) | ||
p3 = karatsuba(x_l + x_r, y_l + y_r) | ||
|
||
p1 = mul_karatsuba(x_l, y_l) | ||
p2 = mul_karatsuba(x_r, y_r) | ||
p3 = mul_karatsuba(x_l + x_r, y_l + y_r) | ||
half_n = n >> 1 | ||
|
||
return (p1 << 2 * half_n) + ((p3 - p1 - p2) << half_n) + p2 | ||
|
||
|
||
# O(n), где n - количество бит в числе | ||
def bit_length(x: int) -> int: | ||
# Не делайте так. | ||
# if x <= 1: | ||
# return 1 | ||
# result = 0 | ||
# while x > 0: | ||
# x >>= 1 | ||
# result += 1 | ||
# return result | ||
return math.ceil(math.log2(x + 1)) | ||
|
||
|
||
# O(n), где n - количество бит в числе | ||
def two_halves(x: int, n: int) -> tuple[int, int]: | ||
half_n = n >> 1 | ||
x_l = x >> half_n | ||
x_r = x & ((1 << half_n) - 1) | ||
return x_l, x_r |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters