Bài 9.2: Số chính phương và Số hoàn hảo trong C++

Thế giới của các con số luôn ẩn chứa những điều kỳ diệu và thú vị. Trong lập trình, chúng ta thường xuyên làm việc với các loại số có tính chất đặc biệt. Hôm nay, chúng ta sẽ cùng nhau khám phá hai khái niệm quen thuộc trong toán học và xem cách chúng ta có thể kiểm tra chúng bằng C++: đó là số chính phươngsố hoàn hảo.

Việc hiểu và có thể code để kiểm tra các tính chất số học cơ bản này không chỉ giúp bạn củng cố kiến thức về toán, mà còn rèn luyện kỹ năng tư duy thuật toán và làm việc với các phép toán trong C++.

Khám phá Số Chính Phương (Perfect Square)

Đầu tiên, chúng ta hãy nói về số chính phương. Một số chính phương là một số nguyên không âm mà căn bậc hai của nó cũng là một số nguyên. Nói cách khác, nó là bình phương của một số nguyên nào đó.

Ví dụ:

  • 0 là số chính phương vì $0 = 0^2$
  • 1 là số chính phương vì $1 = 1^2$
  • 4 là số chính phương vì $4 = 2^2$
  • 9 là số chính phương vì $9 = 3^2$
  • 16 là số chính phương vì $16 = 4^2$
  • ...
  • Các số như 2, 3, 5, 6, 7, 8, 10... không phải là số chính phương.

Làm thế nào để kiểm tra một số nguyên n có phải là số chính phương trong C++? Cách đơn giản nhất là tính căn bậc hai của nó. Nếu kết quả là một số nguyên, thì n là số chính phương.

Trong C++, chúng ta có thể sử dụng hàm sqrt() từ thư viện <cmath> để tính căn bậc hai. Hàm này trả về một giá trị kiểu double. Để kiểm tra xem kết quả có phải là số nguyên hay không, chúng ta có thể làm tròn kết quả về số nguyên gần nhất và kiểm tra xem bình phương của số nguyên đó có bằng số ban đầu hay không.

Tuy nhiên, việc làm việc với số thực (double) có thể gặp vấn đề về độ chính xác nhỏ. Một cách tiếp cận khác an toàn hơn là tính căn bậc hai, sau đó ép kiểu kết quả về số nguyên, và kiểm tra xem bình phương của số nguyên sau khi ép kiểu có bằng số ban đầu hay không.

Hãy cùng xem code C++:

#include <iostream>
#include <cmath>

bool laSoChinhPhuong(int n) {
    if (n < 0) {
        return false;
    }
    if (n == 0 || n == 1) {
        return true;
    }

    double r = sqrt(n);
    int ic = static_cast<int>(r);
    return (ic * ic == n);
}

int main() {
    cout << "Kiem tra mot vai so co phai la so chinh phuong:\n";
    cout << boolalpha;

    cout << "0 la so chinh phuong? " << laSoChinhPhuong(0) << endl;
    cout << "1 la so chinh phuong? " << laSoChinhPhuong(1) << endl;
    cout << "4 la so chinh phuong? " << laSoChinhPhuong(4) << endl;
    cout << "9 la so chinh phuong? " << laSoChinhPhuong(9) << endl;
    cout << "7 la so chinh phuong? " << laSoChinhPhuong(7) << endl;
    cout << "16 la so chinh phuong? " << laSoChinhPhuong(16) << endl;
    cout << "25 la so chinh phuong? " << laSoChinhPhuong(25) << endl;
    cout << "26 la so chinh phuong? " << laSoChinhPhuong(26) << endl;
    cout << "-4 la so chinh phuong? " << laSoChinhPhuong(-4) << endl;

    return 0;
}

Output:

Kiem tra mot vai so co phai la so chinh phuong:
0 la so chinh phuong? true
1 la so chinh phuong? true
4 la so chinh phuong? true
9 la so chinh phuong? true
7 la so chinh phuong? false
16 la so chinh phuong? true
25 la so chinh phuong? true
26 la so chinh phuong? false
-4 la so chinh phuong? false

