BÀI 5. CẤU TRÚC VÒNG LẶP
Mục tiêu
Sau bài này, học viên:
- Viết đúng
while,do while,for, và chọn được vòng phù hợp với chỗ biết trước số lần hay chỗ biết điều kiện dừng. - Dùng được
breakvàcontinuecó chủ đích, không dùng chúng để che một điều kiện vòng viết sai. - Thiết kế được các mẫu lặp: cộng dồn, đếm, duyệt, kiểm tra nhập, vòng lồng.
- Tránh được lỗi lệch một (off-by-one) và vòng không bao giờ dừng.
- Ước lượng được số lần lặp của vòng lồng.
- Ở mức cao thủ: viết bất biến cho vòng lặp số học, phân tích thừa số, và một phép lặp hội tụ (Newton) có điều kiện dừng bằng sai số.
Mảng chưa là chủ đề. Mọi thuật toán ở bài này xử lý một dòng số, một dãy nhập tuần tự, hoặc một đại lượng sinh ra từ công thức. Khi cần lưu toàn bộ dãy, chờ Bài 7.
1. Ba vòng lặp
while
while (điều_kiện) {
thân;
}
Kiểm tra trước. Nếu điều kiện sai ngay từ đầu, thân không chạy lần nào. Dùng khi có thể không cần làm gì, ví dụ đọc đến khi gặp số 0, mà số đầu đã là 0.
do while
do {
thân;
} while (điều_kiện);
Làm thân ít nhất một lần, rồi mới hỏi có lặp nữa không. Hợp với “nhập cho đến khi hợp lệ”: lần nhập đầu là bắt buộc. Dấu chấm phẩy sau while (...) thuộc về cú pháp này. Quên nó là lỗi biên dịch.
for
for (khởi_tạo; điều_kiện; bước) {
thân;
}
Tương đương:
khởi_tạo;
while (điều_kiện) {
thân;
bước;
}
for hợp khi có biến đếm và bước đi rõ. Ba ngăn có thể trống, nhưng đừng viết for(;;) khi một while có tên điều kiện đọc dễ hơn. Biến đếm khai báo ngay trong for, từ C99:
for (int i = 0; i < n; i++) {
}
Biến i chỉ sống trong vòng. Đừng dùng nó sau vòng nếu bạn khai báo kiểu này.
Ba lưu đồ đặt cạnh nhau. Điểm khác nhau duy nhất là hỏi điều kiện lúc nào.
Nếu điều kiện sai từ đầu, while và for không vào thân. do while vẫn vào một lần.
Chọn vòng nào
| Tình huống | Vòng |
|---|---|
| Biết số lần, có biến chạy đều | for |
| Có thể 0 lần, điều kiện là một mệnh đề dữ liệu | while |
| Bắt buộc làm một lần rồi mới biết dừng | do while |
Ba vòng cùng sức mạnh. Chọn vòng nào làm người đọc thấy điều kiện dừng trong ba giây.
2. break và continue
break thoát một vòng gần nhất, không thoát hai vòng lồng nhau. continue bỏ phần còn lại của thân và chuyển sang lần lặp kế (vẫn chạy ngăn bước của for).
Dùng break khi đã đạt mục tiêu giữa chừng: tìm thấy ước, tìm thấy số cần tìm. Nếu mỗi vòng đều break hoặc continue theo một logic rối, hãy viết lại điều kiện while.
break trong switch không thoát vòng lặp bên ngoài. Nếu switch nằm trong for và bạn muốn thoát for, hãy dùng một biến cờ hoặc cấu trúc lại. Đây là bẫy cố định.
Vòng vô hạn có chủ đích viết for (;;) hoặc while (1), rồi thoát bằng break khi một sự kiện xảy ra, ví dụ người dùng chọn thoát menu. Vòng vô hạn do quên tăng biến đếm là lỗi. Hai thứ cùng cú pháp lặp mãi, khác nhau ở chỗ có lối thoát đã được thiết kế hay không.
Muốn thoát hai vòng lồng, một break không đủ. Dùng một biến cờ:
thoat ← sai
LẶP hàng
LẶP cột
NẾU gặp mục tiêu THÌ thoat ← đúng, BREAK khỏi vòng cột
NẾU thoat THÌ BREAK khỏi vòng hàng
3. Bất biến và lệch một
Trước mỗi lần kiểm tra điều kiện, một mệnh đề gọi là bất biến phải đúng. Khi vòng dừng, bất biến cộng điều kiện sai suy ra kết quả.
Lệch một thường đến từ bốn chỗ:
<thay vì<=- bắt đầu từ 0 hay từ 1
- bước cập nhật đặt sai chỗ (
i++trước khi dùngi) - điều kiện dừng dùng giá trị cũ
Cách kiểm: giả lập n = 0, n = 1, n = 2 trên giấy. Nếu ba ca này đúng, phần lớn lệch một đã chết.
Vòng vô hạn khi đại lượng trong điều kiện không tiến tới chỗ làm điều kiện sai. Mỗi vòng phải có một đại lượng tiến: tăng đếm, giảm n, rút ngắn đoạn, hoặc đọc input tiến tới EOF.
4. Vòng lồng nhau
Vòng trong chạy hết đời sống của nó cho mỗi lần của vòng ngoài. Nếu vòng ngoài n lần và vòng trong n lần, số thân trong cùng là n * n, bậc O(n²).
In hình, bảng cửu chương, duyệt cặp là các bài vòng lồng của bài này. Đừng mở ba vòng khi hai vòng đủ. Mỗi vòng thêm là nhân chi phí.
5. Ví dụ
Ví dụ 5.1 — Tổng bậc ba 1^3 + 2^3 + … + n^3
Phân tích. n >= 0. Tổng rỗng bằng 0. Dùng long long vì n = 1000 thì hạng tử là 10^9 và tổng còn lớn hơn; int có thể tràn.
Bất biến: sau khi xử lý xong i, tong bằng tổng lập phương từ 1 đến i.
Đối chứng: công thức (n*(n+1)/2)^2.
Lưu đồ.
Mã nguồn.
#include <stdio.h>
int main(void)
{
int n;
long long tong;
printf("n = ");
if (scanf("%d", &n) != 1 || n < 0) {
return 1;
}
tong = 0;
for (int i = 1; i <= n; i++) {
tong += (long long) i * i * i;
}
printf("Tong lap phuong = %lld\n", tong);
return 0;
}
Kết quả.
n = 3
Tong lap phuong = 36
1 + 8 + 27 = 36. Công thức: (6/2)^2 = 9, đợi, (3*4/2)^2 = 6^2 = 36. Đúng.
Phân tích chi tiết. Ép (long long) i trước khi nhân, nếu không i * i * i nhân trong int rồi mới nới kiểu khi cộng vào tong. Với n lớn đó là tràn có dấu. i từ 1 đến n inclusive: ca n = 1 ra 1, ca n = 0 không vào thân, ra 0.
Ví dụ 5.2 — Nhập cho đến khi hợp lệ bằng do while
Phân tích. Đọc một điểm trong đoạn [0, 10]. Nếu sai, báo và đọc lại. Vòng kết thúc chỉ khi đã có một điểm hợp lệ. Không biết trước số lần gõ sai, nhưng biết phải hỏi ít nhất một lần.
Lưu đồ.
Mã nguồn.
#include <stdio.h>
int main(void)
{
double diem;
int doc_duoc;
do {
printf("Nhap diem 0..10: ");
doc_duoc = scanf("%lf", &diem);
if (doc_duoc != 1) {
printf("Khong phai so.\n");
scanf("%*s");
} else if (diem < 0.0 || diem > 10.0) {
printf("Ngoai doan.\n");
}
} while (doc_duoc != 1 || diem < 0.0 || diem > 10.0);
printf("Da nhan diem %.2f\n", diem);
return 0;
}
Kết quả giả lập người dùng gõ 12 rồi 7.5.
Nhap diem 0..10: 12
Ngoai doan.
Nhap diem 0..10: 7.5
Da nhan diem 7.50
Phân tích chi tiết. %*s bỏ một từ không phải số còn kẹt trong bộ đệm, nếu không vòng sau đọc lại đúng rác đó và lặp vô hạn. Đây là kỹ thuật nhập vững, không phải mẹo. do while đúng vì thân “hỏi người dùng” bắt buộc xảy ra. Điều kiện lặp là phủ định của điều kiện hợp lệ, đúng De Morgan ở Bài 4.
Ví dụ 5.3 — Ước số và kiểm tra nguyên tố
Phân tích. In mọi ước dương của n > 0. Song song, đếm số ước để kết luận nguyên tố: đúng hai ước thì nguyên tố. Vòng đi từ 1 đến n là O(n), đủ rõ cho bài này. Bản đến căn bậc hai đã có ở Bài 2; hãy đối chiếu số lần lặp.
Lưu đồ.
Mã nguồn.
#include <stdio.h>
int main(void)
{
int n;
int so_uoc = 0;
printf("n = ");
if (scanf("%d", &n) != 1 || n <= 0) {
return 1;
}
printf("Uoc:");
for (int d = 1; d <= n; d++) {
if (n % d == 0) {
printf(" %d", d);
so_uoc++;
}
}
printf("\n");
if (so_uoc == 2) {
printf("Nguyen to\n");
} else {
printf("Khong phai nguyen to\n");
}
return 0;
}
Kết quả.
n = 12
Uoc: 1 2 3 4 6 12
Khong phai nguyen to
n = 13
Uoc: 1 13
Nguyen to
Phân tích chi tiết. continue không cần: nhánh if đã đủ. so_uoc == 2 đúng với định nghĩa, và loại được 1 vì 1 chỉ có một ước. Với n = 12, vòng chạy 12 lần dù chỉ có 6 ước. Bản cao thủ chỉ chạy đến sqrt(n) và đếm từng cặp ước, nhưng phải xử lý ước trùng khi n là số chính phương.
Ví dụ 5.4 — Tam giác ký tự bằng vòng lồng
Phân tích. Nhập h từ 1 đến 20. In tam giác:
*
* *
* * *
Hàng i có i ngôi sao. Vòng ngoài là hàng, vòng trong là cột. Ràng buộc chiều cao để khỏi in hàng triệu dòng.
Lưu đồ.
Mã nguồn.
#include <stdio.h>
int main(void)
{
int h;
printf("Chieu cao: ");
if (scanf("%d", &h) != 1 || h < 1 || h > 20) {
return 1;
}
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= i; j++) {
printf("* ");
}
printf("\n");
}
return 0;
}
Kết quả với h = 3.
*
* *
* * *
Phân tích chi tiết. Số sao bằng 1 + 2 + … + h = h(h+1)/2. Với h = 3 là 6 sao. Nếu điều kiện vòng trong là j <= h, mọi hàng dài bằng nhau, hết là tam giác. break ở vòng trong chỉ bỏ một hàng, không kết thúc hình. Muốn dừng cả hình phải có cờ hoặc return.
Ví dụ 5.5 — UCLN, BCNN bằng Euclid
Phân tích. a, b nguyên dương. Giữ bản sao để tính BCNN = a / UCLN * b, chia trước nhân sau để giảm tràn. Dùng long long.
Lưu đồ.
Mã nguồn.
#include <stdio.h>
int main(void)
{
long long a;
long long b;
long long x;
long long y;
long long r;
printf("a b: ");
if (scanf("%lld %lld", &a, &b) != 2 || a <= 0 || b <= 0) {
return 1;
}
x = a;
y = b;
while (y != 0) {
r = x % y;
x = y;
y = r;
}
printf("UCLN = %lld\n", x);
printf("BCNN = %lld\n", a / x * b);
return 0;
}
Kết quả.
a b: 12 18
UCLN = 6
BCNN = 36
Phân tích chi tiết. a / x * b an toàn hơn a * b / x khi a * b vượt long long nhưng kết quả cuối vẫn vừa. Bất biến Euclid giữ nguyên ước chung lớn nhất. Khi y thành 0, x là UCLN. Ca (1, 1) ra UCLN 1, BCNN 1.
Ví dụ 5.6 — Căn bậc hai bằng Newton (cao thủ)
Phân tích. Với s > 0, dãy x_{k+1} = (x_k + s / x_k) / 2 hội tụ tới căn của s. Dừng khi hai lần liên tiếp chênh dưới 1e-9 theo trị tuyệt đối, hoặc sau tối đa 100 vòng để bảo đảm hữu hạn ngay cả khi sai số không đạt vì giới hạn double.
Lưu đồ.
Mã nguồn.
#include <stdio.h>
#include <math.h>
int main(void)
{
double s;
double x;
double x_moi;
int vong = 0;
printf("s = ");
if (scanf("%lf", &s) != 1 || s <= 0.0) {
return 1;
}
x = s;
do {
x_moi = (x + s / x) / 2.0;
vong++;
if (fabs(x_moi - x) < 1e-9) {
break;
}
x = x_moi;
} while (vong < 100);
printf("can = %.10f sau %d vong\n", x_moi, vong);
printf("doi chieu sqrt = %.10f\n", sqrt(s));
return 0;
}
Kết quả điển hình.
s = 2
can = 1.4142135624 sau 5 vong
doi chieu sqrt = 1.4142135624
Phân tích chi tiết. break hợp lý: hội tụ thì không cần chờ đủ 100. Trần 100 vòng là tiền điều kiện của tính hữu hạn trên máy thật, nơi hiệu hai double có thể không xuống dưới ngưỡng nếu ngưỡng nhỏ hơn độ chính xác. sqrt của thư viện chỉ để đối chứng, không phải thuật toán bạn nộp. Với s = 2, năm vòng là đủ chạm ngưỡng 1e-9: hội tụ bậc hai, số chữ số đúng tăng rất nhanh sau vài vòng đầu. Nếu máy bạn in khác ở chữ số cuối, hãy đối chiếu bằng hiệu với sqrt(s) chứ không đối chiếu bằng mắt từng chữ số thứ mười.
6. Bài tập có gợi ý
Bài 5.G1 — Bảng mã ASCII in được
In ký tự có mã từ 32 đến 126, mỗi dòng 8 ký tự.
Gợi ý. for (int ma = 32; ma <= 126; ma++). Xuống dòng khi (ma - 32) % 8 == 7. %c để in ký tự.
Bài 5.G2 — Tổng nghịch đảo
S = 1 + 1/2 + … + 1/n với n >= 1.
Gợi ý. S là double. Viết 1.0 / i, không viết 1 / i. Đối chiếu n = 1 thì S = 1.
Bài 5.G3 — Giai thừa
n! với 0 <= n <= 20, kiểu unsigned long long.
Gợi ý. 0! = 1. Vòng nhân từ 1 đến n. Từ chối n > 20 vì 21! vượt 64 bit.
Bài 5.G4 — Lũy thừa x^n
x thực, n nguyên không âm, không dùng pow.
Gợi ý. Khởi tạo kết quả 1, nhân x đúng n lần. Ca n = 0 ra 1 kể cả khi x = 0, theo quy ước tổ hợp. Bản nhanh O(log n) là bài tự làm số 12.
Bài 5.G5 — Nhập n số, tìm max min
Không lưu mảng: đọc từng số, cập nhật max min.
Gợi ý. Số đầu tiên vừa là max vừa là min. Các số sau chỉ cập nhật. Đừng khởi tạo max bằng 0, vì dãy toàn âm sẽ sai.
Bài 5.G6 — Tam giác cân rỗng
Chỉ in sao ở cạnh, khoảng trắng ở trong.
Gợi ý. Hàng đầu và hàng cuối in đầy. Hàng giữa: sao, rồi 2*i-3 khoảng trắng hoặc công thức bạn suy từ hàng 1..h, rồi sao. Giả lập h = 4 trên giấy trước.
7. Bài tập tự làm
Cơ bản
- In các số chẵn từ 0 đến n, n không âm.
- Tính tổng bình phương các số lẻ từ 1 đến n.
- In bảng cửu chương của một số từ 1 đến 9.
- Nhập một số dương, in các chữ số của nó từ phải sang trái. Ví dụ 1302 in
2 0 3 1.
Vững
- Liệt kê số nguyên tố từ 2 đến n và đếm có bao nhiêu số.
- Phân tích n thành thừa số nguyên tố. Ví dụ 84 in
2 * 2 * 3 * 7. - Vẽ tam giác cân đặc bằng
*chiều cao h, và tam giác cân rỗng. - Tính tổng
S = 1 - 1/2 + 1/3 - 1/4 + … + (-1)^(n+1)/n. - Đọc số cho đến khi gặp 0, in số lượng số dương đã gặp và tổng của chúng. Số 0 không tính.
Nâng cao
- Ước lượng π bằng Leibniz:
π/4 = 1 - 1/3 + 1/5 - 1/7 + …sau n hạng tử. In sai số so vớiacos(-1.0)từ<math.h>. Thử n = 10, 1000, 1000000 và nhận xét tốc độ hội tụ. - Số hoàn hảo nhỏ hơn hoặc bằng n: bằng tổng các ước dương thực sự (không kể chính nó). 6 = 1+2+3. 28 = 1+2+4+7+14. In các số đó. Nêu bậc của thuật toán nếu với mỗi số bạn cộng ước đến chính nó.
- Lũy thừa nhanh. Tính
x^nvới n không âm bằng bình phương liên tiếp: nếu n chẵn thì(x^(n/2))^2, nếu lẻ thì nhân thêm một x. Đếm số phép nhân và so với vòng nhân n lần. Hiện thực vòng lặp, chưa cần đệ quy (đệ quy ở Bài 6).
Siêu khó, có ứng dụng
- Mô phỏng trả góp. Dư nợ ban đầu
D, lãi suất thángr(ví dụ 0.012), trả cố địnhpmỗi tháng. Mỗi tháng: nợ mới = nợ cũ * (1+r) - p. In bảng tối đa 360 tháng: số tháng, dư nợ. Dừng khi dư nợ không còn dương. Nếu sau một tháng dư nợ tăng hoặc không giảm, in “khoan khong du tra lai” và dừng. Đây là vòngwhilecó hai lối thoát khác nhau. Kiểu tiền làdoublekèm ghi chú sai số, hoặclong longđơn vị đồng nếu bạn khử số thực. - Bộ lọc tín hiệu trung bình trượt, không mảng. Một cảm biến gửi số thực cho đến khi gặp
-1. Bạn chỉ giữ được ba biến: tổng cửa sổ, số phần tử đã nhận, và ba giá trị gần nhất bằng ba biến riênga b cxoay vòng. Khi đã có ít nhất 3 mẫu, in trung bình của 3 mẫu gần nhất. Thiết kế xoaya, b, csao cho không cần mảng. Ca kiểm thử: ít hơn 3 mẫu, đúng 3 mẫu, 6 mẫu. - Đồng hồ đếm ngược có kiểm tra. Nhập giờ, phút, giây hợp lệ. In từng nhịp giảm 1 giây theo dạng
hh:mm:sscho đến00:00:00. Không ngủ thật (sleep) trong bài nộp logic; chỉ in. Chứng minh vòng dừng. Nêu số vòng đúng bằng tổng số giây ban đầu. Viết hàm chưa được yêu cầu — hãy để toàn bộ trongmainlần này, vì Bài 6 sẽ tách hàm.
8. Câu hỏi tự kiểm
- Khi nào
do whileđúng hơnwhile? continuetrongforcó bỏ qua bướci++không?breaktrongswitchnằm trongforthoát cái gì?- Viết bất biến của tổng
1 + … + i. - Hai vòng lồng
ivàjtừ 1 đến n, thân O(1), bậc thời gian là gì? - Vì sao khởi tạo
max = 0sai với dãy toàn âm?
9. Tóm tắt và cầu nối
Vòng lặp là một khối cộng với một đại lượng tiến tới điều kiện dừng. for cho đếm, while cho 0 lần trở lên, do while cho ít nhất một lần. break cắt khi mục tiêu đã đạt. Vòng lồng nhân số lần. Bất biến và ba ca 0, 1, 2 giữ bạn khỏi lệch một.
Bài 6 nhấc các khối lặp vừa viết ra khỏi main, đặt tên, nhận tham số và trả kết quả. Cùng một Euclid sẽ thành một hàm dùng được nhiều lần, và đệ quy sẽ là một cách lặp khác: lặp bằng cách gọi chính mình.
10. Đào sâu: vòng lặp là bất biến cộng một bước tiến
Mọi vòng đúng đều trả lời được bốn câu:
- Bất biến nào đúng ngay trước điều kiện?
- Thân vòng có khôi phục bất biến không?
- Đại lượng nào tiến tới chỗ làm điều kiện sai?
- Khi dừng, bất biến còn lại có đúng là kết quả đề bài không?
Lệch một là lệch ở câu 4. Vòng vô hạn là hỏng câu 3. Kết quả sai dù có dừng là hỏng câu 1 hoặc câu 2.
for không mạnh hơn while. Nó chỉ gom khởi tạo, điều kiện và bước vào một hàng để người đọc thấy đủ ba thứ. do while khác ở chỗ thân xảy ra trước lần hỏi đầu tiên. Dùng nó khi "chưa làm thì chưa có dữ liệu để hỏi", điển hình là nhập.
break thoát một vòng. continue bỏ phần thân còn lại nhưng vẫn cập nhật bước của for. break trong switch không thoát for bên ngoài.
Vòng lồng: số lần thân trong cùng bằng tích số lần các vòng, nếu mỗi vòng chạy hết. In hình là bài nhìn được tích đó bằng mắt.
Ví dụ 5.7 — Tổng các ước, lưu đồ đủ bước tiến
Phân tích. Với n > 0, tính tổng các ước dương. Bất biến: s là tổng các ước của n nằm trong đoạn từ 1 đến d - 1. Khi d thành n + 1, s là tổng mọi ước. Đại lượng tiến là d.
Lưu đồ.
Mã nguồn.
#include <stdio.h>
int main(void)
{
int n, d, s = 0;
if (scanf("%d", &n) != 1 || n <= 0) {
return 1;
}
for (d = 1; d <= n; d++) {
if (n % d == 0) {
s += d;
}
}
printf("Tong uoc = %d\n", s);
return 0;
}
Kết quả. Đầu vào 6 in Tong uoc = 12 vì 1+2+3+6 = 12. Đầu vào 1 in 1.
Phân tích chi tiết. Ca n = 1 chỉ một vòng, ước duy nhất là 1. Nếu điều kiện là d < n thì mất chính n và tổng của 6 thành 6, sai. Đó là lệch một nhìn thấy ngay bằng ca nhỏ. Số hoàn hảo là số mà tổng ước thực sự (bỏ chính nó) bằng nó; từ chương trình này chỉ cần trừ n.
11. Bài tập mở rộng
Dễ
- In các bội của 7 từ 0 đến n, n không âm. Vẽ lưu đồ có bước nhảy 7.
- Tính
n!bằngfor,0! = 1, từ chối n âm và n > 20. - Nhập đến khi được một số dương, dùng
do while. Vẽ lưu đồ.
Vừa
- Đảo chữ số của một số không âm: 1203 thành 3021. Lưu ý số có 0 ở cuối. Vẽ lưu đồ chia 10.
- In hình chữ nhật rỗng
rhàng,ccột bằng*. Vòng lồng, hàng đầu và hàng cuối khác hàng giữa. - Đếm số chữ số của
n. Ca0phải ra 1. Viết bất biến.
Khó
- Liệt kê mọi cặp
(a, b)với1 <= a <= b <= nvàa * blà số chính phương. Nêu bậc. - Dãy Collatz: nếu chẵn thì chia 2, nếu lẻ thì
3n+1, đếm bước đến khi về 1. Vẽ lưu đồ. Trần 10000 bước để bảo đảm dừng trên máy nếu bạn gặp số chưa ai chứng minh xong trong đầu. In số bước của 6. Kết quả đúng là 8. - Tính tổng
1^2 + 2^2 + ... + n^2bằng vòng, đối chiếu công thứcn(n+1)(2n+1)/6với n = 10.
Siêu khó
- In n số nguyên tố đầu tiên, không phải các nguyên tố đến n. Phải có hai mức dừng: đã đủ n số, hoặc ước đã vượt căn. Vẽ lưu đồ hai vòng.
- Mô phỏng một cửa sổ trượt tổng: nhập trước số
k, rồi đọc từng số cho đến khi gặp 0. Chỉ giữ tổng của nhiều nhấtksố gần nhất bằng cách nhớ từng số trongkbiến nếukcố định bằng 3. Vẽ lưu đồ xoay biến. Không dùng mảng.
Dự án thật
- Bảng trả góp in ra giấy. Nhập dư nợ, lãi suất tháng, số tiền trả mỗi tháng. Mỗi vòng là một tháng: lãi = dư nợ nhân suất, dư nợ mới = dư nợ + lãi - tiền trả. In số tháng, lãi trong tháng, dư nợ. Dừng khi dư nợ hết hoặc khi tiền trả không đủ trả lãi. Trần 360 tháng. Vẽ lưu đồ có hai lối thoát khác nhau. Kiểu tiền là
long longđơn vị đồng nếu bạn đưa suất về phân số, hoặcdoublenếu bạn chấp nhận và ghi rõ sai số làm tròn.
Bộ 25 bài tập
- In các số chẵn từ 0 đến n, n không âm. Gợi ý: Bước 2: k = 0, 2, 4 chừng nào k <= n.
- In bảng cửu chương của một số từ 1 đến 9. Gợi ý: for i từ 1 đến 9, in so * i.
- Tính
n!với0 <= n <= 20.0!là 1. Gợi ý: Khởi tạo 1, nhân từ 1 đến n. n âm hoặc n > 20 thì từ chối. - Nhập điểm đến khi nằm trong 0..10 bằng
do while. Gợi ý: Thân hỏi trước, while lặp khi điểm còn ngoài đoạn. - Tính tổng
1^3 + … + n^3bằnglong long. Gợi ý: Ép (long long) trước khi nhân iii. - In các ước dương của n và kết luận nguyên tố hay không. Gợi ý: Ước là d mà n % d == 0. Đúng hai ước thì nguyên tố.
- Vẽ tam giác
*cao h, hàng i có i sao. Gợi ý: Hàng i, vòng trong chạy i lần. - Đếm số chữ số của một số không âm. Ca 0 ra 1. Gợi ý: Chia 10 đến khi hết. Riêng 0 thì in 1.
- Tổng
1 + 1/2 + … + 1/nbằngdouble. Viết1.0/i. Gợi ý: Biến tổng kiểu double. 1/i với hai int sẽ ra 0. - Đảo chữ số: 1203 in ra 3021. Vẽ lưu đồ chia 10. Gợi ý: Lấy n % 10, in hoặc ghép, rồi n = n / 10.
- UCLN và BCNN của hai số dương. Chia trước khi nhân. Gợi ý: Giữ bản sao a, b để tính BCNN = a / ucln * b.
- Liệt kê số nguyên tố từ 2 đến n và đếm. Gợi ý: Với mỗi số, thử ước đến căn. Đếm khi là nguyên tố.
- Phân tích n thành thừa số nguyên tố. 84 in
2 * 2 * 3 * 7. Gợi ý: Chia hết cho d thì in d và chia n, không tăng d. Không hết thì tăng d. - Tam giác cân rỗng bằng
*. Gợi ý: Hàng đầu và hàng cuối in đầy sao. Hàng giữa chỉ hai cạnh. - Tổng
1 - 1/2 + 1/3 - …đến n. Gợi ý: Dấu đổi mỗi vòng: bắt đầu dương. - Đọc số cho đến khi gặp 0. In số lượng số dương và tổng của chúng. Gợi ý: Số 0 kết thúc, không cộng vào tổng.
- In n số nguyên tố đầu tiên, không phải mọi nguyên tố đến n. Gợi ý: Vòng ngoài đếm đủ n số. Vòng trong kiểm tra một ứng viên.
- Số hoàn hảo không vượt n. Nêu bậc nếu mỗi số cộng ước đến chính nó. Gợi ý: Tổng ước trừ chính nó bằng chính nó. 6 và 28 là hai ca nhỏ.
- Lũy thừa nhanh
x^nbằng bình phương liên tiếp. Đếm số phép nhân với n = 13. Gợi ý: n chẵn thì bình phương, n lẻ thì nhân thêm một x. Đếm phép nhân. - Leibniz ước lượng pi. Bảng sai số với n = 10, 1000, 100000. Gợi ý: Hạng tử dấu xen kẽ với mẫu lẻ. So với acos(-1).
- Collatz: đếm bước từ một số dương về 1. Trần 10000 vòng. Số 6 phải ra 8 bước. Gợi ý: if chẵn thì n/2, else 3*n+1. Đếm đến khi n == 1.
- Trả góp. Dư nợ, lãi suất tháng, tiền trả mỗi tháng. In từng tháng tối đa 360 tháng. Dừng khi hết nợ hoặc khi tiền trả không đủ trả lãi. Gợi ý: neu = no * (1+r) - tra. Nếu nợ mới không nhỏ hơn nợ cũ thì dừng và báo không đủ trả lãi.
- Đồng hồ đếm ngược. Giờ, phút, giây hợp lệ, in từng nhịp giảm 1 giây đến
00:00:00. Không cầnsleep. Số dòng in bằng tổng số giây. Gợi ý: Mỗi vòng giảm một giây, mượn phút và giờ khi giây hoặc phút về âm. - Cửa sổ ba mẫu. Đọc số thực đến khi gặp -1. Chỉ dùng ba biến xoay vòng, in trung bình mỗi khi đã có đủ ba mẫu gần nhất. Không dùng mảng. Gợi ý: Ba biến a, b, c xoay sau mỗi số mới. Chưa đủ ba số thì chưa in.
- Bộ lọc menu. In menu đến khi người dùng chọn 0. Mỗi lựa chọn 1, 2, 3 làm một việc nhỏ đã học. Lựa chọn lạ báo lỗi và hỏi lại, không thoát cả chương trình. Gợi ý: do while đến khi chọn 0. switch các mục. default không break khỏi menu.