Bài 6.5. Bài tập Python: tìm median

Bài 6.5. Bài tập Python: tìm median
Chào mừng bạn đến với một bài tập thú vị khác trong hành trình chinh phục Python! Hôm nay, chúng ta sẽ cùng nhau tìm hiểu và viết code để tính toán median (hay còn gọi là trung vị) của một tập hợp các số. Median là một khái niệm quan trọng trong thống kê, giúp chúng ta hiểu rõ hơn về giá trị trung tâm của một tập dữ liệu.
Vậy, median là gì? Đơn giản thôi, median là giá trị nằm ở vị trí chính giữa của một tập dữ liệu khi tập dữ liệu đó đã được sắp xếp.
Để tìm median, chúng ta cần thực hiện hai bước chính:
- Sắp xếp tập dữ liệu theo thứ tự tăng dần hoặc giảm dần.
- Tìm giá trị ở vị trí chính giữa.
Tuy nhiên, việc tìm giá trị chính giữa lại có hai trường hợp xảy ra, tùy thuộc vào tổng số lượng phần tử trong tập dữ liệu:
- Nếu số lượng phần tử là lẻ: Median chính là phần tử nằm đúng ở vị trí giữa.
- Nếu số lượng phần tử là chẵn: Không có một phần tử nào nằm đúng ở giữa. Thay vào đó, chúng ta sẽ lấy trung bình cộng của hai phần tử nằm ở hai vị trí giữa.
Nghe có vẻ đơn giản phải không? Giờ chúng ta hãy cùng nhau viết code Python để thực hiện điều này nhé!
Xây dựng hàm tìm Median trong Python
Chúng ta sẽ tạo một hàm nhận vào một danh sách các số và trả về median của danh sách đó.
def tim_median(danh_sach_so):
"""
Tìm median (trung vị) của một danh sách các số.
Args:
danh_sach_so: Một danh sách chứa các số.
Returns:
Giá trị median của danh sách.
Trả về None nếu danh sách rỗng.
"""
# Bước 1: Sắp xếp danh sách
# Sử dụng sorted() để tạo một danh sách mới đã sắp xếp mà không làm thay đổi danh sách gốc
danh_sach_da_sap_xep = sorted(danh_sach_so)
# Lấy số lượng phần tử trong danh sách
n = len(danh_sach_da_sap_xep)
# Kiểm tra trường hợp danh sách rỗng
if n == 0:
return None # Hoặc có thể raise một Exception tùy theo yêu cầu
# Bước 2: Tìm giá trị chính giữa
# Trường hợp số lượng phần tử là lẻ
if n % 2 == 1:
# Vị trí chính giữa là n // 2 (phép chia lấy phần nguyên)
# Ví dụ: n=5, vị trí giữa là 5 // 2 = 2 (phần tử thứ 3, index 2)
vi_tri_giua = n // 2
median = danh_sach_da_sap_xep[vi_tri_giua]
return median
# Trường hợp số lượng phần tử là chẵn
else:
# Có hai vị trí giữa: n // 2 - 1 và n // 2
# Ví dụ: n=6, vị trí giữa là 6 // 2 - 1 = 2 và 6 // 2 = 3
# (phần tử thứ 3 và thứ 4, index 2 và 3)
vi_tri_giua_1 = n // 2 - 1
vi_tri_giua_2 = n // 2
median = (danh_sach_da_sap_xep[vi_tri_giua_1] + danh_sach_da_sap_xep[vi_tri_giua_2]) / 2
return median
Giải thích Code chi tiết
Chúng ta hãy cùng xem xét từng phần của hàm tim_median
để hiểu rõ cách nó hoạt động.
def tim_median(danh_sach_so):
: Dòng này định nghĩa một hàm tên làtim_median
nhận một đối số làdanh_sach_so
(được mong đợi là một danh sách)."""Docstring"""
: Đây là docstring, một chuỗi tài liệu giúp giải thích mục đích của hàm, các đối số (Args
) và giá trị trả về (Returns
). Đây là một thói quen tốt khi viết code để làm cho code dễ hiểu hơn.danh_sach_da_sap_xep = sorted(danh_sach_so)
: Đây là bước quan trọng nhất. Hàmsorted()
trong Python nhận một iterable (như danh sách) và trả về một danh sách mới chứa tất cả các phần tử của iterable gốc theo thứ tự tăng dần. Điều này đảm bảo danh sách gốcdanh_sach_so
không bị thay đổi.n = len(danh_sach_da_sap_xep)
: Dòng này tính toán số lượng phần tử trong danh sách đã được sắp xếp và gán cho biếnn
. Chúng ta cần giá trị này để xác định xem số lượng phần tử là chẵn hay lẻ và tìm vị trí giữa.if n == 0:
: Đây là cách xử lý trường hợp đặc biệt: danh sách đầu vào rỗng. Một danh sách rỗng không có median theo định nghĩa thông thường. Chúng ta chọn cách trả vềNone
để báo hiệu điều này.if n % 2 == 1:
: Đây là điều kiện kiểm tra nếu số lượng phần tửn
là lẻ. Phép toán%
là phép chia lấy phần dư. Nếun
chia cho 2 có phần dư là 1, nghĩa làn
là số lẻ.vi_tri_giua = n // 2
: Nếun
là số lẻ, phép chia lấy phần nguyên//
sẽ cho chúng ta chỉ số (index) của phần tử nằm đúng ở giữa danh sách đã sắp xếp. Ví dụ, với danh sách 5 phần tử, index là 0, 1, 2, 3, 4. Số lượng là 5,5 // 2
bằng 2, đó chính là index của phần tử ở giữa.median = danh_sach_da_sap_xep[vi_tri_giua]
: Truy cập phần tử tại chỉ sốvi_tri_giua
trong danh sách đã sắp xếp và gán nó cho biếnmedian
.return median
: Trả về giá trị median đã tìm được.else:
: Phần này xử lý trường hợp khi số lượng phần tửn
là chẵn (n % 2 == 0
là đúng).vi_tri_giua_1 = n // 2 - 1
vàvi_tri_giua_2 = n // 2
: Khin
là chẵn, có hai phần tử ở giữa. Ví dụ, với danh sách 6 phần tử, index là 0, 1, 2, 3, 4, 5. Số lượng là 6,6 // 2
bằng 3. Vị trí giữa đầu tiên là3 - 1 = 2
, vị trí giữa thứ hai là3
.median = (danh_sach_da_sap_xep[vi_tri_giua_1] + danh_sach_da_sap_xep[vi_tri_giua_2]) / 2
: Lấy giá trị của hai phần tử ở hai vị trí giữa, cộng chúng lại và chia cho 2 để tính trung bình cộng. Đây chính là median cho tập dữ liệu có số lượng phần tử chẵn. Lưu ý rằng kết quả của phép chia/
trong Python 3 luôn là số thực (float).return median
: Trả về giá trị median là trung bình cộng của hai phần tử giữa.
Các ví dụ minh họa
Để hiểu rõ hơn, chúng ta hãy thử nghiệm hàm tim_median
với nhiều loại dữ liệu khác nhau.
Ví dụ 1: Danh sách có số lượng phần tử lẻ
# Danh sách ban đầu (chưa sắp xếp)
du_lieu_le = [5, 2, 8, 1, 9, 4, 7]
# Gọi hàm tìm median
median_le = tim_median(du_lieu_le)
# In kết quả
print(f"Danh sách gốc: {du_lieu_le}")
# Danh sách đã sắp xếp sẽ là [1, 2, 4, 5, 7, 8, 9].
# Có 7 phần tử (số lẻ). Vị trí giữa là 7 // 2 = 3 (index 3).
# Phần tử tại index 3 là 5.
print(f"Median của danh sách (lẻ phần tử): {median_le}")
# Expected Output: Median của danh sách (lẻ phần tử): 5
Giải thích:
Danh sách du_lieu_le
có 7 phần tử. Khi được sắp xếp, nó trở thành [1, 2, 4, **5**, 7, 8, 9]
. Vì 7 là số lẻ, phần tử ở giữa là phần tử thứ tư, có giá trị là 5. Code của chúng ta tính n = 7
, n % 2 == 1
(đúng). vi_tri_giua = 7 // 2 = 3
. danh_sach_da_sap_xep[3]
là 5. Kết quả trả về là 5, hoàn toàn chính xác.
Ví dụ 2: Danh sách có số lượng phần tử chẵn
# Danh sách ban đầu (chưa sắp xếp)
du_lieu_chan = [10, 3, 6, 1, 8, 4]
# Gọi hàm tìm median
median_chan = tim_median(du_lieu_chan)
# In kết quả
print(f"\nDanh sách gốc: {du_lieu_chan}")
# Danh sách đã sắp xếp sẽ là [1, 3, 4, 6, 8, 10].
# Có 6 phần tử (số chẵn). Hai vị trí giữa là 6 // 2 - 1 = 2 và 6 // 2 = 3 (index 2 và 3).
# Phần tử tại index 2 là 4, phần tử tại index 3 là 6.
# Median là (4 + 6) / 2 = 5.0
print(f"Median của danh sách (chẵn phần tử): {median_chan}")
# Expected Output: Median của danh sách (chẵn phần tử): 5.0
Giải thích:
Danh sách du_lieu_chan
có 6 phần tử. Khi được sắp xếp, nó trở thành [1, 3, **4**, **6**, 8, 10]
. Vì 6 là số chẵn, chúng ta lấy trung bình cộng của hai phần tử giữa là 4 và 6. (4 + 6) / 2 = 5
. Code của chúng ta tính n = 6
, n % 2 == 1
(sai). Vào khối else
. vi_tri_giua_1 = 6 // 2 - 1 = 2
. vi_tri_giua_2 = 6 // 2 = 3
. Median được tính là (danh_sach_da_sap_xep[2] + danh_sach_da_sap_xep[3]) / 2 = (4 + 6) / 2 = 5.0
. Kết quả là 5.0 (số thực), cũng đúng.
Ví dụ 3: Danh sách chứa số thập phân (float)
Hàm của chúng ta hoạt động tốt với cả số nguyên và số thực.
# Danh sách chứa số thập phân
du_lieu_thap_phan = [3.14, 1.618, 2.718, 0.577]
# Gọi hàm tìm median
median_thap_phan = tim_median(du_lieu_thap_phan)
# In kết quả
print(f"\nDanh sách gốc: {du_lieu_thap_phan}")
# Danh sách đã sắp xếp: [0.577, 1.618, 2.718, 3.14].
# Có 4 phần tử (chẵn). Hai vị trí giữa: index 1 và 2.
# Phần tử tại index 1 là 1.618, phần tử tại index 2 là 2.718.
# Median = (1.618 + 2.718) / 2 = 4.336 / 2 = 2.168
print(f"Median của danh sách (số thập phân): {median_thap_phan}")
# Expected Output: Median của danh sách (số thập phân): 2.168
Giải thích:
Danh sách du_lieu_thap_phan
có 4 phần tử. Sắp xếp: [0.577, **1.618**, **2.718**, 3.14]
. Hai phần tử giữa là 1.618 và 2.718. Trung bình cộng: (1.618 + 2.718) / 2 = 2.168
. Code của chúng ta sẽ thực hiện tính toán tương tự.
Ví dụ 4: Danh sách có các giá trị lặp lại
# Danh sách có giá trị lặp lại
du_lieu_lap = [7, 7, 7, 7, 7]
# Gọi hàm tìm median
median_lap = tim_median(du_lieu_lap)
# In kết quả
print(f"\nDanh sách gốc: {du_lieu_lap}")
# Danh sách đã sắp xếp: [7, 7, **7**, 7, 7].
# Có 5 phần tử (lẻ). Vị trí giữa là index 2.
# Phần tử tại index 2 là 7.
print(f"Median của danh sách (có giá trị lặp): {median_lap}")
# Expected Output: Median của danh sách (có giá trị lặp): 7
Giải thích:
Median không bị ảnh hưởng bởi các giá trị lặp lại. Với danh sách [7, 7, 7, 7, 7]
, sau khi sắp xếp (vẫn là chính nó), có 5 phần tử (lẻ), phần tử ở giữa (index 2) là 7. Median là 7.
Ví dụ 5: Danh sách rỗng
# Danh sách rỗng
du_lieu_rong = []
# Gọi hàm tìm median
median_rong = tim_median(du_lieu_rong)
# In kết quả
print(f"\nDanh sách gốc: {du_lieu_rong}")
# Số lượng phần tử là 0. Hàm sẽ trả về None.
print(f"Median của danh sách rỗng: {median_rong}")
# Expected Output: Median của danh sách rỗng: None
Giải thích:
Khi danh sách rỗng, n = len([])
sẽ là 0. Điều kiện if n == 0:
sẽ đúng, và hàm sẽ trả về None
như đã định nghĩa.
Ví dụ 6: Danh sách chỉ có một phần tử
# Danh sách chỉ có một phần tử
du_lieu_mot = [42]
# Gọi hàm tìm median
median_mot = tim_median(du_lieu_mot)
# In kết quả
print(f"\nDanh sách gốc: {du_lieu_mot}")
# Danh sách đã sắp xếp: [42].
# Có 1 phần tử (lẻ). Vị trí giữa là 1 // 2 = 0 (index 0).
# Phần tử tại index 0 là 42.
print(f"Median của danh sách (một phần tử): {median_mot}")
# Expected Output: Median của danh sách (một phần tử): 42
Giải thích:
Với danh sách [42]
, sau khi sắp xếp (vẫn là chính nó), có 1 phần tử (lẻ). n = 1
. n % 2 == 1
(đúng). vi_tri_giua = 1 // 2 = 0
. danh_sach_da_sap_xep[0]
là 42. Median là 42, chính là giá trị duy nhất trong danh sách.
Tóm kết (Đến đây thôi nhé!)
Qua bài tập này, chúng ta đã thành công trong việc xây dựng một hàm Python để tìm median của một tập hợp số. Chúng ta đã học cách xử lý cả hai trường hợp: khi số lượng phần tử là lẻ và khi số lượng phần tử là chẵn. Việc sử dụng hàm sorted()
và hiểu rõ về cách truy cập phần tử bằng index trong danh sách là chìa khóa để giải quyết bài toán này.
Hãy tiếp tục luyện tập với các bộ dữ liệu khác để củng cố kiến thức nhé!
Comments