Bài 5.4: Kỹ thuật tách chữ số bằng vòng lặp trong C++

Chào mừng các bạn quay trở lại với series học lập trình C++ cùng FullhouseDev!

Trong thế giới lập trình, chúng ta thường xuyên làm việc với các số nguyên. Đôi khi, chúng ta cần xử lý số nguyên đó như một toàn bộ, ví dụ như cộng, trừ, nhân, chia. Nhưng cũng có những lúc, bài toán đòi hỏi chúng ta phải "nhìn sâu" vào cấu trúc của số đó, làm việc với từng chữ số tạo nên nó.

Lấy ví dụ, bạn muốn tính tổng các chữ số của một số (ví dụ: 123 -> 1+2+3 = 6), kiểm tra xem một số có phải là số Palindrome (đọc xuôi ngược như nhau) hay không, đếm số lượng chữ số, hoặc thậm chí là đảo ngược một số. Tất cả những bài toán này đều yêu cầu chúng ta có khả năng "tách" rời các chữ số ra khỏi số nguyên ban đầu.

May mắn thay, C++ cung cấp cho chúng ta những công cụ cực kỳ mạnh mẽ và linh hoạt để làm điều này, và bí mật nằm ở việc kết hợp vòng lặp với hai phép toán số học cơ bản: phép chia lấy phần dư (%)phép chia lấy phần nguyên (/).

Hãy cùng nhau khám phá kỹ thuật quan trọng này!

Nền Tảng: Hai Phép Toán "Ma Thuật"

Để tách chữ số từ một số nguyên, chúng ta sẽ tận dụng hệ thập phân (cơ số 10) mà chúng ta sử dụng hàng ngày. Mọi số nguyên đều có thể được biểu diễn bằng cách sử dụng các lũy thừa của 10.

  • Phép toán 1: Lấy chữ số cuối cùng

    • Sử dụng toán tử modulo % với 10 (% 10).
    • Kết quả của số % 10 luôn là phần dư khi chia số đó cho 10. Trong hệ thập phân, phần dư này chính là chữ số hàng đơn vị của số đó.
    • Ví dụ:
      • 123 % 10 = 3
      • 45 % 10 = 5
      • 7 % 10 = 7
  • Phép toán 2: Loại bỏ chữ số cuối cùng

    • Sử dụng phép chia lấy phần nguyên / với 10 (/ 10).
    • Khi chúng ta chia một số nguyên cho 10 và chỉ lấy phần nguyên, chúng ta thực chất đang loại bỏ chữ số hàng đơn vị và dịch chuyển tất cả các chữ số còn lại sang phải một vị trí (tương đương với chia cho 10).
    • Ví dụ:
      • 123 / 10 = 12 (phần nguyên)
      • 45 / 10 = 4 (phần nguyên)
      • 7 / 10 = 0 (phần nguyên)

Hãy nhìn kỹ: Với số 123, 123 % 10 cho ta 3 (chữ số cuối cùng). Sau đó, 123 / 10 cho ta 12. Nếu lặp lại quá trình này với 12, 12 % 10 cho ta 2, và 12 / 10 cho ta 1. Lặp lại với 1, 1 % 10 cho ta 1, và 1 / 10 cho ta 0.

Thấy điều kỳ diệu chưa? Chúng ta đã tách được lần lượt các chữ số 3, 2, 1 từ số 123 bằng cách lặp đi lặp lại hai phép toán đơn giản này cho đến khi số ban đầu trở thành 0.

Kết Hợp Với Vòng Lặp while

Quá trình lặp đi lặp lại này chính là lúc vòng lặp phát huy sức mạnh của nó. Vòng lặp while đặc biệt phù hợp vì chúng ta cần tiếp tục quá trình tách chữ số trong khi số ban đầu vẫn còn lớn hơn 0.

Cấu trúc cơ bản sẽ trông như thế này:

while (so_nguyen > 0) {
    // Lấy chữ số cuối cùng: chu_so_cuoi = so_nguyen % 10;
    // Xử lý chữ số chu_so_cuoi (in ra, cộng vào tổng, đếm, v.v.)
    // Loại bỏ chữ số cuối cùng: so_nguyen = so_nguyen / 10;
}

