Перейти до основного вмісту

Лінійне діофантове рівняння

Лінійне діофантове рівняння (з двома змінними) — це рівняння загального вигляду:

ax+by=cax + by = c

де aa, bb, cc — задані цілі числа, а xx, yy — невідомі цілі числа.

У цій статті ми розглянемо кілька класичних задач про такі рівняння:

  • знаходження одного розв'язку
  • знаходження всіх розв'язків
  • знаходження кількості розв'язків і самих розв'язків на заданому проміжку
  • знаходження розв'язку з мінімальним значенням x+yx + y
Коли підходить цей алгоритм?

Вироджений випадок

Вироджений випадок, про який треба подбати, — це коли a=b=0a = b = 0. Легко бачити, що ми або не маємо розв'язків, або маємо їх нескінченно багато, залежно від того, чи c=0c = 0, чи ні. У решті статті ми ігноруватимемо цей випадок.

Аналітичний розв'язок

Коли a0a \neq 0 та b0b \neq 0, рівняння ax+by=cax+by=c можна еквівалентно розглядати як будь-яке з наступних:

\begin{align} ax &\equiv c \pmod b \ by &\equiv c \pmod a \end{align}

Без втрати загальності припустимо, що b0b \neq 0, і розглянемо перше рівняння. Коли aa і bb взаємно прості, його розв'язок задається як

xca1(modb),x \equiv ca^{-1} \pmod b,

де a1a^{-1} — це обернений елемент за модулем для aa за модулем bb.

Коли aa і bb не взаємно прості, значення axax за модулем bb для всіх цілих xx діляться на g=gcd(a,b)g=\gcd(a, b), тож розв'язок існує лише тоді, коли cc ділиться на gg. У цьому випадку один із розв'язків можна знайти, скоротивши рівняння на gg:

(a/g)x(c/g)(modb/g).(a/g) x \equiv (c/g) \pmod{b/g}.

За означенням gg, числа a/ga/g і b/gb/g взаємно прості, тож розв'язок задається явно як

{x(c/g)(a/g)1(modb/g),y=caxb.\begin{cases} x \equiv (c/g)(a/g)^{-1}\pmod{b/g},\\ y = \frac{c-ax}{b}. \end{cases}

Алгоритмічний розв'язок

Лема Безу (також відома як тотожність Безу) — корисний результат, який допоможе зрозуміти наведений нижче розв'язок.

Нехай g=gcd(a,b)g = \gcd(a,b). Тоді існують цілі числа x,yx,y такі, що ax+by=gax + by = g.

Більше того, gg — найменше таке додатне ціле число, яке можна записати у вигляді ax+byax + by; усі цілі числа вигляду ax+byax + by є кратними gg.

Щоб знайти один розв'язок діофантового рівняння з 2 невідомими, можна скористатися розширеним алгоритмом Евкліда. Спочатку припустимо, що aa і bb невід'ємні. Коли ми застосовуємо розширений алгоритм Евкліда для aa і bb, ми можемо знайти їхній найбільший спільний дільник gg і 2 числа xgx_g та ygy_g такі, що:

axg+byg=ga x_g + b y_g = g

Якщо cc ділиться на g=gcd(a,b)g = \gcd(a, b), то задане діофантове рівняння має розв'язок, інакше воно не має жодного розв'язку. Доведення прямолінійне: лінійна комбінація двох чисел ділиться на їхній спільний дільник.

Тепер припустимо, що cc ділиться на gg, тоді маємо:

axgcg+bygcg=ca \cdot x_g \cdot \frac{c}{g} + b \cdot y_g \cdot \frac{c}{g} = c

Отже, одним із розв'язків діофантового рівняння є:

x0=xgcg,x_0 = x_g \cdot \frac{c}{g}, y0=ygcg.y_0 = y_g \cdot \frac{c}{g}.

Наведена ідея все ще працює, коли aa або bb, або обидва вони від'ємні. Нам потрібно лише змінювати знак x0x_0 і y0y_0 за потреби.

Зрештою, ми можемо реалізувати цю ідею так (зауважте, що цей код не враховує випадок a=b=0a = b = 0):

int gcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
int x1, y1;
int d = gcd(b, a % b, x1, y1);
x = y1;
y = x1 - y1 * (a / b);
return d;
}

bool find_any_solution(int a, int b, int c, int &x0, int &y0, int &g) {
g = gcd(abs(a), abs(b), x0, y0);
if (c % g) {
return false;
}

x0 *= c / g;
y0 *= c / g;
if (a < 0) x0 = -x0;
if (b < 0) y0 = -y0;
return true;
}

Отримання всіх розв'язків

З одного розв'язку (x0,y0)(x_0, y_0) ми можемо отримати всі розв'язки заданого рівняння.

Нехай g=gcd(a,b)g = \gcd(a, b) і нехай x0,y0x_0, y_0 — цілі числа, які задовольняють таке:

ax0+by0=ca \cdot x_0 + b \cdot y_0 = c

Тепер ми побачимо, що додавання b/gb / g до x0x_0 і водночас віднімання a/ga / g від y0y_0 не порушить рівність:

a(x0+bg)+b(y0ag)=ax0+by0+abgbag=ca \cdot \left(x_0 + \frac{b}{g}\right) + b \cdot \left(y_0 - \frac{a}{g}\right) = a \cdot x_0 + b \cdot y_0 + a \cdot \frac{b}{g} - b \cdot \frac{a}{g} = c

Очевидно, цей процес можна повторювати знову, тож усі числа вигляду:

