Шугаман Диофантын тэгшитгэл¶
Шугаман Диофантын тэгшитгэл (хоёр хувьсагчтай) нь дараах ерөнхий хэлбэртэй тэгшитгэл юм:
энд $a$, $b$, $c$ нь өгөгдсөн бүхэл тоо, $x$, $y$ нь үл мэдэгдэх бүхэл тоо.
Энэ өгүүлэлд бид эдгээр тэгшитгэлийн хэд хэдэн сонгодог бодлогыг авч үзнэ:
- нэг шийд олох
- бүх шийдийг олох
- өгөгдсөн интервал дахь шийдийн тоо ба шийдүүдийг өөрсдийг нь олох
- $x + y$-ийн хамгийн бага утгатай шийдийг олох
Degenerate case¶
Анхаарах ёстой degenerate case бол $a = b = 0$ үе юм. $c = 0$ эсэхээс хамааран бид шийдгүй эсвэл төгсгөлгүй олон шийдтэй болохыг харахад амархан. Энэ өгүүллийн үлдсэн хэсэгт бид энэ тохиолдлыг үл тоомсорлоно.
Аналитик шийд¶
$a \neq 0$ ба $b \neq 0$ үед $ax+by=c$ тэгшитгэлийг дараах хоёрын аль нэгтэй эквивалентаар авч үзэж болно:
Ерөнхий чанарыг алдагдуулалгүйгээр $b \neq 0$ гэж үзээд эхний тэгшитгэлийг авч үзье. $a$ ба $b$ харилцан анхны үед түүний шийд нь дараах байдлаар өгөгдөнө
энд $a^{-1}$ нь $b$ модулиар авсан $a$-гийн модулийн урвуу юм.
$a$ ба $b$ харилцан анхны биш үед бүх бүхэл $x$-ийн хувьд $ax$-ийн $b$ модулиар авсан утгууд нь $g=\gcd(a, b)$-д хуваагдана, тиймээс шийд зөвхөн $c$ нь $g$-д хуваагдах үед оршино. Энэ тохиолдолд тэгшитгэлийг $g$-ээр хураах замаар нэг шийдийг олж болно:
$g$-ийн тодорхойлолтоор $a/g$ ба $b/g$ тоонууд харилцан анхны байх тул шийд нь тодорхой байдлаар дараах хэлбэртэй байна
Алгоритмын шийд¶
Безугийн лемм (Безугийн адилтгал гэж бас нэрлэдэг) нь дараах шийдийг ойлгоход хэрэгтэй үр дүн юм.
$g = \gcd(a,b)$ гэе. Тэгвэл $ax + by = g$ байх $x,y$ бүхэл тоонууд оршино.
Түүнчлэн $g$ нь $ax + by$ хэлбэрээр бичигдэх хамгийн бага эерэг бүхэл тоо бөгөөд $ax + by$ хэлбэрийн бүх бүхэл тоо нь $g$-ийн үржвэр байна.
Хоёр үл мэдэгдэхтэй Диофантын тэгшитгэлийн нэг шийдийг олохын тулд Өргөтгөсөн Евклидийн алгоритм-ыг ашиглаж болно. Эхлээд $a$ ба $b$ сөрөг биш гэж үзье. $a$ ба $b$-д Өргөтгөсөн Евклидийн алгоритмыг хэрэглэхэд бид тэдгээрийн хамгийн их ерөнхий хуваагч $g$ ба дараах нөхцөлийг хангах $x_g$, $y_g$ хоёр тоог олж болно:
Хэрэв $c$ нь $g = \gcd(a, b)$-д хуваагддаг бол өгөгдсөн Диофантын тэгшитгэл шийдтэй, эсрэг тохиолдолд ямар ч шийдгүй. Баталгаа нь маш энгийн: хоёр тооны шугаман комбинац нь тэдгээрийн ерөнхий хуваагчид хуваагдана.
Одоо $c$ нь $g$-д хуваагдана гэж үзвэл бидэнд дараах илэрхийлэл гарна:
Тиймээс Диофантын тэгшитгэлийн нэг шийд нь:
Дээрх санаа нь $a$ эсвэл $b$, эсвэл хоёул сөрөг байсан ч ажиллана. Бид зөвхөн шаардлагатай үед $x_0$ ба $y_0$-ийн тэмдгийг өөрчлөх хэрэгтэй.
Эцэст нь энэ санааг дараах байдлаар хэрэгжүүлж болно ($a = 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;
}
Бүх шийдийг олох¶
Нэг шийд $(x_0, y_0)$-ээс өгөгдсөн тэгшитгэлийн бүх шийдийг гаргаж авч болно.
$g = \gcd(a, b)$ гэе, мөн $x_0, y_0$ нь дараах нөхцөлийг хангах бүхэл тоо байг:
Одоо $x_0$ дээр $b / g$ нэмж, зэрэгцээгээр $y_0$-ээс $a / g$ хасахад тэнцэл эвдрэхгүйг харах хэрэгтэй:
Мэдээжийн хэрэг энэ үйлдлийг дахин давтаж болох тул дараах хэлбэрийн бүх тоо:
нь өгөгдсөн Диофантын тэгшитгэлийн шийд болно.
Тэгшитгэл шугаман тул бүх шийд нэг шулуун дээр оршино, мөн $g$-ийн тодорхойлолтоор энэ бол өгөгдсөн Диофантын тэгшитгэлийн бүх боломжит шийдийн олонлог юм.
Өгөгдсөн интервал дахь шийдийн тоо ба шийдүүдийг олох¶
Өмнөх хэсгээс харахад хэрэв бид шийдэд ямар ч хязгаарлалт тавихгүй бол тэдгээр нь төгсгөлгүй олон байх нь тодорхой. Тиймээс энэ хэсэгт бид $x$ ба $y$-ийн интервалд зарим хязгаарлалт нэмж, бүх шийдийг тоолж, жагсаахыг оролдоно.
$[min_x; max_x]$ ба $[min_y; max_y]$ хоёр интервал байг, мөн бид зөвхөн эдгээр хоёр интервал дахь шийдийг олохыг хүсэж байна гэе.
Хэрэв $a$ эсвэл $b$ нь $0$ бол бодлого зөвхөн нэг шийдтэй болохыг анхаараарай. Энэ тохиолдлыг бид энд авч үзэхгүй.
Эхлээд бид $x \ge min_x$ байх, $x$-ийн хамгийн бага утгатай шийдийг олж болно. Үүний тулд эхлээд Диофантын тэгшитгэлийн дурын шийдийг олно. Дараа нь энэ шийдийг $x \ge min_x$ болтол шилжүүлнэ (өмнөх хэсэгт бүх шийдийн олонлогийн талаар мэдсэн зүйлээ ашиглан). Үүнийг $O(1)$-д хийж болно. $x$-ийн энэ хамгийн бага утгыг $l_{x1}$ гэж тэмдэглэе.
Үүнтэй адилаар $x \le max_x$ байх $x$-ийн хамгийн их утгыг олж болно. $x$-ийн энэ хамгийн их утгыг $r_{x1}$ гэж тэмдэглэе.
Үүнтэй адилаар $y$-ийн хамгийн бага утга $(y \ge min_y)$ ба хамгийн их утга $(y \le max_y)$-г олж болно. Харгалзах $x$-ийн утгуудыг $l_{x2}$ ба $r_{x2}$ гэж тэмдэглэе.
Эцсийн шийд бол $[l_{x1}, r_{x1}]$ ба $[l_{x2}, r_{x2}]$-ийн огтлолцолд орших $x$-тэй бүх шийд юм. Энэ огтлолцлыг $[l_x, r_x]$ гэж тэмдэглэе.
Following is the code implementing this idea. Notice that we divide $a$ and $b$ at the beginning by $g$. Since the equation $a x + b y = c$ is equivalent to the equation $\frac{a}{g} x + \frac{b}{g} y = \frac{c}{g}$, we can use this one instead and have $\gcd(\frac{a}{g}, \frac{b}{g}) = 1$, which simplifies the formulas.
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;
}
$l_x$ ба $r_x$-г олсны дараа бүх шийдийг жагсаах нь бас энгийн. Зөвхөн $x = r_x$ болтол бүх $k \ge 0$-ийн хувьд $x = l_x + k \cdot \frac{b}{g}$-ээр давтаж, $a x + b y = c$ тэгшитгэл ашиглан харгалзах $y$ утгуудыг олоход хангалттай.
$x + y$-ийн хамгийн бага утгатай шийдийг олох¶
Энд $x$ ба $y$-д бас зарим хязгаарлалт өгөх шаардлагатай, эс тэгвээс хариу хасах хязгааргүй болж магадгүй.
Санаа нь өмнөх хэсэгтэй адил: бид Диофантын тэгшитгэлийн дурын шийдийг олоод, дараа нь зарим нөхцөлийг хангахаар шийдийг шилжүүлнэ.
Эцэст нь бүх шийдийн олонлогийн талаарх мэдлэгээ ашиглан хамгийн багыг олно:
$x + y$ дараах байдлаар өөрчлөгдөхийг анхаараарай:
Хэрэв $a < b$ бол бид $k$-гийн хамгийн бага боломжит утгыг сонгох хэрэгтэй. Хэрэв $a > b$ бол $k$-гийн хамгийн их боломжит утгыг сонгоно. Хэрэв $a = b$ бол бүх шийд ижил $x + y$ нийлбэртэй байна.