Giải thích code:

  1. Chúng ta sử dụng <iostream> để nhập xuất và <cmath> để dùng hàm sqrt().
  2. Hàm laSoChinhPhuong(int n) nhận vào một số nguyên n.
  3. Chúng ta xử lý các trường hợp đặc biệt: số âm (không phải SNT), 0 và 1 (là SNT).
  4. sqrt(n) tính căn bậc hai của n và trả về kiểu double.
  5. static_cast<int>(r) ép kiểu giá trị double của căn bậc hai về kiểu int. Điều này sẽ bỏ đi phần thập phân.
  6. Logic chính nằm ở ic * ic == n. Chúng ta kiểm tra xem bình phương của phần nguyên căn bậc hai có bằng số ban đầu n hay không. Nếu có, n là số chính phương.
  7. Trong main(), chúng ta gọi hàm laSoChinhPhuong với nhiều giá trị khác nhau để kiểm tra. boolalpha giúp in kết quả true hoặc false dễ đọc hơn.

Bạn cũng có thể viết một chương trình nhỏ để người dùng nhập số từ bàn phím và kiểm tra:

#include <iostream>
#include <cmath>

bool laSoChinhPhuong(int n) {
    if (n < 0) return false;
    if (n == 0 || n == 1) return true;
    double r = sqrt(n);
    int ic = static_cast<int>(r);
    return (ic * ic == n);
}

int main() {
    int n;
    cout << "Nhap mot so nguyen de kiem tra so chinh phuong: ";
    cin >> n;

    if (laSoChinhPhuong(n)) {
        cout << n << " la so chinh phuong." << endl;
    } else {
        cout << n << " khong phai la so chinh phuong." << endl;
    }

    return 0;
}

Output ví dụ 1:

Nhap mot so nguyen de kiem tra so chinh phuong: 16
16 la so chinh phuong.

Output ví dụ 2:

Nhap mot so nguyen de kiem tra so chinh phuong: 7
7 khong phai la so chinh phuong.

Khám phá Số Hoàn Hảo (Perfect Number)

Tiếp theo, chúng ta sẽ tìm hiểu về số hoàn hảo. Số hoàn hảo là một số nguyên dương mà tổng các ước số dương thật sự (tức là các ước số dương nhỏ hơn nó) bằng chính số đó.

Ví dụ:

  • Số 6: Các ước số dương thật sự là 1, 2, 3. Tổng: 1 + 2 + 3 = 6. Vậy 6 là số hoàn hảo.
  • Số 28: Các ước số dương thật sự là 1, 2, 4, 7, 14. Tổng: 1 + 2 + 4 + 7 + 14 = 28. Vậy 28 là số hoàn hảo.
  • Số 12: Các ước số dương thật sự là 1, 2, 3, 4, 6. Tổng: 1 + 2 + 3 + 4 + 6 = 16. 16 khác 12, vậy 12 không phải số hoàn hảo.

Các số hoàn hảo tiếp theo là 496, 8128, 33550336,... Chúng khá hiếm!

Để kiểm tra một số n có phải là số hoàn hảo, chúng ta cần thực hiện các bước sau:

  1. Tìm tất cả các ước số dương của n, ngoại trừ chính n.
  2. Tính tổng các ước số đó.
  3. So sánh tổng với n. Nếu bằng nhau, n là số hoàn hảo.

Làm thế nào để tìm các ước số dương thật sự? Chúng ta có thể duyệt qua các số từ 1 đến n/2. Nếu một số i chia hết cho n (tức là n % i == 0), thì i là một ước số dương thật sự của n (vì nếu i > n/2i là ước số, thì i phải là n, nhưng chúng ta loại trừ n).

Dưới đây là hàm C++ để kiểm tra số hoàn hảo:

#include <iostream>

bool laSoHoanHao(int n) {
    if (n <= 1) {
        return false;
    }

    int t = 0;
    for (int i = 1; i <= n / 2; ++i) {
        if (n % i == 0) {
            t += i;
        }
    }
    return (t == n);
}