x=x0+kbgx = x_0 + k \cdot \frac{b}{g} y=y0kagy = y_0 - k \cdot \frac{a}{g}

є розв'язками заданого діофантового рівняння.

Оскільки рівняння лінійне, усі розв'язки лежать на одній прямій, і за означенням gg це і є множина всіх можливих розв'язків заданого діофантового рівняння.

Знаходження кількості розв'язків і розв'язків на заданому проміжку

З попереднього розділу має бути зрозуміло, що якщо ми не накладаємо жодних обмежень на розв'язки, то їх буде нескінченно багато. Тож у цьому розділі ми додаємо деякі обмеження на проміжок xx і yy та спробуємо порахувати й перелічити всі розв'язки.

Нехай задано два проміжки: [minx;maxx][min_x; max_x] і [miny;maxy][min_y; max_y], і скажімо, що ми хочемо знайти лише розв'язки на цих двох проміжках.

Зауважте, що якщо aa або bb дорівнює 00, то задача має лише один розв'язок. Ми не розглядаємо цей випадок тут.

Спочатку ми можемо знайти розв'язок, який має мінімальне значення xx таке, що xminxx \ge min_x. Щоб це зробити, ми спершу знаходимо будь-який розв'язок діофантового рівняння. Потім ми зсуваємо цей розв'язок, щоб отримати xminxx \ge min_x (використовуючи те, що ми знаємо про множину всіх розв'язків з попереднього розділу). Це можна зробити за O(1)O(1). Позначимо це мінімальне значення xx через lx1l_{x1}.

Аналогічно ми можемо знайти максимальне значення xx, яке задовольняє xmaxxx \le max_x. Позначимо це максимальне значення xx через rx1r_{x1}.

Аналогічно ми можемо знайти мінімальне значення yy (yminy)(y \ge min_y) і максимальне значення yy (ymaxy)(y \le max_y). Позначимо відповідні значення xx через lx2l_{x2} і rx2r_{x2}.

Остаточним розв'язком є всі розв'язки з x у перетині [lx1,rx1][l_{x1}, r_{x1}] і [lx2,rx2][l_{x2}, r_{x2}]. Позначимо цей перетин через [lx,rx][l_x, r_x].

Нижче наведено код, що реалізує цю ідею. Зверніть увагу, що на початку ми ділимо aa і bb на gg. Оскільки рівняння ax+by=ca x + b y = c еквівалентне рівнянню agx+bgy=cg\frac{a}{g} x + \frac{b}{g} y = \frac{c}{g}, ми можемо використати останнє замість нього і мати gcd(ag,bg)=1\gcd(\frac{a}{g}, \frac{b}{g}) = 1, що спрощує формули.

void shift_solution(int & x, int & y, int a, int b, int cnt) {
x += cnt * b;
y -= cnt * a;
}

int find_all_solutions(int a, int b, int c, int minx, int maxx, int miny, int maxy) {
int x, y, g;
if (!find_any_solution(a, b, c, x, y, g))
return 0;
a /= g;
b /= g;

int sign_a = a > 0 ? +1 : -1;
int sign_b = b > 0 ? +1 : -1;

shift_solution(x, y, a, b, (minx - x) / b);
if (x < minx)
shift_solution(x, y, a, b, sign_b);
if (x > maxx)
return 0;
int lx1 = x;

shift_solution(x, y, a, b, (maxx - x) / b);
if (x > maxx)
shift_solution(x, y, a, b, -sign_b);
int rx1 = x;

shift_solution(x, y, a, b, -(miny - y) / a);
if (y < miny)
shift_solution(x, y, a, b, -sign_a);
if (y > maxy)
return 0;
int lx2 = x;

shift_solution(x, y, a, b, -(maxy - y) / a);
if (y > maxy)
shift_solution(x, y, a, b, sign_a);
int rx2 = x;

if (lx2 > rx2)
swap(lx2, rx2);
int lx = max(lx1, lx2);
int rx = min(rx1, rx2);

if (lx > rx)
return 0;
return (rx - lx) / abs(b) + 1;
}

Щойно ми маємо lxl_x і rxr_x, перелічити всі розв'язки теж просто. Достатньо проходити через x=lx+kbgx = l_x + k \cdot \frac{b}{g} для всіх k0k \ge 0, доки x=rxx = r_x, і знаходити відповідні значення yy за допомогою рівняння ax+by=ca x + b y = c.

Знаходження розв'язку з мінімальним значенням x+yx + y

Тут xx і yy також потрібно накласти деяке обмеження, інакше відповідь може стати мінус нескінченністю.

Ідея схожа на попередній розділ: ми знаходимо будь-який розв'язок діофантового рівняння, а потім зсуваємо розв'язок, щоб задовольнити деякі умови.

Зрештою, скористаємося знанням про множину всіх розв'язків, щоб знайти мінімум:

x=x+kbg,x' = x + k \cdot \frac{b}{g}, y=ykag.y' = y - k \cdot \frac{a}{g}.

Зауважте, що x+yx + y змінюється так:

x+y=x+y+k(bgag)=x+y+kbagx' + y' = x + y + k \cdot \left(\frac{b}{g} - \frac{a}{g}\right) = x + y + k \cdot \frac{b-a}{g}

Якщо a<ba < b, нам потрібно вибрати найменше можливе значення kk. Якщо a>ba > b, нам потрібно вибрати найбільше можливе значення kk. Якщо a=ba = b, усі розв'язки матимуть однакову суму x+yx + y.

Задачі для практики

Відеоматеріали