Mỗi lần lặp, chúng ta lấy được một chữ số (bắt đầu từ chữ số hàng đơn vị, rồi đến hàng chục, hàng trăm,...). Quá trình dừng lại khi số nguyên trở thành 0, tức là tất cả các chữ số đã được xử lý.

Hãy cùng xem một vài ví dụ cụ thể để thấy rõ kỹ thuật này hoạt động như thế nào trong thực tế.

Ví Dụ Minh Họa

Chúng ta sẽ sử dụng thư viện iostream để nhập/xuất dữ liệu.

#include <iostream>
// Có thể thêm các thư viện khác nếu cần cho các ví dụ sau
// #include <cmath> // Cho ví dụ đảo ngược số (nếu cần abs)

int main() {

    int number;
    cout << "Nhap mot so nguyen duong: ";
    cin >> number;

    // Lưu lại số ban đầu nếu cần dùng sau này (ví dụ: in ra số ban đầu)
    int originalNumber = number;

    // Đảm bảo chỉ xử lý số dương, nếu nhập số âm có thể chuyển sang dương
    if (number < 0) {
        // Đối với tách chữ số, thường xử lý phần tuyệt đối
        // number = abs(number); // Cần include <cmath>
        cout << "Chuong trinh chu yeu lam viec voi so duong. Vui long nhap so duong." << endl;
        // Hoặc xử lý số âm bằng cách lấy trị tuyệt đối và ghi nhớ dấu
        // bool isNegative = true;
        // number = -number;
        return 1; // Kết thúc nếu nhập sai loại số
    }

    cout << "---------------------------------" << endl;
    cout << "Bat dau tach chu so cua so: " << originalNumber << endl;

    // Ví dụ 1: In ra cac chu so tu phai sang trai
    cout << "\n**Vi du 1: In ra cac chu so tu phai sang trai**" << endl;
    int tempNumber1 = originalNumber; // Dung bien tam de khong lam mat so ban dau
    cout << "Cac chu so (tu phai sang trai): ";
    if (tempNumber1 == 0) {
        cout << "0"; // Xu ly rieng truong hop so 0
    } else {
        while (tempNumber1 > 0) {
            int digit = tempNumber1 % 10; // Lay chu so cuoi cung
            cout << digit << " ";  // In ra chu so vua tach duoc
            tempNumber1 /= 10;         // Loai bo chu so cuoi cung
        }
    }
    cout << endl;

    cout << "---------------------------------" << endl;

    // Ví dụ 2: Tinh tong cac chu so
    cout << "\n**Vi du 2: Tinh tong cac chu so**" << endl;
    int tempNumber2 = originalNumber;
    int sumOfDigits = 0;
    if (tempNumber2 == 0) {
        sumOfDigits = 0; // Tong chu so cua 0 la 0
    } else {
        while (tempNumber2 > 0) {
            int digit = tempNumber2 % 10;
            sumOfDigits += digit; // Cong chu so vao tong
            tempNumber2 /= 10;
        }
    }
    cout << "Tong cac chu so cua " << originalNumber << " la: " << sumOfDigits << endl;

    cout << "---------------------------------" << endl;

    // Ví dụ 3: Dem so luong chu so
    cout << "\n**Vi du 3: Dem so luong chu so**" << endl;
    int tempNumber3 = originalNumber;
    int digitCount = 0;
    if (tempNumber3 == 0) {
        digitCount = 1; // So 0 co 1 chu so
    } else {
        while (tempNumber3 > 0) {
            // Khong can lay chu so, chi can dem so lan lap
            digitCount++;      // Tang bien dem
            tempNumber3 /= 10; // Loai bo chu so cuoi
        }
    }
    cout << "So luong chu so cua " << originalNumber << " la: " << digitCount << endl;

    cout << "---------------------------------" << endl;

    // Ví dụ 4: Dao nguoc so
    cout << "\n**Vi du 4: Dao nguoc so**" << endl;
    int tempNumber4 = originalNumber;
    long long reversedNumber = 0; // Su dung long long de phong tran so voi so lon
    if (tempNumber4 == 0) {
        reversedNumber = 0;
    } else {
        while (tempNumber4 > 0) {
            int digit = tempNumber4 % 10;
            // Logic dao nguoc: Dich chuyen cac chu so da co sang trai (*10)
            // va them chu so moi vao hang don vi (+ digit)
            reversedNumber = reversedNumber * 10 + digit;
            tempNumber4 /= 10;
        }
    }
     // Can than voi truong hop so am neu ban chon xu ly so am ban dau
     // va truong hop tran so neu so dao nguoc qua lon so voi int
    cout << "So " << originalNumber << " sau khi dao nguoc la: " << reversedNumber << endl;


    // Lưu ý: Các ví dụ trên đều xử lý số nguyên dương.
    // Để xử lý số âm, bạn có thể lấy giá trị tuyệt đối trước khi vào vòng lặp,
    // và sau khi xử lý xong (ví dụ: đảo ngược số), áp dụng lại dấu âm nếu số ban đầu là âm.
    // Trường hợp số 0 cũng cần xử lý cẩn thận như đã minh họa trong các ví dụ.


    return 0;
}
Giải thích Code Minh Họa

