Факторизация за корень: различия между версиями

Материал из Algocode wiki
Перейти к: навигация, поиск
(Начала писать страницу про факторизацию)
 
(Добавлено описание алгоритма , код, асимптотика)
 
Строка 1: Строка 1:
 
==Факторизация за $O(\sqrt{N})$==
 
==Факторизация за $O(\sqrt{N})$==
Любое натуральное число можно разложить на произведение простых, и с такой записью очень легко работать при решении задач.
+
 
 +
===Базовая теория===
 +
Любое натуральное число можно разложить на произведение простых (об этом говорит [https://ru.wikipedia.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%B0%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B8 основная теорма арифметики]), и с такой записью очень легко работать при решении задач.
 
{{Определение
 
{{Определение
 
|Термин=Факторизация
 
|Термин=Факторизация
Строка 10: Строка 12:
 
$$100 = 2 \times 2 \times 5 \times 5 = 2^2 \times 5^2$$
 
$$100 = 2 \times 2 \times 5 \times 5 = 2^2 \times 5^2$$
 
$$126 = 2 \times 3 \times 3 \times 7 = 2^1 \times 3^2 \times 7^1$$
 
$$126 = 2 \times 3 \times 3 \times 7 = 2^1 \times 3^2 \times 7^1$$
 +
Рассмотрим такую задачу:
 +
 +
<b>Условие:</b> Нужно разбить $N$ людей на группы равного размера. Нам интересно, какие размеры это могут быть и сколькими способами это можно сделать.
 +
 +
<b>Решение:</b> По сути нас просят найти число различных делителей $N$. Нужно посмотреть на разложение числа $N$ на простые множители, в общем виде оно выглядит так:
 +
 +
$$N= p_1^{a_1} \times p_2^{a_2} \times \ldots \times p_k^{a_k}$$
 +
 +
Теперь подумаем над этим выражением с точки зрения комбинаторики. Чтобы «сгенерировать» какой-нибудь делитель, нужно подставить в степень $i$-го простого число от 0 до $a_i$ (то есть $a_i+1$ различное значение), и так для каждого. То есть делитель $N$ выглядит ровно так:
 +
$$M= p_1^{b_1} \times p_2^{b_2} \times \ldots \times p_k^{b_k}, \ \ 0 \leq b_i \leq a_i$$
 +
Значит, ответом будет произведение $(a_1+1) \times (a_2+1) \times \ldots \times (a_k + 1)$.
 +
 +
===Описание алгоритма===
 +
Применяя [[Проверка на простоту за корень|
 +
алгоритм проверки числа на простоту]], мы умеем легко находить <b>минимальный простой делитель числа N</b>. Ясно, что как только мы нашли простой делитель числа $N$, мы можем число $N$ на него поделить и продолжить искать новый минимальный простой делитель.
 +
 +
Будем перебирать простой делитель от $2$ до корня из $N$ (как и раньше), но в случае, если $N$ делится на этот делитель, будем просто на него делить. Причем, возможно, нам понадобится делить несколько раз ($N$ может делиться на большую степень этого простого делителя). Так мы будем набирать простые делители и остановимся в тот момент, когда $N$ стало либо $1$, либо простым (и мы остановились, так как дошли до корня из него). Во втором случае надо еще само $N$ добавить в ответ.
 +
<syntaxhighlight lang="C++" line=true>
 +
vector<int> factorize(int N) {
 +
    vector<int> result;
 +
    for (int i = 2; i * i <= N; i++) {
 +
        while (N % i == 0) {
 +
            result.push_back(i);
 +
            N /= i;
 +
        }
 +
    }
 +
    if (N != 1) {
 +
        result.push_back(N);
 +
    }
 +
    return result;
 +
}
 +
</syntaxhighlight>
 +
 +
===Асимптотика===
 +
Заметим, что итераций цикла <tt>for</tt> будет не более $\bigl\lceil\sqrt{N}\bigr\rceil$, то есть $O\left(\sqrt{N}\right)$. При делении на какое-то простое число $N$ уменьшается хотя бы в два раза, поэтому суммарное число итераций цикла <tt>while</tt> не превосходит $\log N$. Значит, весь алгоритм работает за $O\left(\log N + \sqrt{N}\right) = O\left(\sqrt{N}\right)$.
 +
 +
{{Автор|Полина Романченко|Romanchenko}}
 +
 +
[[Категория:Конспект]]
 +
[[Категория:Теория чисел]]

Текущая версия на 11:59, 26 августа 2019

Факторизация за $O(\sqrt{N})$

Базовая теория

Любое натуральное число можно разложить на произведение простых (об этом говорит основная теорма арифметики), и с такой записью очень легко работать при решении задач.

Определение:
Факторизация —это разложение на простые множители

Примеры: $$11 = 11 = 11^1$$ $$100 = 2 \times 2 \times 5 \times 5 = 2^2 \times 5^2$$ $$126 = 2 \times 3 \times 3 \times 7 = 2^1 \times 3^2 \times 7^1$$ Рассмотрим такую задачу:

Условие: Нужно разбить $N$ людей на группы равного размера. Нам интересно, какие размеры это могут быть и сколькими способами это можно сделать.

Решение: По сути нас просят найти число различных делителей $N$. Нужно посмотреть на разложение числа $N$ на простые множители, в общем виде оно выглядит так:

$$N= p_1^{a_1} \times p_2^{a_2} \times \ldots \times p_k^{a_k}$$

Теперь подумаем над этим выражением с точки зрения комбинаторики. Чтобы «сгенерировать» какой-нибудь делитель, нужно подставить в степень $i$-го простого число от 0 до $a_i$ (то есть $a_i+1$ различное значение), и так для каждого. То есть делитель $N$ выглядит ровно так: $$M= p_1^{b_1} \times p_2^{b_2} \times \ldots \times p_k^{b_k}, \ \ 0 \leq b_i \leq a_i$$ Значит, ответом будет произведение $(a_1+1) \times (a_2+1) \times \ldots \times (a_k + 1)$.

Описание алгоритма

Применяя алгоритм проверки числа на простоту, мы умеем легко находить минимальный простой делитель числа N. Ясно, что как только мы нашли простой делитель числа $N$, мы можем число $N$ на него поделить и продолжить искать новый минимальный простой делитель.

Будем перебирать простой делитель от $2$ до корня из $N$ (как и раньше), но в случае, если $N$ делится на этот делитель, будем просто на него делить. Причем, возможно, нам понадобится делить несколько раз ($N$ может делиться на большую степень этого простого делителя). Так мы будем набирать простые делители и остановимся в тот момент, когда $N$ стало либо $1$, либо простым (и мы остановились, так как дошли до корня из него). Во втором случае надо еще само $N$ добавить в ответ.

 1 vector<int> factorize(int N) {
 2     vector<int> result;
 3     for (int i = 2; i * i <= N; i++) {
 4         while (N % i == 0) {
 5             result.push_back(i);
 6             N /= i;
 7         }
 8     }
 9     if (N != 1) {
10         result.push_back(N);
11     }
12     return result;
13 }

Асимптотика

Заметим, что итераций цикла for будет не более $\bigl\lceil\sqrt{N}\bigr\rceil$, то есть $O\left(\sqrt{N}\right)$. При делении на какое-то простое число $N$ уменьшается хотя бы в два раза, поэтому суммарное число итераций цикла while не превосходит $\log N$. Значит, весь алгоритм работает за $O\left(\log N + \sqrt{N}\right) = O\left(\sqrt{N}\right)$.



Автор конспекта: Полина Романченко

По всем вопросам пишите в telegram @Romanchenko