15. FPB dan KPK — Pembahasan PR
Berkas .cpp jawabannya ada di
exercises/15/homework/solutions/.
Halaman ini dibuka sesudah mencoba sendiri. Bandingkan jalan pikiranmu dengan bagian Cara berpikir, bukan kodenya baris per baris: dua kode yang berbeda bentuk bisa sama-sama benar, dan yang perlu ditiru adalah cara sampai ke sana. Bagian Uji di tiap soal berisi masukan dan keluaran yang sudah diperiksa — jalankan jawabanmu sendiri dengan masukan itu sebelum merasa selesai.
Menjalankan salah satu jawabannya:
g++ -std=c++17 -O2 -o jawab exercises/15/homework/solutions/01-fpb-kpk.cpp
./jawab
Soal 1 — FPB dan KPK
Soal. Cetak FPB lalu KPK dari dua bilangan sampai 10¹⁸.
Cara berpikir. FPB dengan algoritma Euclid, KPK dari
sifat FPB × KPK = a × b. Yang diuji di sini adalah urutan operasinya:
a / g * b, bukan a * b / g.
Pembahasan.
long long g = fpb(a, b);
cout << g << "\n";
cout << a / g * b << "\n";Uji.
| Masukan | Keluaran (dua baris) |
|---|---|
48 18 |
6 lalu 144 |
7 7 |
7 lalu 7 |
1000000000000000000 500000000000000000 |
500000000000000000 lalu
1000000000000000000 |
Baris kedua menguji dua bilangan yang sama: FPB dan KPK-nya sama-sama
bilangan itu sendiri. Baris ketiga menguji urutan operasi — dengan
a * b / fpb(a, b), perkaliannya meluap jauh sebelum sempat
dibagi.
Kesalahan umum.
- Menulis
a * b / g. Untukadanbberukuran 4 × 10⁹, perkaliannya sudah 1,6 × 10¹⁹ dan meluap sebelum dibagi. - Memakai faktorisasi prima. Untuk 10¹⁸, memfaktorkan saja sudah terlalu lama; Euclid selesai dalam puluhan langkah.
Soal 2 — Sederhanakan pecahan
Soal. Cetak p/q dalam bentuk paling
sederhana.
Cara berpikir. Bagi keduanya dengan FPB-nya. Satu pembagian saja sudah menghasilkan bentuk paling sederhana — tidak perlu diulang, karena setelah dibagi FPB, pembilang dan penyebutnya pasti saling prima.
Pembahasan.
long long g = fpb(p, q);
cout << p / g << "/" << q / g << "\n";Uji.
| Masukan | Keluaran | Catatan |
|---|---|---|
12 18 |
2/3 |
dibagi FPB = 6 |
7 3 |
7/3 |
sudah paling sederhana (saling prima) |
100 10 |
10/1 |
penyebutnya menjadi 1, tetap dicetak |
Baris ketiga adalah keputusan format yang perlu diperhatikan: soal
meminta bentuk p/q, jadi 10/1 yang benar —
bukan 10. Kalau soal sungguhan meminta sebaliknya, itu
harus disebut di deskripsinya.
Kesalahan umum.
- Membagi berulang dengan 2, 3, 5, … sampai tidak bisa lagi. Benar, tetapi jauh lebih panjang dan mudah salah.
- Lupa bahwa
fpb(p, q)tidak pernah nol selama salah satunya positif.
Soal 3 — FPB sederet bilangan
Soal. FPB dari n bilangan.
Cara berpikir. FPB bersifat beruntun: FPB(a, b, c) =
FPB(FPB(a, b), c). Nilai awal yang paling rapi adalah 0,
karena FPB(0, x) = x — jadi tidak perlu memperlakukan elemen pertama
secara khusus.
Data bisa langsung diproses sambil dibaca; array tidak diperlukan.
Pembahasan.
long long g = 0;
for (int i = 0; i < n; i++) {
long long x;
cin >> x;
g = fpb(g, x);
}
cout << g << "\n";Uji.
| Masukan | Keluaran | Catatan |
|---|---|---|
1 / 99 |
99 |
satu bilangan, FPB-nya dirinya sendiri |
4 / 12 18 24 30 |
6 |
|
3 / 7 13 5 |
1 |
saling prima, dan bisa berhenti lebih awal |
Baris ketiga adalah alasan petunjuk di soal: begitu FPB berjalan
mencapai 1, sisa datanya tidak perlu diperiksa lagi. Untuk
n = 200.000 pemangkasan itu sering menghemat hampir seluruh
perulangan.
Kesalahan umum.
- Memulai dengan
g = 1. Hasilnya selalu 1. - Menyimpan seluruh data ke array padahal tidak perlu — bukan kesalahan jawaban, tetapi membuang memori.
Soal 4 — Lampu berkedip
Soal. Detik berikutnya saat semua lampu berkedip bersamaan.
Cara berpikir. Jawabannya KPK dari seluruh selang
waktu, dihitung beruntun. Nilai awal 1 tepat di sini,
karena KPK(1, x) = x.
Perhatikan bahaya luapan: KPK beruntun membesar dengan cepat. Soal
ini membatasi n ≤ 15 dan nilai ≤ 1.000 justru supaya
jawabannya tetap muat.
Pembahasan.
long long k = 1;
for (int i = 0; i < n; i++) {
long long x;
cin >> x;
k = kpk(k, x);
}
cout << k << "\n";Periksa 12 18 8: KPK(1, 12) = 12, KPK(12, 18) = 36,
KPK(36, 8) = 72.
Uji.
| Masukan | Keluaran | Catatan |
|---|---|---|
1 / 5 |
5 |
satu lampu |
2 / 4 6 |
12 |
KPK(4, 6) = 12, bukan 24 |
3 / 12 18 8 |
72 |
KPK beruntun |
Baris kedua adalah kesalahan paling khas pada soal ini: mengalikan begitu saja memberi 24, padahal keduanya sudah bertemu di detik ke-12.
Kesalahan umum.
- Memulai
k = 0. KPK(0, x) tidak berarti, dan pembagian dengan FPB menghasilkan nol terus. - Memakai
intuntukk.
Tantangan — Invers modulo tanpa Fermat
Soal. Invers a modulo m
untuk m sembarang.
Cara berpikir. Teorema Fermat kecil hanya berlaku
untuk modulus prima. Untuk m sembarang, pakai Euclid
diperluas: ia memberi x dan y dengan
a·x + m·y = FPB(a, m)
Kalau FPB-nya 1, maka modulo m seluruh suku
m·y hilang, sehingga a·x ≡ 1 (mod m) — artinya
x adalah inversnya. Kalau FPB-nya bukan 1, inversnya memang
tidak ada.
x yang dikembalikan bisa negatif, jadi rapikan dengan
((x % m) + m) % m — persis soal 1 Bab 14.
Pembahasan.
long long g = fpb_luas(a, m, x, y);
if (g != 1) {
cout << "TIDAK ADA\n";
return 0;
}
long long inv = ((x % m) + m) % m;
cout << inv << "\n";
cout << a % m * inv % m << "\n";Periksa 3 10: inversnya 7, dan 3 × 7 = 21 ≡ 1 (mod 10).
Untuk 4 10 jawabannya TIDAK ADA, karena FPB(4,
10) = 2.
Uji.
| Masukan | Keluaran | Catatan |
|---|---|---|
3 7 |
5 lalu 1 |
3 × 5 = 15 ≡ 1 (mod 7) |
5 12 |
5 lalu 1 |
modulus bukan prima, tetap ada |
4 8 |
TIDAK ADA |
FPB(4, 8) = 4 ≠ 1 |
Baris kedua adalah alasan soal ini ada: 12 bukan
bilangan prima, sehingga teorema Fermat kecil tidak berlaku dan
pangkat_mod(a, m - 2, m) akan memberi jawaban yang salah.
Euclid diperluas tetap bekerja selama FPB(a, m) = 1.
Kesalahan umum.
- Memakai
pangkat_mod(a, m - 2, m)untukmbukan prima. Hasilnya angka yang kelihatan wajar tetapi salah — dan tidak ada pesan error apa pun. Selalu periksa dengan mengalikan kembali. - Lupa merapikan
xyang negatif.
Latihan bergaya OSN — pembahasan
Berkas jawabannya ada di
exercises/15/osn/solutions/.
Soal 1 — Ubin Persegi Terbesar
Cara berpikir. Terjemahkan syaratnya satu per satu.
"Tidak bersisa ke arah panjang" berarti sisi ubin membagi habis
P; "tidak bersisa ke arah lebar" berarti ia juga membagi
habis L. Jadi yang dicari adalah pembagi bersama terbesar —
kata "terbesar" pada soal langsung menunjuk FPB.
Banyaknya ubin adalah hasil kali jumlah ubin pada kedua arah:
(P / sisi) × (L / sisi).
Pembahasan.
long long sisi = fpb(p, l);
long long banyak = (p / sisi) * (l / sisi);
cout << sisi << " " << banyak << "\n";Batas atas banyaknya ubin adalah P × L = 10¹⁸, yang
terjadi ketika FPB-nya
- Itu muat di
long longtetapi tidak diint.
Uji.
| Masukan | Keluaran | Yang diperiksa |
|---|---|---|
12 18 |
6 6 |
contoh soal |
7 5 |
1 35 |
FPB 1, ubin paling banyak |
1000000000 1000000000 |
1000000000 1 |
lantai persegi, satu ubin |
Kesalahan umum.
- Memakai bilangan terkecil sebagai sisi ubin. Untuk
12 18itu memberi 12, yang tidak membagi habis 18. - Mencari FPB dengan mencoba semua pembagi dari 1 sampai
P. UntukP= 10⁹ itu 10⁹ putaran per kasus. - Menghitung banyak ubin sebagai
p * l / (sisi * sisi). Hasilnya sama, tetapip * lmeluap lebih dulu untuk nilai besar.
Soal 2 — Kelipatan Bersama
Cara berpikir. Sebuah bilangan habis dibagi
X sekaligus habis dibagi Y tepat ketika ia
habis dibagi KPK(X, Y). Jadi soalnya menjadi:
berapa kelipatan L yang berada di antara A dan
B?
Banyaknya kelipatan L dari 1 sampai n
adalah ⌊n / L⌋. Untuk rentang [A, B],
jawabannya ⌊B / L⌋ − ⌊(A − 1) / L⌋. Bentuk
A − 1 inilah yang membuat batas kirinya ikut terhitung.
Pembahasan.
long long kpk = x / fpb(x, y) * y; // bagi dulu, baru kali
long long banyak = b / kpk - (a - 1) / kpk;Urutan x / fpb * y penting. Dengan
x * y / fpb, perkalian 10⁹ × 10⁹ = 10¹⁸ dilakukan lebih
dulu — untuk batasan ini masih muat, tetapi kebiasaan itu akan meluap
begitu batasannya sedikit dinaikkan.
Uji.
| Masukan | Keluaran | Yang diperiksa |
|---|---|---|
1 100 4 6 |
8 |
contoh soal |
10 10 5 2 |
1 |
rentang satu bilangan |
5 9 3 3 |
2 |
X sama dengan Y |
1 1000000000000000000 999999937 1000000000 |
1 |
KPK hampir sebesar B |
Kesalahan umum.
- Memeriksa setiap bilangan dari
AsampaiBdengan perulangan. - Memakai
b / kpk - a / kpk, yang melewatkanAsendiri kalauAkebetulan kelipatan KPK. - Mengalikan
XdenganYsebagai KPK tanpa membagi FPB-nya. Untuk5 9 3 3cara itu menjawab 1, bukan 2.
Soal 3 — Pembagi Bersama Selisih
Cara berpikir. Kalimat "bersisa sama" terlihat sulit
karena sisanya sendiri tidak diketahui. Ubah sudut pandangnya: kalau
Aᵢ dan A_j bersisa sama terhadap
d, maka selisihnya habis dibagi d. Sebaliknya
juga benar. Jadi syaratnya setara dengan "d membagi habis
setiap selisih".
Selisih yang perlu diperiksa cukup terhadap satu bilangan patokan,
misalnya A₁. Kalau d membagi habis
A₂ − A₁ dan A₃ − A₁, maka ia juga membagi
habis A₃ − A₂, karena selisih itu adalah selisih dari dua
selisih tadi.
d terbesar yang membagi habis semuanya adalah FPB
seluruh selisih.
Nilai awal 0. FPB dihitung bertahap, dan nilai awal
yang benar adalah 0 karena fpb(0, x) = x. Nilai awal itu
sekaligus menangani kasus khusus: kalau semua bilangannya sama, semua
selisihnya 0 dan hasilnya tetap 0 — persis yang diminta soal.
Pembahasan.
long long hasil = 0;
for (int i = 1; i < n; i++) {
long long selisih = a[i] - a[0];
if (selisih < 0) selisih = -selisih;
hasil = fpb(hasil, selisih);
}
cout << hasil << "\n";Uji.
| Masukan | Keluaran | Yang diperiksa |
|---|---|---|
3 / 5 11 17 |
6 |
contoh soal |
3 / 4 4 4 |
0 |
semua sama |
2 / 1000000000000000000 1 |
999999999999999999 |
selisih terbesar |
Kesalahan umum.
- Memulai
hasildari 1, sehingga jawabannya selalu 1. - Mengambil FPB dari bilangannya sendiri, bukan dari selisihnya. Untuk
5 11 17cara itu menjawab 1. - Lupa nilai mutlak kalau patokannya bukan bilangan terkecil.
Kalau masih tersendat
- Bingung memilih FPB atau KPK → Bab 15, bagian Pola soal. "Kapan berulang bersamaan" adalah KPK; "berapa kelompok terbanyak" adalah FPB.
- Jawaban KPK menjadi negatif atau aneh → Bab 15, peringatan tentang urutan operasi.
- Perlu invers modulo tetapi modulusnya bukan prima → Bab 15, bagian Algoritma Euclid diperluas.
Algoritma Euclid hanya empat baris dan dipakai di banyak soal; tulis ulang dari ingatan sampai tidak perlu berpikir lagi.