Saya mencoba untuk belajar sendiri bagaimana menghitung notasi BigO untuk fungsi arbitrer. Saya menemukan fungsi ini di buku teks. Buku ini menegaskan bahwa fungsinya adalah O (n 2 ). Ini memberikan penjelasan mengapa ini terjadi, tetapi saya berjuang untuk mengikuti. Saya bertanya-tanya apakah seseorang mungkin bisa menunjukkan kepada saya matematika di balik mengapa demikian. Pada dasarnya, saya mengerti bahwa itu adalah sesuatu yang kurang dari O (n 3 ), tetapi saya tidak dapat secara mandiri mendarat di O (n 2 )
Misalkan kita diberi tiga urutan angka, A, B, dan C. Kita akan mengasumsikan bahwa tidak ada urutan individual yang mengandung nilai duplikat, tetapi mungkin ada beberapa angka yang ada dalam dua atau tiga urutan. Masalah disjointness set tiga-arah adalah untuk menentukan apakah persimpangan dari tiga sekuens kosong, yaitu, bahwa tidak ada elemen x sehingga x ∈ A, x ∈ B, dan x ∈ C.
Kebetulan, ini bukan masalah pekerjaan rumah bagi saya - bahwa kapal telah berlayar bertahun-tahun yang lalu :), hanya saya yang mencoba menjadi lebih pintar.
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
[Sunting] Menurut buku teks:
Dalam versi yang ditingkatkan, bukan hanya kita menghemat waktu jika kita beruntung. Kami mengklaim bahwa waktu berjalan kasus terburuk untuk disjoint adalah O (n 2 ).
Penjelasan buku ini, yang saya susah payah ikuti, adalah ini:
Untuk menjelaskan keseluruhan waktu berjalan, kami memeriksa waktu yang dihabiskan untuk mengeksekusi setiap baris kode. Manajemen for for over A membutuhkan O (n) waktu. Manajemen for for over B menyumbang total waktu O (n 2 ), karena loop tersebut dieksekusi pada n waktu yang berbeda. Tes a == b dievaluasi O (n 2 ) kali. Sisa waktu yang dihabiskan tergantung pada berapa banyak pasangan yang cocok (a, b). Seperti yang telah kita catat, paling banyak ada n pasangan demikian, dan demikian pula pengelolaan loop di atas C, dan perintah-perintah di dalam tubuh loop itu, gunakan paling banyak pada waktu O (n 2 ). Total waktu yang dihabiskan adalah O (n 2 ).
(Dan untuk memberikan kredit yang layak ...) Buku ini adalah: Struktur Data dan Algoritma dalam Python oleh Michael T. Goodrich et. semua, Penerbitan Wiley, hal. 135
[Sunting] Suatu pembenaran; Di bawah ini adalah kode sebelum optimasi:
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
Pada contoh di atas, Anda dapat dengan jelas melihat bahwa ini adalah O (n 3 ), karena setiap loop harus dijalankan sepenuhnya. Buku ini akan menyatakan bahwa dalam contoh sederhana (diberikan pertama), loop ketiga hanya kompleksitas O (n 2 ), sehingga persamaan kompleksitas berjalan sebagai k + O (n 2 ) + O (n 2 ) yang akhirnya menghasilkan O (n 2 ).
Meskipun saya tidak dapat membuktikan hal ini (dengan demikian pertanyaannya), pembaca dapat setuju bahwa kompleksitas dari algoritma yang disederhanakan setidaknya kurang dari aslinya.
[Sunting] Dan untuk membuktikan bahwa versi yang disederhanakan adalah kuadratik:
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)
Hasil:
0.02684807777404785
0.00019478797912597656
0.19134306907653809
0.0007600784301757812
0.6405444145202637
0.0018095970153808594
1.4873297214508057
0.003167390823364258
2.953308343887329
0.004908084869384766
Karena perbedaan kedua sama, fungsi yang disederhanakan memang kuadratik:
[Sunting] Dan bukti lebih lanjut:
Jika saya menganggap kasus terburuk (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)
hasil:
iterations1 = 1000
iterations2 = 100
iterations1 = 8000
iterations2 = 400
iterations1 = 27000
iterations2 = 900
iterations1 = 64000
iterations2 = 1600
iterations1 = 125000
iterations2 = 2500
Menggunakan uji perbedaan kedua, hasil kasus terburuk adalah kuadratik.