Tôi đang cố gắng tự dạy mình cách tính ký hiệu BigO cho một hàm tùy ý. Tôi tìm thấy chức năng này trong một cuốn sách giáo khoa. Cuốn sách khẳng định rằng hàm là O (n 2 ). Nó đưa ra một lời giải thích về lý do tại sao, nhưng tôi đang cố gắng làm theo. Tôi tự hỏi nếu ai đó có thể chỉ cho tôi toán học đằng sau lý do tại sao điều này là như vậy. Về cơ bản, tôi hiểu rằng đó là một cái gì đó ít hơn O (n 3 ), nhưng tôi không thể độc lập hạ cánh trên O (n 2 )
Giả sử chúng ta được cung cấp ba chuỗi số A, B và C. Chúng ta sẽ giả sử rằng không có chuỗi riêng lẻ nào chứa các giá trị trùng lặp, nhưng có thể có một số số nằm trong hai hoặc ba chuỗi. Vấn đề phân biệt tập hợp ba chiều là xác định xem giao điểm của ba chuỗi có trống không, cụ thể là không có phần tử x sao cho x ∈ A, x ∈ B và x ∈ C.
Ngẫu nhiên, đây không phải là vấn đề bài tập về nhà đối với tôi - con tàu đó đã ra khơi nhiều năm trước :), chỉ là tôi đang cố gắng để thông minh hơn.
def disjoint(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
if a == b: # only check C if we found match from A and B
for c in C:
if a == c # (and thus a == b == c)
return False # we found a common value
return True # if we reach this, sets are disjoint
[Chỉnh sửa] Theo sách giáo khoa:
Trong phiên bản cải tiến, không chỉ đơn giản là chúng ta tiết kiệm thời gian nếu gặp may mắn. Chúng tôi tuyên bố rằng thời gian chạy trường hợp xấu nhất cho sự rời rạc là O (n 2 ).
Lời giải thích của cuốn sách, mà tôi đấu tranh để làm theo, là đây:
Để tính tổng thời gian chạy, chúng tôi kiểm tra thời gian thực hiện từng dòng mã. Việc quản lý vòng lặp for qua A đòi hỏi thời gian O (n). Việc quản lý vòng lặp for trên B chiếm tổng thời gian O (n 2 ), vì vòng lặp đó được thực hiện n lần khác nhau. Phép thử a == b được ước tính O (n 2 ) lần. Thời gian còn lại phụ thuộc vào số lượng cặp (a, b) phù hợp tồn tại. Như chúng tôi đã lưu ý, có nhiều nhất n cặp như vậy, và do đó, việc quản lý vòng lặp trên C và các lệnh trong phần thân của vòng lặp đó, sử dụng tối đa thời gian O (n 2 ). Tổng thời gian sử dụng là O (n 2 ).
(Và để cung cấp tín dụng phù hợp ...) Cuốn sách là: Cấu trúc dữ liệu và thuật toán trong Python của Michael T. Goodrich et. tất cả, xuất bản Wiley, pg. 135
[Chỉnh sửa] Một lời biện minh; Dưới đây là mã trước khi tối ưu hóa:
def disjoint1(A, B, C):
"""Return True if there is no element common to all three lists."""
for a in A:
for b in B:
for c in C:
if a == b == c:
return False # we found a common value
return True # if we reach this, sets are disjoint
Ở trên, bạn có thể thấy rõ đây là O (n 3 ), vì mỗi vòng lặp phải chạy hết mức. Cuốn sách sẽ khẳng định rằng trong ví dụ đơn giản hóa (được đưa ra trước), vòng lặp thứ ba chỉ là độ phức tạp của O (n 2 ), do đó phương trình phức tạp đi theo k + O (n 2 ) + O (n 2 ) mà cuối cùng mang lại O (n 2 ).
Mặc dù tôi không thể chứng minh đây là trường hợp (do đó là câu hỏi), người đọc có thể đồng ý rằng độ phức tạp của thuật toán đơn giản hóa ít nhất là ít hơn so với bản gốc.
[Chỉnh sửa] Và để chứng minh rằng phiên bản đơn giản hóa là bậc hai:
if __name__ == '__main__':
for c in [100, 200, 300, 400, 500]:
l1, l2, l3 = get_random(c), get_random(c), get_random(c)
start = time.time()
disjoint1(l1, l2, l3)
print(time.time() - start)
start = time.time()
disjoint2(l1, l2, l3)
print(time.time() - start)
Sản lượng:
0.02684807777404785
0.00019478797912597656
0.19134306907653809
0.0007600784301757812
0.6405444145202637
0.0018095970153808594
1.4873297214508057
0.003167390823364258
2.953308343887329
0.004908084869384766
Vì sự khác biệt thứ hai là bằng nhau, nên hàm đơn giản hóa thực sự là bậc hai:
[Chỉnh sửa] Và thậm chí còn chứng minh thêm:
Nếu tôi giả sử trường hợp xấu nhất (A = B! = C),
if __name__ == '__main__':
for c in [10, 20, 30, 40, 50]:
l1, l2, l3 = range(0, c), range(0,c), range(5*c, 6*c)
its1 = disjoint1(l1, l2, l3)
its2 = disjoint2(l1, l2, l3)
print(f"iterations1 = {its1}")
print(f"iterations2 = {its2}")
disjoint2(l1, l2, l3)
sản lượng:
iterations1 = 1000
iterations2 = 100
iterations1 = 8000
iterations2 = 400
iterations1 = 27000
iterations2 = 900
iterations1 = 64000
iterations2 = 1600
iterations1 = 125000
iterations2 = 2500
Sử dụng thử nghiệm khác biệt thứ hai, kết quả trường hợp xấu nhất là chính xác bậc hai.