Header <numeric> menyimpan perhitungan-perhitungan kecil yang muncul terus di kode nyata. Sebagian isinya bekerja pada rentang iterator — std::iota, std::inner_product, std::partial_sum, dan std::adjacent_difference — dan sudah kita bahas tuntas pada Chapter Algoritma Numerik <numeric> di Section Iterators dan Algorithms. Chapter ini melengkapi sisi lain dari header yang sama: fungsi yang menerima angka, bukan rentang — std::gcd dan std::lcm untuk bilangan bulat, serta std::midpoint (C++20) untuk mencari titik tengah dengan aman. Ketiganya pendek, mudah diingat, dan menyelesaikan tugas yang selama ini kita tulis manual — kadang dengan hasil yang keliru. Di akhir chapter, kita merangkum seluruh isi <numeric> dalam satu tabel agar mudah dicari kembali.
gcd dan lcm dalam Satu Panggilan #
std::gcd(a, b) mengembalikan greatest common divisor (pembagi bersama terbesar) — bilangan bulat terbesar yang membagi habis a dan b. std::lcm(a, b) mengembalikan least common multiple (kelipatan bersama terkecil) — bilangan terkecil yang habis dibagi oleh keduanya. Keduanya hidup di header <numeric>, menerima dua argumen bertipe bilangan bulat, dan memakai algoritma Euclid yang cepat di balik layar.
#include <iostream> // std::cout
#include <iterator> // std::begin, std::end
#include <numeric> // std::gcd, std::lcm, std::midpoint
int main() {
// gcd: pembagi bersama terbesar
std::cout << "gcd(48, 18) : " << std::gcd(48, 18) << "\n";
std::cout << "gcd(17, 5) : " << std::gcd(17, 5) << "\n";
std::cout << "gcd(-12, 18) : " << std::gcd(-12, 18) << "\n";
std::cout << "gcd(0, 7) : " << std::gcd(0, 7) << "\n";
// lcm: kelipatan bersama terkecil; rantai untuk lebih dari dua angka
std::cout << "lcm(4, 6) : " << std::lcm(4, 6) << "\n";
std::cout << "lcm(3, 7, 5) : " << std::lcm(std::lcm(3, 7), 5) << "\n";
// midpoint: titik tengah tanpa bahaya luapan
int a = 2147483646; // dekat batas atas int (2147483647)
int b = 2147483647;
std::cout << "midpoint(a, b) : " << std::midpoint(a, b) << "\n";
std::cout << "midpoint(2, 5) : " << std::midpoint(2, 5) << "\n";
std::cout << "midpoint(5, 2) : " << std::midpoint(5, 2) << "\n";
// midpoint juga bekerja pada pointer: indeks tengah array
int data[] = {10, 20, 30, 40, 50, 60};
int* tengah = std::midpoint(std::begin(data), std::end(data));
std::cout << "elemen tengah : " << *tengah << "\n";
return 0;
}Output yang Dihasilkan:
gcd(48, 18) : 6
gcd(17, 5) : 1
gcd(-12, 18) : 6
gcd(0, 7) : 7
lcm(4, 6) : 12
lcm(3, 7, 5) : 105
midpoint(a, b) : 2147483646
midpoint(2, 5) : 3
midpoint(5, 2) : 4
elemen tengah : 40Bedah baris demi baris:
- Baris 7-10 (gcd):
gcd(48, 18)menghasilkan6karena 6 adalah bilangan terbesar yang membagi habis keduanya. Dua bilangan yang saling prima seperti 17 dan 5 menghasilkan1. Untuk argumen negatif,std::gcdbekerja pada nilai mutlaknya sehinggagcd(-12, 18)tetap6, dangcd(0, 7)menghasilkan7karena setiap bilangan membagi 0. - Baris 13-14 (lcm):
lcm(4, 6)menghasilkan12. Untuk lebih dari dua angka, fungsi ini dirantai:std::lcm(std::lcm(3, 7), 5)menghitung kelipatan bersama terkecil dari tiga bilangan sekaligus —105. - Baris 17-21 (midpoint pada bilangan):
std::midpoint(a, b)mengembalikan titik tengahadanb. Untuk bilangan bulat, bila hasilnya pecahan, pembulatannya mengarah ke argumen pertama:midpoint(2, 5)memberi3, sedangkanmidpoint(5, 2)memberi4. Baris 19 adalah bintangnya — dua nilai yang penuhnya dekat batas atasinttetap menghasilkan titik tengah yang benar; kita lihat mengapa pada bagian berikutnya. - Baris 24-26 (midpoint pada pointer): midpoint juga menerima dua pointer ke array yang sama dan mengembalikan pointer tengahnya.
*tengahmencetak40, elemen tengah enam elemen data — berguna untuk membelah array pada pencarian biner buatan sendiri.
Mengapa midpoint Ada #
Cara lama mencari titik tengah dua bilangan adalah (a + b) / 2. Rumus ini benar untuk nilai kecil, tetapi menyimpan jebakan yang halus: penjumlahan a + b dihitung dulu sebelum dibagi, dan bila kedua nilai besar, penjumlahan itu bisa melewati batas atas int. Melewati batas tipe bilangan bulat bertanda adalah undefined behavior — hasil yang muncul tidak dijamin apa pun, dan compiler berhak menganggap kejadian itu mustahil terjadi.
int a = 2147483647; // batas atas int
int b = 2147483646;
int tengah = (a + b) / 2; // a + b meluap -> undefined behavior
int aman = std::midpoint(a, b); // 2147483646, tanpa luapan
std::midpoint menghitung titik tengah tanpa pernah membentuk nilai a + b yang bisa meluap, sehingga selalu aman untuk seluruh rentang tipenya. Itulah alasan baris 19 pada program kita menghasilkan 2147483646 — tepat di wilayah yang membuat rumus (a + b) / 2 gagal. Kebiasaan yang baik: setiap kali melihat kebutuhan “titik tengah dua bilangan”, pakai std::midpoint, bukan penjumlahan manual.
Praktik: Pecahan dengan gcd dan lcm #
Pasangan gcd-lcm punya use case klasik yang menghubungkan keduanya: aritmetika pecahan. Gcd menyederhanakan pecahan, dan lcm mencari penyebut bersama untuk penjumlahan. Program berikut memakai keduanya dalam satu alur.
#include <iostream> // std::cout
#include <numeric> // std::gcd, std::lcm
// sederhanakan pecahan: bagi pembilang dan penyebut dengan gcd-nya
void sederhanakan(int& pembilang, int& penyebut) {
int g = std::gcd(pembilang, penyebut);
if (g != 0) {
pembilang /= g;
penyebut /= g;
}
}
int main() {
// satu pecahan disederhanakan
int p = 48, q = 18;
sederhanakan(p, q);
std::cout << "48/18 = " << p << "/" << q << "\n";
// penjumlahan pecahan: penyebut bersama dari lcm
int p1 = 3, q1 = 4; // 3/4
int p2 = 5, q2 = 6; // 5/6
int k = std::lcm(q1, q2);
int hasilP = p1 * (k / q1) + p2 * (k / q2);
int hasilQ = k;
sederhanakan(hasilP, hasilQ);
std::cout << "3/4 + 5/6 = " << hasilP << "/" << hasilQ << "\n";
return 0;
}Output yang Dihasilkan:
48/18 = 8/3
3/4 + 5/6 = 19/12- Baris 5-11 (
sederhanakan): fungsi menerima kedua bagian pecahan lewat referensi, membagi keduanya dengan gcd-nya, dan memeriksag != 0agar pecahan nol tidak dibagi nol.48/18dengan gcd 6 menjadi8/3. - Baris 22-24 (penjumlahan): penyebut bersama diambil dari
std::lcm(q1, q2)—lcm(4, 6)memberi12— lalu tiap pembilang dikalikan faktor penguatnya:3 * (12/4) + 5 * (12/6) = 9 + 10 = 19. - Baris 25-26 (hasil akhir): hasil penjumlahan
19/12disederhanakan sekali lagi; karena gcd(19, 12) = 1, pecahan itu sudah paling ringkas.
Ringkasan Header <numeric> #
Sekarang isi header <numeric> kita lengkapi. Tabel berikut memetakan setiap fungsi beserta pola kerjanya, agar chapter ini bisa dijadikan catatan pintas:
| Fungsi | Bekerja pada | Keterangan |
|---|---|---|
std::accumulate, std::reduce | rentang iterator | agregasi seluruh koleksi ke satu nilai |
std::iota | rentang iterator | mengisi deret bertambah satu |
std::inner_product | dua rentang | jumlah hasil kali pasangan elemen |
std::partial_sum | rentang iterator | jumlah berjalan (prefix sum) |
std::adjacent_difference | rentang iterator | selisih elemen bersebelahan |
std::gcd | dua bilangan bulat | pembagi bersama terbesar |
std::lcm | dua bilangan bulat | kelipatan bersama terkecil |
std::midpoint | dua angka / dua pointer | titik tengah, aman dari luapan |
Lima baris pertama telah dibahas dengan contoh penuh pada Chapter Algoritma Numerik <numeric> di Section Iterators dan Algorithms, sedangkan tiga baris terakhir adalah milik chapter ini. Pembagian tugasnya mudah diingat: algoritma rentang mengolah koleksi, fungsi titik menghitung jawaban tunggal dari beberapa angka.
Dengan <numeric> tuntas, kita kembali ke header yang lebih dikenal: <utility> dan <algorithm> menyimpan fungsi-fungsi kecil serbaguna — std::swap, std::exchange, std::min/max/clamp — yang akan kita bahas pada chapter berikutnya.
Rujukan resmi: cppreference — header <numeric> , cppreference — std::gcd dan std::lcm , cppreference — std::midpoint .