Trong đoạn code trên:

  1. Chúng ta bắt đầu bằng cách khai báo một biến number để lưu trữ số nguyên do người dùng nhập vào.
  2. Biến originalNumber được dùng để lưu giữ giá trị ban đầu của số, phục vụ cho việc hiển thị kết quả sau này.
  3. Có một phần kiểm tra đơn giản để yêu cầu người dùng nhập số dương, vì kỹ thuật % 10/ 10 hoạt động trực tiếp hiệu quả nhất với số dương. Việc xử lý số 0 và số âm cần được xem xét riêng (như đã đề cập trong comment và xử lý if (tempNumber == 0)).
  4. Chúng ta sử dụng một biến tạm (ví dụ: tempNumber1, tempNumber2, v.v.) cho mỗi ví dụ. Điều này cực kỳ quan trọng! Nếu chúng ta sử dụng trực tiếp biến number hoặc originalNumber trong vòng lặp while, giá trị của nó sẽ bị thay đổi (/= 10), và chúng ta sẽ mất số ban đầu cho các phép tính hoặc hiển thị sau này.
  5. Vòng lặp while (tempNumber > 0): Đây là trái tim của kỹ thuật. Vòng lặp sẽ tiếp tục chạy miễn là biến tạm vẫn còn giá trị dương.
  6. int digit = tempNumber % 10;: Dòng này thực hiện phép toán modulo 10 để lấy chữ số cuối cùng của số tạm hiện tại. Chữ số này được lưu vào biến digit.
  7. tempNumber /= 10;: Dòng này thực hiện phép chia lấy phần nguyên cho 10, cập nhật biến tạm bằng cách loại bỏ chữ số cuối cùng vừa được xử lý.
  8. Phần "Xử lý chữ số": Đây là nơi chúng ta thực hiện các thao tác mong muốn với chữ số digit vừa tách được.
    • Ví dụ 1: Chỉ đơn giản là in chữ số đó ra màn hình. Lưu ý thứ tự in ra là từ phải sang trái (hàng đơn vị trước, hàng chục sau, v.v.).
    • Ví dụ 2: Cộng chữ số đó vào biến sumOfDigits để tính tổng.
    • Ví dụ 3: Chỉ đơn giản là tăng biến đếm digitCount mỗi khi tách được một chữ số.
    • Ví dụ 4: Xây dựng số đảo ngược bằng công thức reversedNumber = reversedNumber * 10 + digit;. Công thức này dịch chuyển các chữ số đã có của reversedNumber sang trái một hàng (nhân 10) và thêm chữ số mới vào hàng đơn vị.
  9. Các khối if (tempNumber == 0) được thêm vào để xử lý chính xác trường hợp số đầu vào là 0, vì vòng lặp while (tempNumber > 0) sẽ không chạy nếu tempNumber ban đầu là 0.

Tóm Lại

Kỹ thuật tách chữ số bằng vòng lặp while kết hợp với phép toán % 10/ 10 là một công cụ vô cùng cơ bản nhưng cực kỳ mạnh mẽ trong lập trình C++. Nó cho phép chúng ta "mổ xẻ" một số nguyên để phân tích hoặc xử lý từng thành phần (chữ số) của nó.

Comments

There are no comments at the moment.