Category: Python

  • Python: Bài toán xếp hậu sử dụng đệ quy

    Python: Bài toán xếp hậu sử dụng đệ quy

    Python: Bài toán xếp hậu sử dụng đệ quy

    Bài toán xếp hậu là một bài toán kinh điển thường được giới thiệu trong các cuốn sách về thuật toán. Ở đây, chúng ta sẽ giải quyết bài toán bằng các sử dụng giải thuật đệ quythuật toán quay lui (vét cạn).

    Xem thêm: Thuật toán giải sudoku bằng quay lui backtracking

    Xét bàn cờ tổng quát kích thước nxn. Một quân hậu trên bàn cờ có thể ăn được các quân khác nằm tại các ô cùng hàng, cùng cột hoặc cùng đường chéo. Hãy tìm các xếp n quân hậu trên bàn cờ sao cho không quân nào ăn quân nào.

    Ví dụ một cách xếp với n = 8

    bài toán xếp hậu với n=8

    1. Giải bài toán xếp hậu sử dụng đệ quy

    Để giải quyết bài toán xếp 8 quân hậu này, chúng ta sử dụng list (mảng) a có kích thước bằng 8 với quy ước a[i]=j để đánh dấu vị trí xếp quân hậu thuộc dòng i ở cột thứ j.

    1.1. Tạo danh sách để lưu vị trí các quân hậu

    Để khởi tạo mảng a, chúng ta dùng lệnh

    n = 8
    a = n*[0]

    Lưu ý rằng các list danh sách trong Python được đánh chỉ số từ 0, do đó vị trí của quân hậu ở dòng thứ nhất được lưu ở a[0]

    Ví dụ a=[0, 4, 7, 5, 2, 6, 1, 3]có ý nghĩa, quân hậu ở dòng thứ nhất sẽ ở cột thứ nhất, quân hậu ở dòng thứ hai sẽ ở cột thứ 5, quân hậu ở dòng thứ 3 sẽ ở cột thứ 8…

    1.2. Viết hàm in vị trí các quân hậu dạng ma trận

    Chúng ta sẽ viết một hàm print_board() để in ra màn hình vị trí các quân hậu dạng ma trận (bảng), ví dụ với mảng a ở trên, in ra màn hình được:

    1 0 0 0 0 0 0 0 
    0 0 0 0 1 0 0 0 
    0 0 0 0 0 0 0 1 
    0 0 0 0 0 1 0 0 
    0 0 1 0 0 0 0 0 
    0 0 0 0 0 0 1 0 
    0 1 0 0 0 0 0 0 
    0 0 0 1 0 0 0 0

    Hàm in vị trí các quân hậu như sau:

    def print_board(b):
       l = len(b)
       for i in range(l):
          for j in range(l):
             if j == a[i]:
                print(1, end = " ")
             else:
                print(0, end = " ")
          print()

    1.3. Viết hàm kiểm tra vị trí (d,c) có còn đặt được quân hậu mới không?

    Chúng ta quy ước dc là vị trí của dòng và cột đang xét để xem có khả năng đặt được quân hậu mới hay không.

    Rõ ràng, ta chỉ cần kiểm tra xem trong các dòng từ 0 tới d-1 đã có quân hậu nào mà có thể ăn được quân hậu nếu đặt ở ô đang xét hay không, do đó chỉ cần duyệt từ các dòng có chỉ số thuộc range(d).

    Chúng ta viết hàm possible(d,c) để kiểm tra vị trí d,c còn có khả năng đặt hậu hay không. Khi đó, kết quả trả về là False nếu một trong 3 khả năng sau xảy ra:

    • Tại cột c đã có quân hậu ở dòng i nào đó, điều kiện là a[i] == c
    • Tại 2 đường chéo đi qua điểm (d,c) đã có một quân hậu nào đó.

    Lưu ý rằng các đường chéo sẽ song song hoặc trùng với các tia phân giác của góc phần tư trong hệ tọa độ Oxy. Các tia phân giác có phương trình là y = x hoặc y = - x. Do đó phương trình các đường chéo đi qua điểm có tọa độ (d,c) sẽ là c - d =y -x  hoặc c + d = y  + x. Từ đó suy ra điều kiện:

    a[i] == c  or c - d == a[i] - i or c + d == a[i] + i

    Mã nguồn của hàm kiểm tra vị trí có thể đặt hậu như sau:

    def possible(x, y):
       for i in range(x):      
          if a[i] == y  or y - x == a[i] - i or y + x == a[i] + i:
             return False
       return True

    1.4. Hàm đệ quy để sinh ra các vị trí đặt quân hậu

    def gen(i, n):
       for j in range(n):
          if possible(i,j):
             a[i] = j
             if i == n - 1:
                print(a)
             gen(i+1, n)

    Chương trình bắt đầu với lời gọi gen(0, n).

    Mã nguồn hoàn chỉnh bằng Python của bài toán xếp hậu như sau:

    n = 8
    a = n*[0]
    
    def print_board(b):
       l = len(b)
       for i in range(l):
          for j in range(l):
             if j == a[i]:
                print(1, end = " ")
             else:
                print(0, end = " ")
          print()
    
    def possible(d, c):
       for i in range(d):
          # if a[i] == y or abs(i - x) == abs(a[i] - y):
          if a[i] == c  or c - d == a[i] - i or c + d == a[i] + i:
             return False
       return True
    
    def gen(i, n):
       for j in range(n):
          if possible(i,j):
             a[i] = j
             if i == n - 1:
                print(a)
             gen(i+1, n)
    
    gen(0, n)

    Kết quả trả về là tất cả các phương án mỗi phương án là một danh sách chứa vị trí các quân hậu. Nếu muốn in một phương án nào đó, chúng ta sử dụng hàm print_board()

    2. Bài toán xếp hậu quay lui

    Chúng tôi giới thiệu thêm bài toán xếp hậu sử dụng thuật toán quay lui để bạn đọc tham khảo. Cách làm này có thời gian chạy khá chậm, và mỗi lần chỉ in ra một phương án xếp các quân hậu. Muốn tìm được phương án khác, chúng ta cần thay đổi giá trị của mảng board ban đầu.

    Cách giải này tôi có tham khảo từ bài viết này.

    board = [
          [0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0]
          ]
    
    
    def print_board(bo):
        for d in bo:        
            for c in d:
                print(c, end = " ")
            print()
    
    
    def solve(bo):
       l = len(bo)
       for d in range(l):
          for c in range(l):
             if bo[d][c] == 0:
                if possible(bo, d, c):
                   bo[d][c] = 1
                   solve(bo)
                if sum(sum(a) for a in bo) == l:
                   return bo
                else:
                   bo[d][c] = 0
       return bo		
    
    
    def possible(bo, d, c):
       l = len(bo)
       for i in range(l):
          if bo[i][c] == 1:
             return False
       for i in range(l):
          if bo[d][i] == 1:
             return False
       for i in range(l):
          for j in range(l):
             if bo[i][j] == 1:
                if c-d == j-i or c+d == i+j:
                   return False
       return True
    
    solve(board)
    print_board(board)
  • Thuật toán giải sudoku bằng quay lui backtracking

    Thuật toán giải sudoku bằng quay lui backtracking

    Thuật toán giải sudoku bằng quay lui backtracking

    Trong bài này chúng ta sẽ sử dụng thuật toán quay lui để giải quyết bài toán giải Sudoku. Để hiểu thuật toán quay lui là gì mời bạn xem bài Thuật toán quay lui và minh họa.

    Xem thêm: Python: Bài toán xếp hậu sử dụng đệ quy

    Bài viết tham khảo từ techwithtim.net

    1. Giới thiệu luật chơi và cách giải Sudoku

    Sudoku là một trò chơi giải đố theo wiki định nghĩa như sau:

    Sudoku (数独すうどく (số độc) sūdoku?) (suːˈdoʊkuː/, /-ˈdɒ-/, /sə-/, ban đầu có tên gọi là Number Place) là một trò chơi câu đố sắp xếp chữ số dựa trên logic theo tổ hợp. Mục tiêu của trò chơi là điền các chữ số vào một lưới 9×9 sao cho mỗi cột, mỗi hàng, và mỗi phần trong số chín lưới con 3×3 cấu tạo nên lưới chính (cũng gọi là “hộp”, “khối”, hoặc “vùng”) đều chứa tất cả các chữ số từ 1 tới 9. Câu đố đã được hoàn thành một phần, người chơi phải giải tiếp bằng việc điền số. Mỗi câu đố được thiết lập tốt có một cách làm duy nhất.

    Cách giải trò chơi Sudoku, mời các bạn xem video hướng dẫn sau:

    2. Thuật toán giải Sudoku

    Sau đây ta sẽ tìm thuật toán giải Sudoku bằng kỹ thuật backtracking, ngôn ngữ lập trình sử dụng là Python. Các bước tiến hành như sau:

    • Viết hàm in câu đố Sudoku ra màn hình.
    • Tìm vị trí các ô trống trong Sudoku.
    • Với mỗi vị trí ô trống vừa tìm được, lần lượt thử đặt số từ 1 đến 9 vào ô trống đó. Kiểm tra xem sau khi thử đặt số đó vào ô trống đó thì có hợp lệ (thỏa mãn các điều kiện về luật chơi của Sudoku hay không). Nếu hợp lệ thì tiếp tục tìm các ô trống tiếp theo và lại thử, nếu không thì thử với số_tiếp_theo
    • Lặp lại quy trình trên cho đến khi không còn ô trống nào trên câu đố, hoặc không tìm được lời giải.

    Input, một câu đố sudoku biểu diễn bởi danh sách list 2 chiều (một danh sách gồm 9 phần tử, mỗi phần tử là dòng – lại là một danh sách gồm 9 phần tử  tương ứng với 9 ô trong một dòng) với các ô trống được quy ước điền bởi số 0, ví dụ Sudoku cau_do được biểu diễn như sau:

    cau_do = [
        [7,8,0,4,0,0,1,2,0],
        [6,0,0,0,7,5,0,0,9],
        [0,0,0,6,0,1,0,7,8],
        [0,0,7,0,4,0,2,6,0],
        [0,0,1,0,5,0,9,3,0],
        [9,0,4,0,6,0,0,0,5],
        [0,7,0,3,0,0,0,1,2],
        [1,2,0,0,0,7,4,0,0],
        [0,4,9,2,0,6,0,0,7]
    ]

    Chú ý rằng chỉ số index trong Python được đánh từ 0 trở đi, do đó các vị trí của từng ô trong bảng số sẽ là cau_d0[0][0] cho đến cau_do[8][8], ở đây cau_do[d][c] là ô số ở vị trí dòng d và cột c.

    2.1. Viết hàm in câu đố Sudoku ra màn hình

    Ở đây chúng ta sử dụng giao diện dòng lệnh, chưa sử dụng giao diện đồ họa GUI nên sẽ sử dụng hàm print() của Python để in một đối tượng ra màn hình CMD.

    Ta sẽ viết hàm in_sudoku để in một câu đố Sudoku có tên là q ra màn hình, sử dụng biến dc để biểu diễn dòng và cột.

    Nếu dòng d là dòng thứ 3 hoặc 6 thì ta sẽ in ra màn hình một dòng gồm các kí tự - - - - - - - - - - - để ngăn cách, mục đích là biểu diễn cho các khối ô vuông 3x3 của Sudoku. Tương tự, nếu cột ở vị trí 3 hoặc 6 thì ta sẽ in ra kí tự | để ngăn cách. Nếu cột ở vị trí thứ 8 thì ta sẽ xuống dòng mới.

    def in_sudoku(q):
        for d in range(len(q)):
            if d % 3 == 0 and d != 0:
                print("- - - - - - - - - - -")
            for c in range(len(q[0])):
                if c % 3 == 0 and c != 0:
                    print("| ", end ="")
                if c == 8:
                    print(str(q[d][c]))
                else:
                    print(str(q[d][c]) + " ", end = "")

    Thử in với cau_do ở phần đầu, chúng ta được kết quả như sau, ở đây tôi dùng SublimeText để code:

    thuật toán giải sudoku

    2.2. Viết hàm tìm các ô trống trong Sudoku

    Mục tiêu của chúng ta là tìm các vị trí ô trống trong câu đố q

    def tim_o_trong(q):
        for d in range(len(q)):
            for c in range(len(q[0])):
                if q[d][c] == 0:
                    return d, c
        return None

    2.3.  Viết hàm kiểm tra tính hợp lệ của một câu đố

    def kiem_tra(q, gia_tri, dong, cot):
        for i in range(len(q[0])):
            if q[i][cot] == gia_tri and i != dong:
                return False
        for i in range(len(q)):
            if q[dong][i] == gia_tri and i != cot:
                return False
    
        x = cot // 3
        y = dong // 3
    
        for i in range(y*3, y*3+3):
            for j in range(x*3, x*3+3):
                if q[i][j] == gia_tri and i != dong and j != cot:
                    return False
        return True

    2.4. Viết hàm chính để tìm lời giải cho một câu đố Sudoku

    def giai(q):
        tim_thay = tim_o_trong(q)
        if not tim_thay:
            return True
        else:
            d, c = tim_thay
        for i in range(1,10):
            if kiem_tra(q, i, d, c):
                q[d][c] = i
                if giai(q):
                    return True
                else:
                    q[d][c] = 0
            
        return False

    3. Chương trình Python giải Sudoku hoàn chỉnh

    cau_do = [
        [7,8,0,4,0,0,1,2,0],
        [6,0,0,0,7,5,0,0,9],
        [0,0,0,6,0,1,0,7,8],
        [0,0,7,0,4,0,2,6,0],
        [0,0,1,0,5,0,9,3,0],
        [9,0,4,0,6,0,0,0,5],
        [0,7,0,3,0,0,0,1,2],
        [1,2,0,0,0,7,4,0,0],
        [0,4,9,2,0,6,0,0,7]
    ]
    
    def in_sudoku(q):
        for d in range(len(q)):
            if d % 3 == 0 and d != 0:
                print("- - - - - - - - - - -")
            for c in range(len(q[0])):
                if c % 3 == 0 and c != 0:
                    print("| ", end ="")
                if c == 8:
                    print(str(q[d][c]))
                else:
                    print(str(q[d][c]) + " ", end = "")
    
    
    def giai(q):
        tim_thay = tim_o_trong(q)
        if not tim_thay:
            return True
        else:
            d, c = tim_thay
        for i in range(1,10):
            if kiem_tra(q, i, d, c):
                q[d][c] = i
                if giai(q):
                    return True
                else:
                    q[d][c] = 0
            
        return False
            
        
    
    def tim_o_trong(q):
        for d in range(len(q)):
            for c in range(len(q[0])):
                if q[d][c] == 0:
                    return d, c
        return None
    
    
    def kiem_tra(q, gia_tri, dong, cot):
        for i in range(len(q[0])):
            if q[i][cot] == gia_tri and i != dong:
                return False
        for i in range(len(q)):
            if q[dong][i] == gia_tri and i != cot:
                return False
    
        x = cot // 3
        y = dong // 3
    
        for i in range(y*3, y*3+3):
            for j in range(x*3, x*3+3):
                if q[i][j] == gia_tri and i != dong and j != cot:
                    return False
        return True
    
    in_sudoku(cau_do)
    giai(cau_do)
    print('Loi giai cua Sudoku tren la:')
    in_sudoku(cau_do)

    Cho chạy chương trình, chúng ta được kết quả như hình sau:

    giải sudoku bằng Python

  • Thuật toán sinh các dãy nhị phân có độ dài n

    Thuật toán sinh các dãy nhị phân có độ dài n

    Thuật toán sinh các dãy nhị phân có độ dài n

    Trong bài viết này chúng ta sẽ tìm cách liệt kê toàn bộ các dãy nhị phân có độ dài n cho trước.

    • Yêu cầu: Liệt kê tất cả các dãy nhị phân có độ dài n. (dãy nhị phân là dãy chỉ gồm hai số 01)
    • Input: Nhập vào n – độ dài của chuỗi nhị phân cần in ra.
    • Output : In ra màn hình tất cả các chuỗi nhị phân có độ dài n nhập từ đầu vào.

    Ví dụ:

    • Input: 3
    • Output:
    • 000   001   010   011   100   101   110   111

    1. Liệt kê các dãy nhị phân độ dài n bằng phương pháp sinh tuần tự

    Phương pháp sinh tuần tự sử dụng để liệt kê tất cả các phương án [cấu hình] của bài toán tổ hợp, sao cho các bài toán đó thỏa mãn:

    • Có thể xác định được một thứ tự trên tập các cấu hình tổ hợp cần liệt kê. Từ đó có thể biết đượccấu hình đầu tiên và cấu hình cuối cùng trong thứ tự đó.
    • Xây dựng được thuật toán từ một cấu hình chưa phải cấu hình cuối, sinh ra được cấu hình kế tiếp nó.

    Theo đó, thuật toán sinh tuần tự các cấu hình được mô tả như sau:

    <Xây dựng cấu hình đầu tiên>
    while True:
       <Đưa ra cấu hình đang có>
       <Từ cấu hình đang có sinh ra cấu hình kế tiếp nếu còn>
       if <hết cấu hình>:
          break

    Lưu ý rằng, Python không có câu lệnh điều khiển luồn repeat... until như một số ngôn ngữ khác, nên ta phải sử dụng vòng lặp while (Xem chi tiết trong bài Bài 7. Câu lệnh vòng lặp while trong Python)

    Vì kiểu xâu string là immutable nên chúng ta sẽ sử dụng kiểu danh sách list để thao tác trên đó, và định nghĩa hàm fine_print() để in toàn bộ các phần tử của danh sách dưới dạng xâu kí tự.

    Một lưu ý là các chỉ số index trong Python luôn được đánh số từ 0, do đó phần tử thứ nhất của một danh sách list x sẽ có chỉ số là 0, tức là phần tử x[0]

    n = 3
    s = n*[0]
    
    def fill_char(s,i):
       for j in range(i,n):
          s[j] = 0
    def fine_print(x):
       tmp = ''
       for i in x:
          tmp += str(i)
       return tmp
    
    
    while True:
       print(fine_print(s))
       i = n - 1
       while (i > -1) and s[i] == 1:
          i = i - 1
       s[i] = 1
       fill_char(s,i+1)
       if i == -1:
          break

    Kết quả thu được như sau:

    000
    001
    010
    011
    100
    101
    110
    111

    2. Sinh các dãy nhị phân độ dài n bằng thuật toán quay lui (Backtracking)

    liệt kê các chuỗi nhị phân bằng thuật toán quay lui

    Vẫn sử dụng kiểu danh sách list và hàm fine_print() như ở phần trên, chúng ta có chương trình liệt kê tất cả các xâu nhị phân có độ dài n sử dụng thuật toán quay lui như sau:

    n = 3
    x = n*[0]
    
    
    def fine_print(x):
       tmp = ''
       for i in x:
          tmp += str(i)
       return tmp
    
       
    def bin_gen(i):
       for j in range(0,2):
          x[i] = j
          if i == n-1:
             print(fine_print(x))
          else:
             bin_gen(i+1)
    
    bin_gen(0)

    Thuật toán quay lui có ưu điểm là rất dễ cài đặt so với phương pháp sinh tuần tự, hơn nữa có thể dễ dàng mở rộng để giải quyết nhiều bài toán khác nhau. Chẳng hạn, ta dễ dàng giải quyết bài toán sinh các dãy tam phân bằng cách thay range(0,2) bởi range(0,3). Kết quả thu được như sau:

    000
    001
    002
    010
    011
    012
    020
    021
    022
    100
    101
    102
    110
    111
    112
    120
    121
    122
    200
    201
    202
    210
    211
    212
    220
    221
    222

     

  • Python Arrays – Python Lists

    Python Arrays – Python Lists

    Python Arrays – Python Lists

    Does python have an array?

    • Arrays are used to store multiple values in one single variable.
    • Python does not have built-in support for Arrays, but Python Lists can be used instead.

    What is Python Lists/Python Arrays?

    Python Lists are used to store multiple items in a single variable. For example, fruits_list has 4 items apple, banana, cherry, orange

    fruits_list = ["apple", "banana", "cherry", "orange"]

    Lists are one of 4 built-in data types in Python used to store collections of data, the other 3 are Tuple, Set, and Dictionary, all with different qualities and usage.

    Lists are created using square brackets []

    Python List/Array Methods

    Python has a set of built-in methods that you can use on lists/arrays.

    Method Description
    append() Adds an element at the end of the list
    clear() Removes all the elements from the list
    copy() Returns a copy of the list
    count() Returns the number of elements with the specified value
    extend() Add the elements of a list (or any iterable), to the end of the current list
    index() Returns the index of the first element with the specified value
    insert() Adds an element at the specified position
    pop() Removes the element at the specified position
    remove() Removes the first item with the specified value
    reverse() Reverses the order of the list
    sort() Sorts the list

    Python Lists/Python Arrays Items

    • List items are ordered, changeable, and allow duplicate values.
    • List items are indexed, the first item has index [0], the second item has index [1] etc. For example, fruits_list = ["apple", "banana", "cherry", "orange"] then fruits_list[0]="apple", fruits_list[1]=banana

    Ordered

    • When we say that lists are ordered, it means that the items have a defined order, and that order will not change.
    • If you add new items to a list, the new items will be placed at the end of the list.
    • Note: There are some list methods that will change the order, but in general: the order of the items will not change.
    • Changeable
    • The list is changeable, meaning that we can change, add, and remove items in a list after it has been created.

    Python List Items Allow Duplicates

    • Since lists are indexed, lists can have items with the same value:
    • Example

    fruits_list = ["apple", "banana", "cherry", "apple", "cherry"]

     

    Python List Length

    • To determine how many items a list has, use the len() function:

    Example

    Print the number of items in the list:

    fruits_list = ["apple", "banana", "cherry", "apple", "cherry"]
    len(fruits_list)

    List Items – Data Types

    • List items can be of any data type.

    Example: String, int and boolean data types:

    list1 = ["apple", "banana", "cherry"]
    list2 = [1, 5, 7, 9, 3]
    list3 = [True, False, False]
    • A list can contain different data types:

    Example: A list with strings, integers and boolean values:

    my_list = ["abc", 34, True, 40, "male"]

    type()

    • From Python’s perspective, lists are defined as objects with the data type ‘list’:
    • <class ‘list’>
    • Example: What is the data type of a list?

    mylist = ["apple", "banana", "cherry"]
    print(type(mylist))

    The list() Constructor

    • It is also possible to use the list() constructor when creating a new list.
    • Example

      Using the list() constructor to make a List:

      thislist = list(("apple", "banana", "cherry")) # note the double round-brackets
      print(thislist)

    Python List of Lists (Python List in list)

    A list of lists in Python is a list object where each list element is a list by itself. Create a list of list in Python by using the square bracket notation to create a nested list [[1, 2, 3], [4, 5, 6], [7, 8, 9]].

    Create a List of Lists in Python

    Create a list of lists by using the square bracket notation. For example, to create a list of lists of integer values, use [[1, 2], [3, 4]]. Each list element of the outer list is a nested list itself.

    Convert List of Lists to One List

    Say, you want to convert a list of lists [[1, 2], [3, 4]] into a single list [1, 2, 3, 4]. How to achieve this? There are different options:

    • List comprehension [x for l in lst for x in l] assuming you have a list of lists lst.
    • Unpacking [*lst[0], *lst[1]] assuming you have a list of two lists lst.
    • Using the extend() method of Python lists to extend all lists in the list of lists.

    Find examples of all three methods in the following code snippet:

    lst = [[1, 2], [3, 4]]
    
    # Method 1: List Comprehension
    flat_1 = [x for l in lst for x in l]
    
    # Method 2: Unpacking
    flat_2 = [*lst[0], *lst[1]]
    
    # Method 3: Extend Method
    flat_3 = []
    for l in lst:
    flat_3.extend(l)
    
    ## Check results:
    print(flat_1)
    # [1, 2, 3, 4]
    
    print(flat_2)
    # [1, 2, 3, 4]
    
    print(flat_3)
    # [1, 2, 3, 4]

    Due its simplicity and efficiency, the first list comprehension method is superior to the other two methods.

  • Python Get input from Console

    Python Get input from Console

    Python Get input from Console

    How to prompt for user input and read command-line arguments. Python user input from the keyboard can be read using the input() built-in function. The input from the user is read as a string and can be assigned to a variable. After entering the value from the keyboard, we have to press the “Enter” button. Then the input() function reads the value entered by the user.

    See more:

    1. What is Console in Python?

    Console (also called Shell) is basically a command-line interpreter that takes input from the user i.e one command at a time and interprets it. If it is error free then it runs the command and gives required output otherwise shows the error message. A Python Console looks like this.

    python get input from console

    2. Python Get input from Console

    To read user input you can try the cmd module for easily creating a mini-command line interpreter (with help texts and autocompletion) and raw_input (input for Python 3+) for reading a line of text from the user.

    text = raw_input("prompt") # Python 2 
    text = input("prompt") # Python 3

    There are two modules for parsing command line options: optparse (deprecated since Python 2.7, use argparse instead) and getopt. If you just want to input files to your script, behold the power of fileinput.

    Note that, raw_input is no longer available in Python 3.x. But raw_input was renamed input, so the same functionality exists.

    3. String and Numeric input

    The input() function, by default, will convert all the information it receives into a string.  Numbers, on the other hand, need to be explicitly handled as such since they come in as strings originally.

    To converts the string into a integer, you can use int()function. Converts the string into decimal format, use float()function.

    user_input1 = input ("Enter a integer number: ")
    int_number = int(user_input1)
    print ("The number you entered is: ", input_number)
    
    user_input2 = input ("Enter a decimal number: ") 
    float_number = float(user_input2)
    print ("The number you entered is: ", float_number)

    Another way to do the same thing is as follows:

    int_number = int(input ("Enter a integer number: "))
    print ("The number you entered is: ", input_number)
    
    float_number = float(input ("Enter a decimal number: "))
    print ("The number you entered is: ", float_number)
  • Python Swap Two Variables

    Python Swap Two Variables

    Python Swap Two Variables

    In computer programming, swapping two variables specifies the mutual exchange of values of the variables. It is generally done by using a temporary variable.

    See more: Python Solve Quadratic Equation

    For example, before swapping: var_1 = 1  and var_2 = 5. After swapping, var_1 = 5 and var_2 = 1.

    Python Swap Two Variables Using Temporary Variable

    We use a temporary variable temp to store the value of var_1, then assign the value of var_2 to var_1. Finally, we assign the value of temp to var_2.

    var_1 = input('The fisrt variable = ')
    var_2 = input('The second variable = ')
    
    temp = var_1
    var_1 = var_2
    var_2 = temp
    
    print(var_1, var_2)
    
    input()

    Python Swap Two Variables Using Assign Multiple Variables

    var_1 = input('The fisrt variable = ')
    var_2 = input('The second variable = ')
    
    var_1, var_2 = var_2, var_1
    
    print(var_1, var_2)
    
    input()

    Python Swap Two Numbers Without Using a Temporary Variable

    When the type of two variable is number (integer, float…) we can swap them without using a temporary variable.

    var_1 = float(input('The fisrt variable = '))
    var_2 = float(input('The second variable = '))
    
    var_1 = var_1 + var_2
    var_2 = var_1 - var_2
    var_1 = var_1 - var_2
    
    print(var_1, var_2)
    input()

     

  • Python Solve Quadratic Equation

    Python Solve Quadratic Equation

    Python Solve Quadratic Equation

    Given a quadratic equation the task is solve the equation or find out the roots of the equation. Standard form of quadratic equation is $$ax^2+ bx + c =0$$ where, $a, b$, and $c$ are coefficient and real numbers and also $a \ne 0$ 0. If $a$ is equal to $0$ that equation is not valid quadratic equation.

    See more: Hướng dẫn lập trình Python – Python Guide

    1. Python Solve Quadratic Equation Using the Direct Formula

    Using the below quadratic formula we can find the root of the quadratic equation.

    Let $\Delta =b^2-4ac$, then:

    • If $b^2 – 4ac<0$, then roots are complex (not real).
    • If $b^2 – 4ac=0$, then roots are real and both roots are same $x=\frac{-b}{2a}$.
    • If $b^2 – 4ac>0$, then roots are real and different $$ x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}.$$
    # Python program to find roots of quadratic equation 
    import math 
    
    
    # function for finding roots 
    def equationroots(): 
    
       a = float(input('Enter coefficient a: '))
       while a == 0:
          print("Coefficient a can not equal 0")
          a = float(input('Enter coefficient a: '))
       b = float(input('Enter coefficient b: '))
       c = float(input('Enter coefficient c: '))
       
       # calculating dcriminant using formula 
       d = b * b - 4 * a * c 
       
       # checking condition for dcriminant 
       if d > 0: 
          print("Your equation has real and different roots:") 
          print((-b + math.sqrt(d))/(2 * a)) 
          print((-b - math.sqrt(d))/(2 * a)) 
       
       elif d == 0: 
          print("Your equation has real and same roots:") 
          print(-b / (2 * a)) 
       
       # when dcriminant is less than 0 
       else: 
          print("Your equation has complex roots:") 
          print(- b / (2 * a), " +", math.sqrt(-d),'i') 
          print(- b / (2 * a), " -", math.sqrt(-d),'i') 
    
    
    equationroots() 
    

    2. Python Solve Quadratic Equation Using the Complex Math Module

    First, we have to calculate the discriminant and then find two solution of quadratic equation using cmath module.

    cmath module — Mathematical functions for complex numbers — provides access to mathematical functions for complex numbers. The functions in this module accept integers, floating-point numbers or complex numbers as arguments. They will also accept any Python object that has either a __complex__() or a __float__() method: these methods are used to convert the object to a complex or floating-point number, respectively, and the function is then applied to the result of the conversion.

    # Python program to find roots of quadratic equation 
    import cmath 
    
    
    # function for finding roots 
    def equationroots(): 
    
       a = float(input('Enter coefficient a: '))
       while a == 0:
          print("Coefficient a can not equal 0")
          a = float(input('Enter coefficient a: '))
       b = float(input('Enter coefficient b: '))
       c = float(input('Enter coefficient c: '))
       
       # calculating dcriminant using formula 
       d = b * b - 4 * a * c 
       
       if d == 0: 
          print("Your equation has real and same roots:") 
          print(-b / (2 * a)) 
       
       # when dcriminant is not equal 0 
       else: 
          print("Your equation has complex roots:") 
          print(- b / (2 * a), " +", cmath.sqrt(d)) 
          print(- b / (2 * a), " -", cmath.sqrt(d)) 
    
    
    equationroots() 
    
    input()
    

     

  • Xác suất có điều kiện – Công thức Bayes

    Xác suất có điều kiện – Công thức Bayes

    xác suất có điều kiện, định lý công thức bayes

    Xác suất có điều kiện – Công thức Bayes

    Công thức tính Xác suất có điều kiệnđịnh lý Bayes (Bayes’ Theorem) là các công cụ mạnh mẽ để tính xác suất xảy ra của một sự kiện (biến cố – event) ngẫu nhiên A khi biết sự kiện liên quan B đã xảy ra.

    Mời xem thêm bài tập xác suất thống kê của ĐH Bách Khoa HN:

    1. Xác suất có điều kiện

    1.1. Ví dụ về xác suất có điều kiện

    Xác suất có điều kiện (Conditional probability) là xác suất của một biến cố $ A$ nào đó khi biết rằng một biến cố $ B$ khác xảy ra. Ký hiệu $ \mathrm{P}(A|B)$, và đọc là “xác suất của $ A$, biết $ B$”.

    Chẳng hạn, rút một lá bài từ một bộ bài có $52$ lá, xác suất để lấy được một lá Át là $ 1/52$. Nhưng nếu người chơi đã rút được lá Át rồi, nếu tiếp tục rút thêm một lá bài nữa thì thì để nhận được một lá Át nữa, xác suất chỉ còn là $ 1/51.$

    Để hiểu rõ hơn, chúng ta xét tiếp các ví dụ nữa.

    Ví dụ 1. Một bình đựng 5 viên bi kích thước và chất liệu giống nhau, chỉ khác nhau về màu sắc. Trong đó có 3 viên bi xanh và 2 viên bi đỏ. Lấy ngẫu nhiên từ bình ra một viên bi ta được viên bi màu xanh, rồi lại lấy ngẫu nhiên ra một viên bi nữa. Tính xác suất để lấy được viên bi đỏ ở lần thứ hai.

    Hướng dẫn. Gọi $ A$ là biến cố: “Lấy được một viên bi đỏ ở lần thứ hai”. Vì một viên bi xanh đã được lấy ra ở lần thứ nhất nên còn lại trong bình 4 viên bi trong đó số viên bi đỏ là 2 và số viên bi xanh cũng là 2. Do đó, xác suất cần tìm là $$ \mathrm{P}(A)=\frac{2}{4}=0{,}5. $$

    Ví dụ 2. Gieo một con xúc xắc cân đối và đồng chất hai lần. Tính xác suất để lần đầu gieo được mặt 1 chấm biết rằng tổng số chấm trong hai lần gieo không vượt quá 3.

    Xác suất có điều kiện - Công thức Bayes 1

    Hướng dẫn. Không gian mẫu là $$ \Omega=\big\{\left(i, j\right): 1\leqslant i, j\leqslant 6\big\}, $$ trong đó cặp số $ \left(i, j\right)$ thể hiện việc lần gieo đầu xuất hiện mặt $ i$ chấm, lần sau xuất hiện mặt $ j$ chấm. Không gian mẫu có tất cả $6\times 6=36$ phần tử.

    Gọi $ A$ là biến cố: “Lần đầu gieo xuất hiện mặt 1 chấm”, $ B$ là biến cố: “Tổng số chấm trong hai lần gieo không vượt quá 3”. Chúng ta dễ dàng liệt kê được các phần tử thuận lợi cho từng biến cố là
    \begin{align}
    A=&\big\{\left(1, 1\right), \left(1, 2\right), \left(1, 3\right), \left(1, 4\right), \left(1, 5\right), \left(1, 6\right)\big\},\\
    B=&\big\{\left(1, 1\right), \left(1, 2\right), \left(2, 1\right)\big\},\\
    AB=&\big\{\left(1, 1\right), \left(1, 2\right)\big\}.
    \end{align}

    Dễ dàng đếm được số phần tử của $A,B,AB$ lần lượt là $6$, $3$, $2$. Do đó, theo định nghĩa cổ điển của xác suất thì ta có

    $$ \mathrm{P}(A)=\frac{6}{36}, \quad \mathrm{P}(B)=\frac{3}{36},\quad \mathrm{P}(AB)=\frac{2}{36} .$$

    Nếu biết rằng $ B$ đã xảy ra thì $ A$ xảy ra khi một trong hai kết quả $ \left(1, 1\right)$ và $ \left(1, 2\right)$ xảy ra. Do đó, xác suất của $ A$ với điều kiện $ B$ là $$ \mathrm{P}(A | B)=\frac{2}{3}. $$ Nhận xét rằng $$ \frac{2}{3}=\frac{{2}/{36}}{{3}/{36}}=\frac{\mathrm{P}(AB)}{\mathrm{P}(B)} $$ hay chính là $$ \mathrm{P}(A | B)=\frac{\mathrm{P}(AB)}{\mathrm{P}(B)}. $$ Từ đó, chúng ta có công thức tính xác suất có điều kiện như sau đây.

    Nếu bài viết hữu ích, bạn có thể  tặng tôi 1 cốc cafe vào số tài khoản Agribank 3205 215 033 513.  Xin cảm ơn!

    1.2. Công thức tính xác suất có điều kiện

    Giả sử số các kết quả đồng khả năng có thể xảy ra khi thực hiện phép thử đó là $ N$, số kết quả thuận lợi cho biến cố $ B$ là $ m$ và số kết quả thuận lợi cho biến cố $ AB$ là $n $.

    Theo định nghĩa cổ điển của xác suất thì $$ \mathrm{P}(B)=\frac{m}{N}, \mathrm{P}(AB)=\frac{n}{N}. $$

    Khi biến cố $ B$ đã xảy ra thì số các kết quả đồng khả năng của phép thử có thể xảy ra đối với biến cố $ A$ là $ m$, trong đó có $ n$ kết quả thuận lợi cho $ A$ xảy ra. Do đó, xác suất của biến cố $ A$ khi biết $ B$ đã xảy ra là
    $$ \mathrm{P}(A | B)=\frac{n}{m}=\frac{n/N}{m/N}=\frac{\mathrm{P}(AB)}{\mathrm{P}(B)}. $$

    Từ đó, chúng ta có công thức tính xác suất có điều kiện như sau:

    Xác suất có điều kiện của biến cố $ A$ với điều kiện $ B$ là một số được ký hiệu là $ \mathrm{P}(A | B)$ xác định bởi công thức $$ \mathrm{P}(A | B)=\frac{\mathrm{P}(AB)}{\mathrm{P}(B)}, \mathrm{P}(B)>0. $$

    Từ định nghĩa trên ta dễ dàng nhận được các tính chất sau của xác suất có điều kiện:

    • $ \mathrm{P}(A | B)\geqslant 0.$
    • $ \mathrm{P}\left(\Omega | B\right)=\mathrm{P}\left(B | B\right)=1.$
    • Nếu $ A_1, A_2,\ldots, A_n$ là các biến cố xung khắc từng đôi một, nghĩa là $ A_iA_j=\varnothing$ với mọi $ i\neq j$, ta có $$ \mathrm{P} \left( \left(\bigcup\limits_{i=1}^{n}A_i \right) \Bigg| B \right)=\sum\limits_{i=1}^{n}\mathrm{P}\left(A_i | B\right). $$

    Ví dụ 3. Gieo đồng thời ba con xúc xắc cân đối đồng chất. Tính xác suất để tổng số chấm xuất hiện trên ba con bằng 8 biết rằng ít nhất có một con xuất hiện mặt 5 chấm.

    Hướng dẫn. Không gian mẫu gồm các phần tử $$ \Omega=\big\{\left(i, j, k\right): 1\leqslant i, j, k\leqslant 6\big\}, $$ trong đó bộ số $ \left(i, j, k\right)$ kí hiệu cho việc “con xúc xắc thứ nhất xuất hiện mặt $ i$ chấm, con xúc xắc thứ hai xuất hiện mặt $ j$ chấm và con xúc xắc thứ ba xuất hiện mặt $ k$ chấm”.

    Gọi $ A$ là biến cố: “Tổng số chấm xuất hiện trên ba con xúc xắc bằng 8”, $ B$ là biến cố: “Ít nhất một con xúc xắc ra 5 chấm”. Ta có $$ \mathrm{P}(A | B)=\frac{\mathrm{P}(AB)}{\mathrm{P}(B)}. $$

    Vì $ B$ là biến cố: “Ít nhất một con xúc xắc xuất hiện mặt 5 chấm” nên $ \overline{B}$ là biến cố: “Không có con xúc xắc nào xuất hiện mặt 5 chấm”, do đó $$ \overline{B}=\big\{\left(i, j, k\right): 1\leqslant i, j, k\leqslant 6, i, j, k\neq 5\big\}. $$

    Suy ra $$ \mathrm{P}(\overline{B})=\frac{|\overline{B}|}{|\Omega|}=\frac{5^3}{6^3}. $$

    Do đó $$ \mathrm{P}(B)=1-\mathrm{P}(\overline{B})=1-\frac{5^3}{6^3}=\frac{91}{216}. $$

    Ta thấy $ AB$ là biến cố: “Tổng số chấm xuất hiện trên ba con xúc xắc bằng 8 và ít nhất một con xúc xắc ra 5 chấm”, do đó $$ AB=\big\{\left(1, 2, 5\right), \left(1, 5, 2\right), \left(2, 1, 5\right), \left(2, 5, 1\right), \left(5, 1, 2\right),\left(5, 2, 1\right)\big\}. $$

    Suy ra $$ \mathrm{P}(AB)=\frac{|AB|}{|\Omega|}=\frac{15}{6^3}=\frac{15}{216}. $$

    Vậy xác suất cần tìm là $$ \mathrm{P}(A | B)=\frac{\mathrm{P}(AB)}{\mathrm{P}(B)}=\frac{{15}/{216}}{{91}/{216}}=\frac{15}{91}. $$

    Ví dụ 4. Một gia đình có 2 đứa trẻ. Biết rằng có ít nhất 1 đứa trẻ là con gái. Hỏi xác suất 2 đứa trẻ đều là con gái là bao nhiêu?

    Hướng dẫn. Chúng ta có các nhận xét sau:

    • Xác suất để một đứa trẻ là trai hoặc gái là bằng nhau và bằng $ 1/2$.
    • Giới tính cả 2 đứa trẻ là ngẫu nhiên và không liên quan đến nhau.

    Lời giải. Do gia đình có 2 đứa trẻ nên sẽ có thể xảy ra 4 khả năng:

    (trai, trai), (gái, gái), (gái, trai), (trai, gái).

    Gọi $ A$ là biến cố “Cả hai đứa trẻ đều là con gái” và $ B$ là biến cố “Có ít nhất một đứa trẻ là con gái” thì có $$ \mathrm{P}(A)=\frac{1}{4},\quad \mathrm{P}(B)=\frac{3}{4}. $$
    Do nếu xảy ra $ A$ thì đương nhiên sẽ xảy ra $ B$ nên ta có: $$ \mathrm{P}(AB) = \mathrm{P}(A) =\frac{1}{4}. $$
    Suy ra, xác suất để cả hai đứa trẻ đều là con gái khi biết ít nhất có một đứa trẻ là gái là
    $$\mathrm{P}(A | B) = \dfrac{\mathrm{P}\left(A,B\right)}{\mathrm{P}(B)} = \dfrac{{1}/{4}}{{3}/{4}} = \frac{1}{3}.$$
    Bằng trực quan ta cũng có thể nhìn ra xác suất này. Khi biết một đứa trẻ là gái, giới tính của 2 đứa trẻ sẽ có 3 khả năng: (trai, gái), (gái, trai), (gái, gái).

    Ví dụ 5. Một hộp chứa 8 bi trắng, 2 bi đỏ. Lần lượt bốc từng bi. Giả sử lần đầu tiên bốc được bi trắng. Xác định xác suất
    lần thứ 2 bốc được bi đỏ.

    Hướng dẫn. Gọi $ B$ là biến cố lần 1 bốc được bi trắng, $ A$ là biến cố lần 2 bốc được bi đỏ. Xác suất lần 2 bốc được bi đỏ khi lần 1 đã bốc được bi trắng là $$ \mathrm{P}(A | B)=\frac{\mathrm{P}(AB)}{\mathrm{P}(B)}=\frac{8/10\times 2/9}{8/10}=\frac{2}{9}. $$

    2. Công thức nhân xác suất

    2.1. Công thức nhân xác suất

    Từ công thức tính xác suất có điều kiện $$ \mathrm{P}(A | B)=\frac{\mathrm{P}(AB)}{\mathrm{P}(B)}, \quad \mathrm{P}(B|A)=\frac{\mathrm{P}(AB)}{\mathrm{P}(A)},$$ ta suy ra công thức nhân xác suất $$ \mathrm{P}(AB)=\mathrm{P}(B)\mathrm{P}(A | B)=\mathrm{P}(A)\mathrm{P}\left(B | A\right), $$ với $ \mathrm{P}(A)>0{,} \mathrm{P}(B)>0$.

    Công thức nhân xác suất sử dụng trong một số trường hợp, khi mà chúng ta có thể biết ngay xác suất $ \mathrm{P}\left(B | A\right)$ hoặc $ \mathrm{P}(A | B)$ thì sẽ tính được xác suất $ \mathrm{P}(AB)$.

    Ví dụ 1. Trong hộp có 20 nắp chai bia Tiger, trong đó có 2 nắp ghi “Chúc mừng bạn đã trúng thưởng”. Bạn được chọn lên rút thăm lần lượt hai nắp chai bia, tính xác suất để cả hai nắp đều trúng thưởng.

    Hướng dẫn. Gọi $ A$ là biến cố “nắp chai bia thứ nhất trúng thưởng”, $ B$ là biến cố “nắp chai bia thứ hai trúng thưởng”, $ C$ là biến cố “cả 2 nắp đều trúng thưởng”.

    Khi bạn rút thăm lần đầu thì trong hộp có 20 nắp trong đó có 2 nắp trúng nên $$ \mathrm{P}(A)=\frac{2}{20}. $$

    Khi biến cố $ A$ đã xảy ra thì còn lại 19 nắp trong đó có 1 nắp trúng thưởng. Do đó $$ \mathrm{P}\left(B/A\right) = \frac{1}{19}. $$
    Suy ra, xác suất để cả hai nắp đều trúng thưởng là $$ \mathrm{P}\left(C\right) = \mathrm{P}(A). \mathrm{P}\left(B/A\right) = \frac{2/20}{1/19} = \frac{1}{190} \approx 0{,}0053. $$

    Ví dụ 2. Một bình đựng 5 viên bi kích thước, chất liệu như nhau, trong đó có 3 viên bi xanh và 2 viên bi trắng. Lấy ngẫu nhiên ra một viên bi, rồi lại lấy ngẫu nhiên ra một viên bi nữa. Tính xác suất để lấy được một viên bi xanh ở lần thứ nhất và một viên bi trắng ở lần thứ hai.

    Hướng dẫn. Gọi $ A$ là biến cố: “Lấy được một viên bi xanh ở lần thứ nhất”, $ B$ là biến cố: “Lấy được một viên bi trắng ở lần thứ hai”. Chúng ta cần tính xác suất $ \mathrm{P}(AB)$.

    Theo công thức nhân xác suất $$ \mathrm{P}(AB)=\mathrm{P}(A)\mathrm{P}\left(B | A\right). $$
    Vì có 3 viên bi xanh trong tổng số 5 viên bi nên $$ \mathrm{P}(A)=\frac{3}{5}=0{,}6. $$
    Nếu $ A$ đã xảy ra, tức là một viên bi xanh đã được lấy ra ở lần thứ nhất, thì còn lại trong bình 4 viên bi trong đó số viên bi trắng là 2, do đó $$ \mathrm{P}\left(B | A\right)=\frac{2}{4}=0{,}5. $$
    Vậy xác suất cần tìm là $$ \mathrm{P}(AB)=\mathrm{P}(A)\mathrm{P}\left(B | A\right)=0{,}6\times 0{,}5=0{,}3. $$

    2.2. Công thức nhân xác suất tổng quát

    Bằng phương pháp quy nạp, ta có công thức nhân xác suất tổng quát sau:

    Giả sử $ n\geqslant 2$ và $ A_1, A_2,\ldots, A_n$ là các biến cố sao cho $ \mathrm{P}\left(A_1A_2\ldots A_{n-1}\right)>0$. Khi đó ta có $$ \mathrm{P}\left(A_1A_2\ldots A_{n}\right)=\mathrm{P}\left(A_1\right)\mathrm{P}\left(A_2 | A_1\right)\mathrm{P}\left(A_3 | A_1A_2\right)\ldots\mathrm{P}\left(A_n | A_1A_2\ldots A_{n-1}\right). $$

    Ví dụ 3. Một thủ kho có một chùm chìa khóa gồm 9 chiếc bề ngoài giống hệt nhau trong đó chỉ có hai chiếc mở được cửa kho. Anh ta thử ngẫu nhiên từng chìa (chìa nào không đúng thì bỏ ra khỏi chùm chìa khóa). Tìm xác suất để lần thử thứ ba thì anh ta mới mở được cửa.

    Hướng dẫn. Gọi $ A_1$ là biến cố: “Không mở được cửa ở lần thử thứ 1”, $ A_2$ là biến cố: “Không mở được cửa ở lần thử thứ 2” và $ A_3$ là biến cố: “Mở được cửa ở lần thử thứ 3”. Ta phải tìm $ \mathrm{P}\left(A_1A_2A_3\right)$. Theo công thức nhân xác suất ta có $$ \mathrm{P}\left(A_1A_2A_3\right)=\mathrm{P}\left(A_1\right)\mathrm{P}\left(A_2|A_1\right)\mathrm{P}\left(A_3|A_1A_2\right). $$

    Ta có $$ \mathrm{P}\left(A_1\right)=\frac{7}{9}, \mathrm{P}\left(A_2|A_1\right)=\frac{6}{8}, \mathrm{P}\left(A_3|A_1A_2\right)=\frac{2}{7}. $$

    Do đó $$ \mathrm{P}\left(A_1A_2A_3\right)=\frac{7}{9}\times\frac{6}{8}\times\frac{2}{7}=\frac{1}{6}. $$

    Ví dụ 4. Một người săn thỏ trong rừng, khả năng anh ta bắn trúng thỏ trong mỗi lần bắn tỷ lệ nghịch với khoảng cách bắn. Anh ta bắn lần đầu ở khoảng cách 20 m với xác suất trúng thỏ là 0,5, nếu bị trượt anh ta bắn viên thứ 2 ở khoảng cách 30 m, nếu lại trượt anh ta bắn viên thứ 3 ở khoảng cách 50 m. Tính xác suất để người thợ săn bắn được thỏ.

    Hướng dẫn. Gọi $ A_k$ là biến cố “Người thợ săn bắn trúng thỏ ở lần thứ $ k$” với $ k=1,2,3.$ Theo đề bài, chúng ta có
    \begin{align}
    \mathrm{P}\left(A_1\right)&=0{,}5,\\
    \mathrm{P}\left(A_2|\overline{A_1}\right)&=\frac{20\times 0{,}5}{30}=\frac{1}{3},\\
    \mathrm{P}\left(A_3|\overline{A_1}.\overline{A_2}\right)&=\frac{20\times 0{,}5}{50}=\frac{1}{5}.
    \end{align}
    Gọi $ A$ là biến cố “Người thợ săn bắn trúng thỏ” thì $$ A=A_1\cup \overline{A_1}A_2\cup \overline{A_1}.\overline{A_2}.A_3. $$
    Vì các biến cố $ A_1, \overline{A_1}A_2, \overline{A_1}.\overline{A_2}.A_3$ xung khắc từng đôi một, nên ta có
    $$ \mathrm{P}(A)=\mathrm{P}\left(A_1\right)+\mathrm{P}\left(\overline{A_1}A_2\right)+\mathrm{P}\left(\overline{A_1}.\overline{A_2}.A_3\right) $$
    Theo công thức nhân xác suất thì
    $$ \mathrm{P}\left(\overline{A_1}A_2\right) = \mathrm{P}\left(\overline{A_1}\right)\mathrm{P}\left(A_2|\overline{A_1}\right)=\left(1-0{,}5\right)\times \mathrm{P}\left(A_2|\overline{A_1}\right)=\frac{1}{6}. $$
    $$ \mathrm{P}\left(\overline{A_1}.\overline{A_2}.A_3\right)= \mathrm{P}\left(\overline{A_1}\right) \mathrm{P}\left(\overline{A_2}|\overline{A_1}\right)\mathrm{P}\left(A_3|\overline{A_1}.\overline{A_2}\right)=\left(1-0{,5}\right)\left(1-\frac{1}{3}\right)\times \frac{1}{5} =\frac{1}{15}.$$
    Do đó, xác suất cần tìm là $$ \mathrm{P}(A)=0{,}5+\frac{1}{6}+\frac{1}{15}=\frac{11}{15}. $$

    3. Công thức xác suất đầy đủ

    3.1. Hệ đầy đủ các biến cố

    Hệ các biến cố $ \big\{B_1, B_2,\ldots, B_n\big\}$ được gọi là đầy đủ nếu thỏa mãn đồng thời hai điều kiện:

    • $ B_1, B_2,\ldots, B_n$ là các biến cố xung khắc từng đôi một, nghĩa là $ B_iB_j=\varnothing$ với mọi $ i\neq j$,
    • $ \Omega=B_1\cup B_2\cup\cdots\cup B_n$.

    Nhận xét rằng, hệ $ \big\{B, \overline{B}\big\}$ là một hệ đầy đủ, trong đó $ B$ là một biến cố bất kỳ.

    3.2. Công thức xác suất đầy đủ

    Giả sử $ \big\{B_1, B_2,\ldots, B_n\big\}$ là hệ đầy đủ các biến cố với $ \mathrm{P}\left(B_i\right)>0,\,\forall i=1,2,\ldots,n$. Khi đó với bất kỳ biến cố $ A$, ta có $$ \mathrm{P}(A)=\mathrm{P}\left(B_1\right)\mathrm{P}\left(A | B_1\right)+\mathrm{P}\left(B_2\right)\mathrm{P}\left(A | B_2\right)+\cdots+\mathrm{P}\left(B_n\right)\mathrm{P}\left(A | B_n\right). $$

    Ví dụ 1. Có 3 hộp giống nhau. Hộp thứ nhất đựng 10 sản phẩm, trong đó có 6 chính phẩm, hộp thứ hai đựng 15 sản phẩm, trong đó có 10 chính phẩm, hộp thứ ba đựng 20 sản phẩm, trong đó có 15 chính phẩm. Lấy ngẫu nhiên một hộp và từ đó lấy ngẫu nhiên một sản phẩm. Tìm xác suất để lấy được chính phẩm.

    Hướng dẫn. Ký hiệu $ B_k$ là biến cố: “Sản phẩm lấy ra thuộc hộp thứ $ k$”, $ k=1, 2, 3$ và $ A$ là biến cố: “Lấy được chính phẩm”. Chúng ta có ngay $ \big\{B_1, B_2, B_3\big\} $là hệ đầy đủ các biến cố và

    • $ \mathrm{P}\left(B_1\right)=\frac{1}{3}, \mathrm{P}\left(B_2\right)=\frac{1}{3}, \mathrm{P}\left(B_3\right)=\frac{1}{3},$
    • $ \mathrm{P}\left(A | B_1\right)=\frac{6}{10}, \mathrm{P}\left(A | B_2\right)=\frac{10}{15}, \mathrm{P}\left(A | B_3\right)=\frac{15}{20}.$

    Theo công thức xác suất đầy đủ $$ \mathrm{P}(A)=\mathrm{P}\left(B_1\right)\mathrm{P}\left(A | B_1\right)+\mathrm{P}\left(B_2\right)\mathrm{P}\left(A | B_2\right)+\mathrm{P}\left(B_3\right)\mathrm{P}\left(A | B_3\right) $$

    Thay các giá trị tính được ở trên vào công thức này ta thu được $$ \mathrm{P}(A)=\frac{1}{3}\times \frac{6}{10}+\frac{1}{3}\times \frac{10}{15}+\frac{1}{3}\times \frac{15}{20}=\frac{31}{45} $$
    Vậy xác suất để lấy được chính phẩm là $ {31}/{45}$.

    Ví dụ 2. Từ một hộp chứa $ m$ quả cầu trắng và $ n$ quả cầu đen, người ta rút ngẫu nhiên không hoàn lại từng quả một hai lần. Tính xác suất để quả lấy lần thứ hai là trắng.

    Hướng dẫn. Ký hiệu $ A$ là biến cố: “Lần thứ hai rút được quả cầu trắng”, $ B_1$ là biến cố: “Lần thứ nhất rút được quả cầu trắng”, $ B_2$ là biến cố: “Lần thứ nhất rút được quả cầu đen”.

    Ta có

    • $ \mathrm{P}\left(B_1\right)=\frac{m}{m+n}, \mathrm{P}\left(B_2\right)=\frac{n}{m+n},$
    • $ \mathrm{P}\left(A|B_1\right)=\frac{m-1}{m+n-1}, \mathrm{P}\left(A|B_2\right)=\frac{m}{m+n-1}.$

    Vì $ \big\{B_1, B_2\big\}$ là một hệ đầy đủ nên theo công thức xác suất đầy đủ, chúng ta có \begin{align}
    \mathrm{P}(A)&=\mathrm{P}\left(B_1\right)\mathrm{P}\left(A|B_1\right)+\mathrm{P}\left(B_2\right)\mathrm{P}\left(A|B_2\right)\\
    =&\frac{m}{m+n}\times\frac{m-1}{m+n-1}+\frac{n}{m+n}\times\frac{m}{m+n-1}\\
    =&\frac{m\left(m-1\right)+mn}{\left(m+n\right)\left(m+n-1\right)}
    =&\frac{m\left(m+n-1\right)}{\left(m+n\right)\left(m+n-1\right)}
    =&\frac{m}{m+n}.
    \end{align}
    Vậy xác suất để quả lấy lần thứ hai là trắng là$ \frac{m}{m+n}$.

    Ví dụ 3. Có 10 chiếc túi đựng bi như sau:

    • 4 túi loại 1, trong mỗi túi loại 1 chứa 6 viên bi trắng và 4 viên bi đen,
    • 2 túi loại 2, trong mỗi túi loại 2 chứa 3 viên bi trắng và 7 viên bi đen,
    • 1 túi loại 3, trong mỗi túi loại 3 chứa 7 viên bi trắng và 3 viên bi đen,
    • 3 túi loại 4, trong mỗi túi loại 4 chứa 4 viên bi trắng và 6 viên bi đen.

    Chọn ngẫu nhiên 1 chiếc túi rồi lấy ngẫu nhiên 2 viên bi. Tính xác suất để lấy được hai viên bi cùng màu.

    Hướng dẫn. Ký hiệu $ B_k$ là biến cố “chọn được túi loại $ k$”, $ k=1, 2, 3, 4$ và $ A$ là biến cố “lấy được hai viên bi cùng màu”.

    Ta có $ \big\{B_1, B_2, B_3, B_4\big\} $ là hệ đầy đủ các biến cố và

    \begin{align}
    \mathrm{P}\left(B_1\right)=\frac{4}{10}, \mathrm{P}\left(B_2\right)=\frac{2}{10},\\
    \mathrm{P}\left(B_3\right)=\frac{1}{10}, \mathrm{P}\left(B_4\right)=\frac{3}{10},\\
    \mathrm{P}\left(A | B_1\right)=\frac{C_6^2+C_4^2}{C_{10}^2}=\frac{21}{45},\mathrm{P}\left(A | B_2\right)=\frac{C_3^2+C_7^2}{C_{10}^2}=\frac{24}{45},\\
    \mathrm{P}\left(A | B_3\right)=\frac{C_7^2+C_3^2}{C_{10}^2}=\frac{24}{45}, \mathrm{P}\left(A | B_4\right)=\frac{C_4^2+C_6^2}{C_{10}^2}=\frac{21}{45}.
    \end{align}

    Theo công thức xác suất đầy đủ
    $$ \mathrm{P}(A)=\mathrm{P}\left(B_1\right)\mathrm{P}\left(A | B_1\right)+\mathrm{P}\left(B_2\right)\mathrm{P}\left(A | B_2\right)+\mathrm{P}\left(B_3\right)\mathrm{P}\left(A | B_3\right)+\mathrm{P}\left(B_4\right)\mathrm{P}\left(A | B_4\right) $$

    Suy ra $$ \mathrm{P}(A)=\frac{4}{10}\times \frac{21}{45}+\frac{2}{10}\times \frac{24}{45}+\frac{1}{10}\times \frac{24}{45}+\frac{3}{10}\times \frac{21}{45} =\frac{219}{450}. $$

    Vậy xác suất cần tìm là $ \frac{219}{450}$.

    Ví dụ 4. Có hai cái hộp. Hộp thứ nhất có 4 bi trắng và 5 bi đen. Hộp thứ hai có 5 bi trắng và 4 bi đen. Chọn ngẫu nhiên 3 viên bi ở hộp thứ nhất bỏ vào hộp thứ hai rồi sau đó chọn ngẫu nhiên một viên bi ở hộp thứ hai ra. Tính xác suất để lấy được bi trắng từ hộp thứ hai.

    Hướng dẫn. Gọi $ A$ là biến cố: “Lấy được bi trắng từ hộp thứ hai”, $ B_k$ là biến cố: “Trong 3 viên bi lấy ra từ hộp thứ nhất có $ k$ bi trắng”, $ k=0, 1, 2, 3$.

    Khi đó $ \big\{B_0{,} B_1, B_1, B_3\big\} $ là hệ đầy đủ các biến cố và ta có
    \begin{align}
    \mathrm{P}\left(B_0\right)&=\frac{C_5^3}{C_9^3}=\frac{10}{84},\\
    \mathrm{P}\left(B_1\right)&=\frac{C_4^1C_5^2}{C_9^3}=\frac{40}{84},\\
    \mathrm{P}\left(B_2\right)&=\frac{C_4^2C_5^1}{C_9^3}=\frac{30}{84},\\
    \mathrm{P}\left(B_3\right)&=\frac{C_4^3}{C_9^3}=\frac{4}{84}.
    \end{align}

    Theo công thức xác suất đầy đủ
    $$ \mathrm{P}(A)=\mathrm{P}\left(B_0\right)\mathrm{P}\left(A | B_0\right)+\mathrm{P}\left(B_1\right)\mathrm{P}\left(A | B_1\right)+\mathrm{P}\left(B_2\right)\mathrm{P}\left(A | B_2\right)+\mathrm{P}\left(B_3\right)\mathrm{P}\left(A | B_3\right). $$

    Dễ thấy
    \begin{align}
    \mathrm{P}\left(A | B_0\right)=\frac{5}{12},\quad & \mathrm{P}\left(A | B_1\right)=\frac{6}{12},\\
    \mathrm{P}\left(A | B_2\right)=\frac{7}{12},\quad & \mathrm{P}\left(A | B_3\right)=\frac{8}{12}.
    \end{align}

    Thay các giá trị này vào công thức xác suất đầy đủ ta được $$ \mathrm{P}(A)=\frac{10}{84}\times\frac{5}{12}+\frac{40}{84}\times\frac{6}{12}+\frac{30}{84}\times\frac{7}{12}+\frac{4}{84}\times\frac{8}{12} =\frac{532}{1008} =\frac{19}{36}. $$

    Vậy xác suất cần tìm là $ {19}/{36}$.

    Ví dụ 5. Trong một cái hộp có $ n$ sản phẩm, ta bỏ vào cái hộp đó một sản phẩm tốt sau đó lấy ngẫu nhiên ra một sản phẩm. Tính xác suất để sản phẩm lấy ra là tốt nếu mọi giả thiết về trạng thái cấu thành ban đầu của hộp là đồng xác suất.

    Hướng dẫn. Gọi $ A$ là biến cố: “Lấy được sản phẩm tốt”, $ B_i$ là biến cố: “Lúc ban đầu hộp có $ i$ sản phẩm tốt”, $ i=0,1,\ldots,n$. Khi đó $ \big\{B_0{,} B_1,\ldots, B_n\big\} $ là hệ đầy đủ các biến cố.

    Theo giả thiết $$ \mathrm{P}\left(B_i\right)=\frac{1}{n+1}, i=0,1,\ldots,n.$$

    Ta có $ \mathrm{P}\left(A | B_i\right)=\frac{i+1}{n+1}$ với mọi $ i=0,1,\ldots,n$. Theo công thức xác suất đầy đủ

    $$ \mathrm{P}(A)=\sum\limits_{i=0}^{n}\mathrm{P}\left(B_i\right)\mathrm{P}\left(A | B_i\right). $$

    Thay vào ta được
    \begin{align}
    \mathrm{P}(A)&=\sum\limits_{i=0}^{n}\frac{i+1}{\left(n+1\right)^2}\\
    &=\frac{1+2+\cdots+\left(n+1\right)}{\left(n+1\right)^2}\\
    &=\frac{\left(n+1\right)\left(n+2\right)}{2\left(n+1\right)^2}\\
    &=\frac{n+2}{2\left(n+1\right)}.
    \end{align}

    4. Công thức Bayes – Định lý Bayes

    Giả sử $ \mathrm{P}(A)>0$ và $ \big\{B_1, B_2,\ldots, B_n\big\} $ là hệ đầy đủ các biến cố với $ \mathrm{P}\left(B_k\right)>0$ với mọi $ k=1,2,\ldots,n$. Khi đó với mọi $ k=1,2,\ldots,n$, ta có $$ \mathrm{P}\left(B_k | A\right)=\frac{\mathrm{P}\left(B_k\right)\mathrm{P}\left(A | B_k\right)}{\mathrm{P}\left(B_1\right)\mathrm{P}\left(A | B_1\right)+\mathrm{P}\left(B_2\right)\mathrm{P}\left(A | B_2\right)+\cdots+\mathrm{P}\left(B_n\right)\mathrm{P}\left(A | B_n\right)}. $$

    Ví dụ 1. Dây chuyền lắp ráp nhận được các chi tiết do hai máy sản xuất. Trung bình máy thứ nhất cung cấp 60% chi tiết, máy thứ hai cung cấp 40% chi tiết. Khoảng 90% chi tiết do máy thứ nhất sản xuất là đạt tiêu chuẩn, còn 85% chi tiết do máy thứ hai sản xuất là đạt tiêu chuẩn. Lấy ngẫu nhiên từ dây chuyền một sản phẩm, thấy nó đạt tiêu chuẩn. Tìm xác suất để sản phẩm đó do máy thứ nhất sản xuất.

    Hướng dẫn. Gọi $ A$ là biến cố: “Chi tiết lấy từ dây chuyền đạt tiêu chuẩn”, $ B_1$ là biến cố: “Chi tiết do máy thứ nhất sản xuất” và $ B_2$ là biến cố: “Chi tiết do máy thứ hai sản xuất”. Ta cần tính xác suất $ \mathrm{P}\left(B_1|A\right)$.

    Theo công thức Bayes $$ \mathrm{P}\left(B_1|A\right)=\frac{\mathrm{P}\left(B_1\right)\mathrm{P}\left(A|B_1\right)}{\mathrm{P}\left(B_1\right)\mathrm{P}\left(A|B_1\right)+\mathrm{P}\left(B_2\right)\mathrm{P}\left(A|B_2\right)}. $$

    Theo điều kiện bài toán $$ \mathrm{P}(B_1)=0{,}6; \mathrm{P}(B_2)=0{,}4; $$
    $$ \mathrm{P}(A|B_1)=0{,}9; \mathrm{P}(A|B_2)=0{,}85. $$

    Thay vào ta có $$ \mathrm{P}\left(B_1|A\right)=\frac{0{,}6\times 0{,}9}{0{,}6\times 0{,}9+0{,}4\times 0{,}85}=0{,}614. $$

    Sau đây là một bài toán khá nổi tiếng trong xác suất thống kê, được giải theo nhiều cách khác nhau. Ta hãy thử giải bài toán này bằng định lý Bayes.
    Ví dụ 2.[Bài toán Tuesday Child] Một gia đình có hai đứa trẻ. Biết có ít nhất có một đứa trẻ là con gái và sinh vào thứ 3. Hỏi xác suất 2 đứa trẻ đều là con gái là bao nhiêu?

    Hướng dẫn. Chúng ta có nhận xét sau:

    • Xác suất để một đứa trẻ sinh vào một ngày nhất định trong tuần là $ 1/7$.
    • Giới tính của đứa trẻ và ngày sinh của nó là 2 sự kiện không liên quan đến nhau.

    Ta ký hiệu các biến cố như sau:

    • $ B$ là biến cố “Ít nhất 1 đứa trẻ là con gái sinh ra vào thứ 3”,
    • $ A$ là biến cố “Cả 2 đứa trẻ đều là con gái”, xác suất là $ \mathrm{P}(A)=1/4$,
    • $ A_1$ là biến cố “Chỉ một trong 2 đứa trẻ là con gái”, $ \mathrm{P}(A_1)=1/2$,
    • $ C $ là biến cố “Đứa trẻ sinh ra vào thứ 3”, $ \mathrm{P}(C)=1/7$,
    • $ \overline{C} $ là biến cố “Đứa trẻ sinh ra vào thứ 3”, $ \mathrm{P}(\overline{C})=6/7$.

    Để sử dụng định lý Bayes tính $ \mathrm{P}(A | B)$ ta cần tính được $ \mathrm{P}(B|A)$ và $ \mathrm{P}(B)$.

    $ \mathrm{P}(B|A)$ được hiểu là xác suất ít nhất 1 đứa trẻ là con gái sinh ra vào thứ 3 nếu biết trước 2 đứa trẻ là con gái.
    Ta sẽ tính xác suất phần bù $ \mathrm{P}(\overline{B}|A)$, đây là xác suất để không có đứa trẻ nào sinh ra vào thứ 3.
    $$ \mathrm{P}(\overline{B}|A) = \mathrm{P}(\overline{C}) \mathrm{P}(\overline{C}) = \dfrac{6}{7} \times \dfrac{6}{7} = \dfrac{36}{49} $$
    Như vậy ta có
    $$ \mathrm{P}(B|A) = 1 – \mathrm{P}(\overline{B}|A) = \dfrac{13}{49} $$
    $ \mathrm{P}(B)$ là xác suất sự ít nhất 1 đứa trẻ là con gái sinh ra vào thứ 3. Sự kiện này bao gồm 2 khả năng:

    • Cả 2 đứa trẻ đều là con gái $ A$,
    • Chỉ 1 đứa trẻ là con gái $ A_1$.

    Ta có \begin{align}
    \mathrm{P}(B) &= \mathrm{P}(BA) + \mathrm{P}(BA_1) \\
    &= \mathrm{P}(B|A)\mathrm{P}(A) + \mathrm{P}(B|A_1)\mathrm{P}(A_1)\\
    &= \dfrac{13}{49} \times \dfrac{1}{4} + \dfrac{1}{7} \times \dfrac{1}{2}\\
    &=\dfrac{27}{196}
    \end{align}

    Thay vào định lý Bayes, ta tính được
    $$ \mathrm{P}(A | B) = \dfrac{\mathrm{P}(B|A)\times \mathrm{P}(A)}{\mathrm{P}(B)} = \dfrac{\tfrac{13}{49} \times \tfrac{1}{4}}{\tfrac{27}{196}} = \dfrac{13}{27} \approx 0{,}481 $$
    Chúng ta có thể minh họa bằng hình vẽ sau đây, xác suất cần tìm chính bằng số ô màu xanh chia cho tổng số ô màu vàng và xanh.

    Ta dùng một đoạn code Python nho nhỏ để kiểm tra thử kết quả vừa tính được.

    import random
    
    def random_kid():
       gender = random.choice(["boy", "girl"])
       birth_date = random.choice(["mon", "tue", "wed", "thu", "fri", "sat", "sun"])
       return (gender, birth_date)
    
    both_girls = 0
    tuesday_girl = 0
    
    random.seed(0)
    total = 100000
    for _ in range(total):
       first_child = random_kid()
       second_child = random_kid()
    
    if first_child == ("girl", "tue") or second_child == ("girl", "tue"):
       tuesday_girl += 1
    if first_child[0] == "girl" and second_child[0] == "girl":
       both_girls += 1
    
    print("both_girls = ", both_girls)
    print("tuesday_girl = ", tuesday_girl)
    print("P(both_girls|tuesday_girl) = ", both_girls / tuesday_girl)

    Đoạn code trên thực hiện random 100K dữ liệu. Thu được kết quả in ra như sau

    both_girls = 6506
    tuesday_girl = 13637
    P(both_girls|tuesday_girl) = 0.4770844027278727

    Xác suất tính ra tương đối sát với con số ta tính bằng định lý Bayes ở trên.

    Bài viết tổng hợp từ https://1upnote.me/post/2018/11/ds-ml-bayes-theorem/ và https://math4rum.wordpress.com/2013/04/01/bai-1-5-cong-thuc-xac-suat-day-du-cong-thuc-bayes/

  • Hàm trong Python

    Hàm trong Python

    Hàm trong Python

    1. Khái niệm hàm trong Python

    Trong lập trình, để thực hiện một công việc nào đó, và công việc này lặp đi lặp lại, chúng ta sử dụng hàm sẽ tiết kiệm được công sức. Chính bạn đã sử dụng một số hàm đã được xây dựng sẵn trong Python, ví dụ như hàm print(), type()

    Chẳng hạn, trong chương trình chúng ta có một nhiệm vụ là giải phương trình bậc hai năm lần, thì mỗi lần người dùng sẽ nhập vào các hệ số a, b, c từ bàn phím, chương trình sẽ giải phương trình với các hệ số đó và trả lại kết quả là vô nghiệm, có nghiệm kép hay hai nghiệm phân biệt và in các nghiệm đó. Nếu không sử dụng hàm ta phải đánh máy đoạn chương trình sau 5 lần!

    from math import sqrt
    
    a = int(input("a = "))
    b = int(input("b = "))
    c = int(input("c = "))
    d = b**2 – 4*a*c
    if d < 0:
          print("Phương trình vô nghiệm.")
    elif d == 0:
          print("Phương trình có nghiệm kép x = ", -b /(2*a))
    else:
          print("Phương trình có hai nghiệm ", (-b + sqrt(d))/(2*a), " và ",\   (-b - sqrt(d))/(2*a))

    Khi đó, chương trình trông sẽ dài dòng phức tạp, hơn nữa, mỗi lần giải một phương trình mới ta lại phải gõ lại đoạn mã  này, tính tái sử dụng không[1] có. Lúc này, chúng ta cần sử dụng đến hàm.

    2. Khai báo hàm trong Python

    Để khai báo một hàm trong Python chúng ta sử dụng từ khóa def với cú pháp:

    def name([arg,... arg=value,... *arg, **arg]):
        """Mô tả về hàm nếu (nên) có (phần này thường gọi là docstring)"""
        <khối lệnh>
        return [giá trị trả về]

    Trong đó name là tên của hàm, được đặt tên theo đúng quy tắc đặt tên và theo sau phải là cặp ngoặc tròn (). Bên trong cặp ngoặc tròn, ta có arg là các tham số nếu có, value là giá trị mặc định của tham số – nếu người dùng không cung cấp một giá trị cho tham số khi gọi hàm thì giá trị mặc định này sẽ được sử dụng, *arg**arg là các tham số kiểu tuple, tức một bộ các tham số.

    Một hàm trong Python có thể có tham số hoặc không. Cần lưu ý rằng, nếu hàm có tham số nhận các giá trị mặc định, thì các tham số này phải được khai báo sau các tham số không nhận giá trị mặc định. Chẳng hạn, khai báo hàm bậc hai y=a*x**2+b*x+c thì thường giá trị mặc định là a=1, nhưng ta không thể khai báo tham số a lên trước, mà bắt buộc phải khai báo tham số a cuối cùng, chẳng hạn

    def ham_bac_hai(b, c, a = 1):
        <khối lệnh thực thi hàm>

    Tiếp theo là dấu hai chấm : và xuống dòng để bắt đầu vào phần thân của hàm. Phần này có thể gồm chú thích mô tả về hàm để giúp cho các lập trình viên khác, và chính bản thân chúng ta sau này, hiểu rõ hơn về hàm và khối lệnh chính của hàm để thực hiện nhiệm vụ đề ra.

    Cuối cùng là từ khóa return để thoát khỏi hàm và trở về chương trình chính, hoặc trả về giá trị của hàm đối với hàm có giá trị trả về; phần này không bắt buộc phải có.

    Một hàm nếu được khai báo trong một lớp thì còn được gọi là một phương thức method của lớp.

    3. Lời gọi đến hàm trong Python

    Để gọi một hàm có tên là name đã được định nghĩa, ta sử dụng câu lệnh name(). Hãy quay trở lại ví dụ giải phương trình bậc hai để hiểu rõ hơn.

    from math import sqrt
    def giai_pt():
          a = int(input("a = "))
          b = int(input("b = "))
          c = int(input("c = "))
          d = b**2 – 4*a*c
          if d < 0:
                print("Phương trình vô nghiệm.")
          elif d == 0:
                print("Phương trình có nghiệm kép x = ", -b /(2*a))
          else:
                print("Phương trình có hai nghiệm ", (-b + sqrt(d))/(2*a),
                " và ", (-b – sqrt(d))/(2*a))

    Như vậy, hàm giai_pt ở trên không có tham số truyền vào và cũng không có kết quả trả về, bởi bản thân các lệnh trong hàm đã in ra nghiệm của phương trình rồi. Để gọi hàm này ta chỉ đơn giản là dùng câu lệnh giai_pt().

    Ở cách làm này, trong bản thân hàm đã có các câu lệnh để người dùng nhập vào giá trị của các hệ số a, b, c của phương trình. Nếu muốn truyền các hệ số khi gọi hàm giai_pt() ta có thể viết lại như sau:

    from math import sqrt
    def giai_pt(a, b, c):
          d = b**2 – 4*a*c
          if d < 0:
                print("Phương trình vô nghiệm.")
          elif d == 0:
                print("Phương trình có nghiệm kép x = ", -b /(2*a))
          else:
                print("Phương trình có hai nghiệm ", (-b + sqrt(d))/(2*a),\
                " và ", (-b – sqrt(d))/(2*a))

    Lúc này, để giải phương trình bậc hai với các hệ số a = 1, b = -3, c = 5 ta chỉ việc gọi hàm bằng cách sử dụng câu lệnh giai_pt(1, -3, 5).

    4. Giá trị trả về của hàm

    Hàm trong Python có thể có hoặc không có giá trị trả về. Điều này khác với các hàm số trong toán học bắt buộc phải nhận một giá trị trả về, thì hàm trong Python có thể chỉ thực hiện một nhiệm vụ nào đó như in ra màn hình một chuỗi, xóa hết các phần tử của một tập hợp…

    Trong trường hợp hàm có kết quả trả về ta sử dụng từ khóa return, theo sau là giá trị trả về của hàm. Khi đó hàm có thể được sử dụng như một biến để tính toán. Hãy xét một hàm đơn giản, tên là cong để cộng hai số nguyên ab, giá trị trả về chính là tổng của hái số đó, định nghĩa như sau:

    def cong(a, b):
          return a + b

    Chúng ta sẽ thử gọi hàm này với các tham số truyền vào  a = 3, b =7.

    >>> cong(3, 7)
    10
    >>> cong(3, 7) + 5
    15

    Nếu ta thực hiện chương trình này trong trình thông dịch Python, thì ở lần gọi hàm thứ nhất kết quả trả về là 10, ở lần gọi thứ hai, bản thân hàm được sử dụng như một biến số để thực hiện tiếp phép cộng, và kết quả trả về là 15.

    Tuy nhiên, nếu các bạn soạn một script có nội dung như sau

    def cong(a, b):
          return a + b
    
    cong(3, 7)
    cong(4, 7) + 5

    và lưu lại với tên có phần mở rộng .py rồi chạy script này thì kết quả là không xuất hiện gì cả trên màn hình! Lí do là chương trình vẫn tính a + b, và vẫn trả về kết quả này và được sử dụng để tính toán tiếp nhưng không được in ra màn hình. Lúc này, muốn in kết quả ra chúng ta phải sử dụng đến hàm print() của Python.

    def cong(a, b):
          return a + b
    
    print(cong(3, 7))
    print(cong(4, 7) + 5)

    Theo sau từ khóa return là một giá trị, một biểu thức, một câu lệnh, thậm chí lại là một hàm[2] hoặc không có gì cả. Nếu không có gì, thì chương trình chỉ đơn giản là thoát khỏi hàm mà thôi và trả về giá trị None. Mỗi khi gặp từ khóa return thì hàm sẽ kết thúc, chương trình sẽ thoát khỏi hàm và thực hiện câu lệnh tiếp theo. Hãy xem ví dụ sau để hiểu rõ hơn:

    def test():
          print(1)
          return
          print(2)

    Chúng ta sẽ chạy thử.

    >>> test()
    1
    >>> print(test())
    1
    None

    Ta thấy ngay, câu lệnh print(2) không được thực thi vì trước đó chương trình đã thực hiện lệnh return rồi. Hơn nữa, lệnh return này không trả về gì cả, nên nếu ta dùng lệnh print(test()) thì chương trình sẽ trả về kết quả None. Còn số 1 kia được in ra là do bản thân trong hàm có câu lệnh print(1) mà thôi.

    Bài tập

    • Viết hàm giải hệ phương trình bậc nhất hai ẩn, sử dụng định thức cấp hai.
    • Viết chương trình kiểm tra xem số tự nhiên n có phải là số nguyên tố không.
    • Viết chương trình tìm ước chung lớn nhất của hai số tự nhiên.

    5. Tham số của hàm trong Python

    Như đã nói, một hàm có thể có tham số hoặc không. Đối với hàm có tham số thì nếu số lượng tham số xác định, chúng ta có thể khai báo hết các tham số/đối số đó. Nhưng đối với các hàm có số lượng tham số chưa biết, ví dụ như tính tổng của một dãy số mà chưa biết dãy đó có bao nhiêu phần tử, thì chúng ta phải cần đến các tham số đặc biệt, khai báo với các dấu sao * trước danh sách tham số/đối số.

    Phần này có tham khảo từ blog https://icewolf-blog.herokuapp.com/post/8

     5.1. Cách truyền tham số dạng *args**kwargs trong python (abittrary arguments)

    Kiểu truyền tham số này thường được sử dụng khi định nghĩa những hàm không biết trước số lượng tham số truyền vào. Thực sự thì không nhất thiết phải là *args**kwargs, điều quan trọng là tham số có 1 dấu sao * hay là 2 dấu sao **. Đặt tên tham số là *var hay **vars hay bất cứ thứ gì bạn muốn. Nhưng để dễ hiểu thì nên dùng tên chuẩn là *args**kwargs

    5.2. *args**kwargs dùng để làm gì?

    Khi khai báo một hàm trong Python, sử dụng *args**kwargs cho phép bạn truyền vào nhiều tham số/đối số mà không cần biết trước số lượng.

    #giả sử các tham số truyền vào đều là số
    
    def sum(*args):
       total = 0
       for number in args:
         total += number
     return total
    
    # gọi hàm
    sum(1, 2, 3, 19)
    sum(1, 100)
    
    def myFun(*argv): 
        for arg in argv: 
            print (arg)
       
    myFun('Hello', 'Welcome', 'to', 'GeeksforGeeks')

    5.3. *args**kwargs khác gì nhau?

    Khi gọi hàm trong Python, có 2 kiểu truyền tham số:

    • Truyền tham số theo tên.
    • Truyền tham số bình thường theo thứ tự khai báo đối số.

    Ví dụ

    def register(name, password):
     ....
    
    #Truyền tham số theo kiểu thông thường, phải theo đúng thứ tự
    register( 'Coulson', 'hail_Hydra')
    
    #Truyền tham số theo tên, Không cần phải theo thứ tự khai báo thao số
    register( password='cookHim', name='Skye')
    • *args nhận các tham số truyền bình thường, sử dụng args như một list.
    • **kwargs nhận tham số truyền theo tên, sử dụng kwargs như một dictionary.
    def test_args(*args):
      for item in args:
         print item
    
    >>>test_args('Hello', 'world!')
    Hello
    world!
    
    def test_kwargs(*kwargs):
      for key, value in kwargs.iteritems():
         print '{0} = {1}'.format(key, value)
    
    >>test_kwargs(name='Dzung', age=10)
    age = 10
    name = Dzung

    5.4. Thứ tự sử dụng và truyền tham số *args, **kwargs và tham số bình thường

    Khi sử dụng hàm, ta phải khai báo đối số theo thứ tự ưu tiên đối số/tham số xác đinh, tiếp theo là *args và cuối cùng là **kwargs.  Đây là thứ tự bắt buộc. Và khi truyền tham số bạn cũng phải truyền theo đúng thứ tự này. Không thể truyền lẫn lộn giữa 2 loại.

    Khi sử dụng đồng thời *args**kwargs thì không thể truyền tham số bình thường theo tên

    def show_detail(name, *args, **kwargs):
    ...
    
    show_detail(name='Coulson', 'agent', age='40', level='A')
    >>> Lỗi
    
    def show_detail_2(name, **kwargs):
    ...
    
    show_detail_2(name=Coulson', age='40', level='A')
    >>> Chạy OK

    6. Lời gọi đệ quy đến hàm trong Python

    Một hàm có thể gọi lại chính nó, lúc này ta gọi là các lời gọi đệ quy[3] recursion.

    Ví dụ, cho cấp số cộng un với u1 = 5 và công sai d = 7 thì, chẳng hạn, muốn tìm số hạng thứ 20 ta lấy số hạng thứ 19 cộng thêm 7, nhưng muốn tính số hạng thứ 19 ta lại phải lấy số hạng thứ 18 cộng thêm 7 và cứ như thế cho đến số hạng thứ nhất thì đã có sẵn u1 = 5. Tổng quát, ta có công thức truy hồi như sau

    Nếu ta viết hàm cap_so_cong(n) để tính số hạng thứ n thì trong bản thân hàm này sẽ gọi lại chính nó với tham số truyền vào là n - 1:

    def cap_so_cong(n):
          if n == 1:
                return 5
          else:
                return cap_so_cong(n-1) + 7

    Lúc này, để tìm số hạng thứ n ta chỉ việc gọi hàm và truyền vào tham số n, chẳng hạn tính số hạng thứ 50 thì gọi hàm và truyền vào cho nó tham số n = 50, cap_so_cong(50):

    >>> cap_so_cong(50)
    348

    Một hàm có thể gọi lại chính nó nhiều lần, ví dụ, chúng ta xét dãy số Fibonacci

    Chúng ta có chương trình như sau

    def fibonacci(n):
          if n == 1 or n == 2:
                return 1
          else:
                return fibonacci(n-1) + fibonacci(n-2)

    Khi chạy chương trình, thử tìm số Fibonacci thứ 30 ta được kết quả

    >>> fibonacci(30)
    832040

    Tuy nhiên, trong lập trình, chúng ta nên hạn chế các lời gọi đệ quy, vì như thế chương trình sẽ cần rất nhiều bộ nhớ, chẳng hạn, ta tìm số Fibonacci thứ 100 thôi đã phải đợi rất lâu, vì bản thân mỗi lần gọi hàm, chương trình sẽ gọi lại chính nó hai lần và cứ như thế, do đó số lượng lời gọi hàm sẽ tăng lên rất nhiều với tốc độ cấp số nhân!

    Bài tập

    • Viết hàm cap_so_nhan(n) để tìm số hạng thứ n của một cấp số nhân với số hạng đầu bằng 7 và công bội bằng 2.
    • Viết hàm giai_thua(n) để tính giai thừa[4] của số tự nhiên n, chẳng hạn giai_thua(6) trả về kết quả 720.
    • Tìm tất cả các số tự nhiên hai chữ số mà khi đảo trật tự của hai chữ số đó sẽ thu được một số nguyên tố cùng nhau với số đã cho.
    • Tìm các số tự nhiên lẻ có ba chữ số. Ba chữ số này, theo trật tự từ trái qua phải tạo thành một cấp số cộng.
    • Tìm các số tự nhiên có ba chữ số. Ba chữ số này, theo trật tự từ trái qua phải tạo thành một cấp số nhân với công bội là một số tự nhiên khác 0.

    7. Biến toàn cục, biến cục bộ

    Trong Python không có sự phân biệt giữa tham số và tham số giá trị như trong ngôn ngữ khác, Pascal chẳng hạn, nên tất cả các giá trị được truyền vào một hàm chúng ta đều gọi là tham số của hàm. Ở đây, chúng ta chỉ cần phân biệt khái niệm biến toàn cục global và biến cục bộ (biến địa phương) local mà thôi.

    Một biến được định nghĩa bên trong một hàm chỉ có thể được sử dụng bên trong hàm đó và được gọi là biến cục bộ. Các tham số và biến cục bộ này chỉ được phép sử dụng bởi các lệnh nằm bên trong hàm. Khi hàm được thực thi thì biến sẽ tồn tại và sẽ bị hủy khi chúng ta thoát khỏi hàm.

    a = 1
    def bien_cuc_bo(a):
        a = 5
        print(a)
    
    bien_cuc_bo(a) # chương trình  trả về kết quả là 5
    print(a) #chương trình vẫn trả về kết quả là 1

    Nhưng nếu như biến mà có kiểu dữ liệu là list thì những thay đổi bên trong hàm sẽ được giữ lại.

    a = [0, 5, 10, 15]
    def bien_cuc_bo(a):
        a[1] = 100
        print(a)
    
    bien_cuc_bo(a) # chương trình  trả về kết quả là [0, 100, 10, 15]
    print(a) #chương trình vẫn trả về kết quả là [0, 100, 10, 15]

    Để ghi nhận lại những thay đổi giá trị của một biến do các câu lệnh bên trong hàm tạo nên, chúng ta có thể sử dụng từ khóaglobal (biến toàn cầu, biến toàn cục). Khi một biến được khai báo là global thì chúng ta có thể gọi và thay đổi giá trị của nó từ bất kỳ đâu trong chương trình, kể cả trong thân hàm. Lưu ý rằng biến toàn cục chỉ có thể khai báo bên trong hàm và nó không thể là tham số/đối số của hàm.

    Như ví dụ sau, biến a lúc đầu có giá trị là 1, và trong hàm a được khai báo global đồng thời nhận giá trị mới là 5 thì ra bên ngoài hàm, giá trị mới này vẫn được ghi lại chứ không như ví dụ trước.

    a = 1
    def bien_toan_cuc():
       global a
       a = 5
       print(a)
    
    bien_toan_cuc() # chương trình  trả về kết quả là 5
    print(a) #chương trình vẫn trả về kết quả là 5

    8. Hàm nặc danh

    Chúng ta sử dụng từ khoá lambda được dùng để tạo một hàm số nặc danh (một hàm số không có tên). Nó là một hàm số trên một dòng và không có câu lệnh return. Nó chỉ bao gồm một biểu thức mà kết quả của biểu thức này chính là giá trị trả về của hàm. Ví dụ:

    a = lambda x: x*2
    for i in range(1,6):
          print(a(i))

    Kết quả

    2
    4
    6
    8
    10

    Ở đây, chúng ta tạo ra một hàm số chỉ trong một dòng lệnh, nó được dùng để nhân đôi giá trị của đối số nhận vào, sử dụng câu lệnh lambda. Sau đó, chúng ta dùng hàm này để nhân đôi các giá trị của một danh sách các số nguyên từ 1 đến 5.

    Mời bạn xem chi tiết trong bài Hàm ẩn danh trong Python

    [1]Bạn chỉ có thể tái sử dụng bằng cách Copy – Paste mà thôi 🙂

    [2]Xem phần đệ quy hàm để rõ hơn.

    [3]Thực ra, thử tìm hiểu từ đệ quy thì thấy rất khó hiểu, ta cứ hiểu nôm na như là phép quy nạp trong Toán học.

    [4]Giai thừa của một số tự nhiên n, kí hiệu là n!, được tính bởi công thức