Bài 10.615 phút đọc
Đường chéo và tam giác
Sau bài này bạn sẽ làm được
- Viết điều kiện chỉ số cho hai đường chéo
- Phân biệt ba vùng tam giác trên, dưới và đường chéo
- Tính vết ma trận và kiểm tra ma trận đơn vị
- Nhận ra ma trận đối xứng bằng một vòng lặp nửa
Bài cuối của Chương 10 gom lại những mẫu điều kiện chỉ số hay dùng nhất với ma trận vuông. Chúng đơn giản nhưng xuất hiện khắp nơi, và biết viết chúng nhanh giúp bạn tập trung vào phần lô gic thật của bài toán.
#Hai đường chéo
i == j /* đường chéo chính, từ góc trên trái xuống góc dưới phải */
i + j == n - 1 /* đường chéo phụ, từ góc trên phải xuống góc dưới trái */Với n = 4:
đường chéo chính đường chéo phụ
| X . . . | | . . . X |
| . X . . | | . . X . |
| . . X . | | . X . . |
| . . . X | | X . . . |duong-cheo.c
/* Đường chéo chính: chỉ cần MỘT vòng lặp */
long long tong_cheo_chinh(const int *A, size_t n)
{
long long s = 0;
for (size_t i = 0; i < n; ++i)
s += A[i * n + i];
return s;
}
/* Đường chéo phụ: cũng chỉ cần một vòng lặp */
long long tong_cheo_phu(const int *A, size_t n)
{
long long s = 0;
for (size_t i = 0; i < n; ++i)
s += A[i * n + (n - 1 - i)];
return s;
}#Ba vùng tam giác
| Vùng | Điều kiện | Số ô khi ma trận n nhân n |
|---|---|---|
| Tam giác trên, không kể đường chéo | j > i | n(n-1)/2 |
| Tam giác trên, kể cả đường chéo | j >= i | n(n+1)/2 |
| Đường chéo chính | j == i | n |
| Tam giác dưới, kể cả đường chéo | j <= i | n(n+1)/2 |
| Tam giác dưới, không kể đường chéo | j < i | n(n-1)/2 |
Với n = 4, ký hiệu T là trên, C là chéo, D là dưới:
| C T T T |
| D C T T |
| D D C T |
| D D D C |tam-giac.c
/* Duyệt tam giác trên bằng vòng lặp trực tiếp, không dùng if */
for (size_t i = 0; i < n; ++i)
for (size_t j = i + 1; j < n; ++j)
xu_ly(A[i * n + j]);
/* Tam giác dưới */
for (size_t i = 1; i < n; ++i)
for (size_t j = 0; j < i; ++j)
xu_ly(A[i * n + j]);
/* Tam giác trên kèm đường chéo */
for (size_t i = 0; i < n; ++i)
for (size_t j = i; j < n; ++j)
xu_ly(A[i * n + j]);Kiểm tra ma trận tam giác
/* Ma trận tam giác trên: mọi ô ở tam giác dưới đều bằng 0 */
int la_tam_giac_tren(const int *A, size_t n)
{
for (size_t i = 1; i < n; ++i)
for (size_t j = 0; j < i; ++j)
if (A[i * n + j] != 0)
return 0;
return 1;
}Ma trận tam giác quan trọng trong đại số tuyến tính vì hệ phương trình với ma trận tam giác giải được ngay bằng phép thế ngược, không cần khử Gauss. Định thức của ma trận tam giác cũng chỉ là tích các phần tử trên đường chéo.
#Vết ma trận
Vết
Tổng các phần tử trên đường chéo chính. Ký hiệu toán học là
tr(A).long long vet(const int *A, size_t n)
{
long long s = 0;
for (size_t i = 0; i < n; ++i)
s += A[i * n + i];
return s;
}| Tính chất | Công thức |
|---|---|
| Tuyến tính | tr(A + B) = tr(A) + tr(B) |
| Với hệ số | tr(cA) = c * tr(A) |
| Bất biến qua chuyển vị | tr(At) = tr(A) |
| Hoán vị vòng | tr(A * B) = tr(B * A) |
| Bằng tổng các trị riêng | tr(A) = tổng các trị riêng |
#Nhận dạng các loại ma trận
nhan-dang.c
/* Ma trận đơn vị: đường chéo bằng 1, mọi ô khác bằng 0 */
int la_don_vi(const int *A, size_t n)
{
for (size_t i = 0; i < n; ++i)
for (size_t j = 0; j < n; ++j) {
int mong_doi = (i == j) ? 1 : 0;
if (A[i * n + j] != mong_doi)
return 0;
}
return 1;
}
/* Ma trận đối xứng: A[i][j] == A[j][i] */
int la_doi_xung(const int *A, size_t n)
{
for (size_t i = 0; i < n; ++i)
for (size_t j = i + 1; j < n; ++j)
if (A[i * n + j] != A[j * n + i])
return 0;
return 1;
}
/* Ma trận đường chéo: mọi ô ngoài đường chéo bằng 0 */
int la_duong_cheo(const int *A, size_t n)
{
for (size_t i = 0; i < n; ++i)
for (size_t j = 0; j < n; ++j)
if (i != j && A[i * n + j] != 0)
return 0;
return 1;
}Ma phương
ma-phuong.c
/* Ma phương: mọi hàng, mọi cột và hai đường chéo có tổng bằng nhau. */
int la_ma_phuong(const int *A, size_t n)
{
if (n == 0) return 0;
/* Lấy tổng hàng đầu làm chuẩn */
long long chuan = 0;
for (size_t j = 0; j < n; ++j)
chuan += A[j];
/* Kiểm tra từng hàng */
for (size_t i = 1; i < n; ++i) {
long long s = 0;
for (size_t j = 0; j < n; ++j)
s += A[i * n + j];
if (s != chuan) return 0;
}
/* Kiểm tra từng cột */
for (size_t j = 0; j < n; ++j) {
long long s = 0;
for (size_t i = 0; i < n; ++i)
s += A[i * n + j];
if (s != chuan) return 0;
}
/* Hai đường chéo */
long long c1 = 0, c2 = 0;
for (size_t i = 0; i < n; ++i) {
c1 += A[i * n + i];
c2 += A[i * n + (n - 1 - i)];
}
return c1 == chuan && c2 == chuan;
}terminal
./ma-phuong
2 7 6
9 5 1
4 3 8
La ma phuong, tong moi duong = 15#Duyệt xoắn ốc
Bài toán cuối cùng của chương, và là bài khó nhất về mặt điều khiển chỉ số. In các phần tử theo đường xoắn từ ngoài vào trong.
| 1 2 3 4 |
| 5 6 7 8 | -> 1 2 3 4 8 12 11 10 9 5 6 7
| 9 10 11 12 |xoan-oc.c
void duyet_xoan_oc(const int *A, size_t hang, size_t cot)
{
if (hang == 0 || cot == 0) return;
size_t tren = 0, duoi = hang - 1;
size_t trai = 0, phai = cot - 1;
while (1) {
/* Đi sang phải trên hàng trên cùng */
for (size_t j = trai; j <= phai; ++j)
printf("%d ", A[tren * cot + j]);
if (tren == duoi) break;
++tren;
/* Đi xuống dưới ở cột phải cùng */
for (size_t i = tren; i <= duoi; ++i)
printf("%d ", A[i * cot + phai]);
if (trai == phai) break;
--phai;
/* Đi sang trái trên hàng dưới cùng */
for (size_t j = phai + 1; j-- > trai; )
printf("%d ", A[duoi * cot + j]);
if (tren == duoi) break;
--duoi;
/* Đi lên trên ở cột trái cùng */
for (size_t i = duoi + 1; i-- > tren; )
printf("%d ", A[i * cot + trai]);
if (trai == phai) break;
++trai;
}
}terminal
./xoan-oc
3x4: 1 2 3 4 8 12 11 10 9 5 6 7 1x4: 1 2 3 4 4x1: 1 2 3 4 1x1: 1
Tự làm thử
- Viết chương trình tính tổng hai đường chéo của ma trận vuông, xử lý đúng trường hợp n lẻ.
- Đếm số ô của từng vùng tam giác với n bằng 5 và đối chiếu với công thức trong bảng.
- Viết hàm kiểm tra ma trận tam giác trên, tam giác dưới và ma trận đường chéo.
- Cài
vet_tichtính vết của tích mà không tính tích, so sánh thời gian với cách tính tích trước rồi lấy vết, với n bằng 1000. - Viết chương trình kiểm tra ma phương, thử với ma phương ba nhân ba kinh điển và một ma trận gần đúng chỉ sai một ô.
- Cài duyệt xoắn ốc, thử đủ các trường hợp một hàng, một cột, một ô, và ma trận vuông lẻ.
- Viết hàm sinh ma trận xoắn ốc: điền các số từ 1 tới n nhân n theo đường xoắn.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- Đường chéo chính là
i == j, đường chéo phụ lài + j == n - 1. Cả hai chỉ cần một vòng lặp. - Khi
nlẻ, hai đường chéo cắt nhau ở ô tâm, phải trừ đi khi cộng tổng hai đường. - Đặt điều kiện vùng tam giác vào biên của vòng lặp thay vì vào câu lệnh
if, để không duyệt thừa một nửa số ô. - Vết của tích tính được trong
n²phép nhân mà không cần tính cả tích. - Duyệt xoắn ốc cần bốn chỗ kiểm tra dừng, và phải thử với ma trận một hàng, một cột và một ô.