int main() {
    cout << "Kiem tra mot vai so co phai la so hoan hao:\n";
    cout << boolalpha;

    cout << "6 la so hoan hao? " << laSoHoanHao(6) << endl;
    cout << "28 la so hoan hao? " << laSoHoanHao(28) << endl;
    cout << "12 la so hoan hao? " << laSoHoanHao(12) << endl;
    cout << "10 la so hoan hao? " << laSoHoanHao(10) << endl;
    cout << "496 la so hoan hao? " << laSoHoanHao(496) << endl;
    cout << "1 la so hoan hao? " << laSoHoanHao(1) << endl;
    cout << "-5 la so hoan hao? " << laSoHoanHao(-5) << endl;

    return 0;
}

Output:

Kiem tra mot vai so co phai la so hoan hao:
6 la so hoan hao? true
28 la so hoan hao? true
12 la so hoan hao? false
10 la so hoan hao? false
496 la so hoan hao? true
1 la so hoan hao? false
-5 la so hoan hao? false

Giải thích code:

  1. Chúng ta sử dụng <iostream> để nhập xuất.
  2. Hàm laSoHoanHao(int n) nhận vào một số nguyên n.
  3. Số hoàn hảo phải là số nguyên dương và lớn hơn 1, nên chúng ta loại bỏ các trường hợp n <= 1.
  4. Biến t được khởi tạo bằng 0 để tích lũy tổng các ước số.
  5. Vòng lặp for duyệt từ i = 1 đến n / 2.
  6. Bên trong vòng lặp, n % i == 0 kiểm tra xem i có phải là ước của n hay không (phép chia lấy dư bằng 0).
  7. Nếu i là ước, nó được cộng vào t.
  8. Cuối cùng, hàm trả về true nếu t bằng n, ngược lại trả về false.
  9. Trong main(), chúng ta kiểm tra vài số ví dụ.

Vì số hoàn hảo khá hiếm, bạn có thể muốn tìm các số hoàn hảo trong một phạm vi nhất định. Chương trình sau sẽ tìm và in ra tất cả các số hoàn hảo nhỏ hơn hoặc bằng một giới hạn mà người dùng nhập vào:

#include <iostream>

bool laSoHoanHao(int n) {
    if (n <= 1) return false;
    int t = 0;
    for (int i = 1; i <= n / 2; ++i) {
        if (n % i == 0) {
            t += i;
        }
    }
    return (t == n);
}

int main() {
    int gh;
    cout << "Nhap gioi han tren de tim so hoan hao: ";
    cin >> gh;

    cout << "Cac so hoan hao trong pham vi tu 2 den " << gh << " la:\n";

    for (int i = 2; i <= gh; ++i) {
        if (laSoHoanHao(i)) {
            cout << i << endl;
        }
    }

    return 0;
}

Output ví dụ:

Nhap gioi han tren de tim so hoan hao: 1000
Cac so hoan hao trong pham vi tu 2 den 1000 la:
6
28
496

Giải thích code:

  1. Chương trình yêu cầu người dùng nhập một giới hạn trên (gh).
  2. Vòng lặp for duyệt qua tất cả các số nguyên từ 2 đến gh. (Chúng ta bắt đầu từ 2 vì các số nhỏ hơn không thể là số hoàn hảo).
  3. Với mỗi số i, chúng ta gọi hàm laSoHoanHao(i) để kiểm tra.
  4. Nếu hàm trả về true, tức là i là số hoàn hảo, chúng ta in số đó ra màn hình.

Qua bài này, chúng ta đã cùng tìm hiểu về định nghĩa của số chính phươngsố hoàn hảo, cũng như cách triển khai các hàm kiểm tra chúng trong C++ bằng cách sử dụng các phép toán và vòng lặp cơ bản. Đây là những ví dụ tuyệt vời cho thấy cách chúng ta có thể áp dụng kiến thức toán học vào giải quyết các bài toán lập trình.

Comments

There are no comments at the moment.