ฉันพยายามสอนตัวเองถึงวิธีการคำนวณสัญกรณ์ BigO สำหรับฟังก์ชั่นโดยพลการ ฉันพบฟังก์ชันนี้ในตำราเรียน หนังสือยืนยันว่าฟังก์ชันนั้นเป็น O (n 2 ) มันให้คำอธิบายว่าทำไมถึงเป็นเช่นนี้ แต่ฉันพยายามที่จะติดตาม ฉันสงสัยว่าใครบางคนอาจแสดงคณิตศาสตร์ให้ฉันฟังได้ไหมว่าทำไมถึงเป็นเช่นนั้น ฉันเข้าใจว่ามันเป็นอะไรที่น้อยกว่า O (n 3 ) แต่ฉันไม่สามารถลงจอดบน O (n 2 ) ได้อย่างอิสระ
สมมติว่าเราได้รับสามลำดับของตัวเลข A, B และ C เราจะสมมติว่าไม่มีลำดับแต่ละรายการมีค่าซ้ำกัน แต่อาจมีบางตัวเลขที่อยู่ในลำดับที่สองหรือสาม ปัญหาความไม่ลงรอยกันชุดสามทางคือการตรวจสอบว่าจุดตัดของทั้งสามลำดับนั้นว่างเปล่าหรือไม่นั่นคือไม่มีองค์ประกอบ x เช่น x x A, x ∈ B, และ x ∈ C
อนึ่งนี่ไม่ใช่ปัญหาการบ้านสำหรับฉัน - เรือลำนั้นแล่นไปเมื่อหลายปีก่อน) ฉันแค่พยายามฉลาดขึ้น
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
[แก้ไข] ตามตำรา:
ในรุ่นที่ปรับปรุงแล้วมันไม่ได้เป็นเพียงแค่เราประหยัดเวลาถ้าเราโชคดี เราอ้างว่าเวลาที่เลวร้ายที่สุดในการทำงานสำหรับ disjoint คือ O (n 2 )
คำอธิบายของหนังสือที่ฉันพยายามทำตามคือ:
เพื่อพิจารณาถึงเวลาการทำงานโดยรวมเราตรวจสอบเวลาที่ใช้ในการประมวลผลโค้ดแต่ละบรรทัด การจัดการสำหรับ for loop over A ต้องใช้เวลา O (n) การจัดการวนรอบสำหรับบัญชี B รวมเป็นเวลา O (n 2 ) เนื่องจากการวนซ้ำนั้นดำเนินการในเวลาที่ต่างกัน การทดสอบ a == b จะถูกประเมิน O (n 2 ) ครั้ง เวลาที่เหลือจะขึ้นอยู่กับจำนวนการจับคู่ (a, b) ดังที่เราได้สังเกตเห็นว่ามีอยู่ไม่เกิน n คู่ดังกล่าวดังนั้นการจัดการของลูปผ่าน C และคำสั่งภายในเนื้อความของลูปนั้นใช้เวลา O (n 2 ) มากที่สุด เวลาทั้งหมดที่ใช้คือ O (n 2 )
(และเพื่อให้เครดิตที่เหมาะสม ... ) หนังสือเล่มนี้คือ: โครงสร้างข้อมูลและอัลกอริทึมใน Python โดย Michael T. Goodrich และ ทั้งหมด Wiley Publishing, pg. 135
[แก้ไข] เหตุผล ด้านล่างนี้เป็นรหัสก่อนการเพิ่มประสิทธิภาพ:
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
ในข้างต้นคุณจะเห็นได้อย่างชัดเจนว่านี่คือ O (n 3 ) เพราะแต่ละวงจะต้องวิ่งให้เต็มที่ หนังสือเล่มนี้จะยืนยันว่าในตัวอย่างง่าย ๆ (รับแรก) วงที่สามเป็นเพียงความซับซ้อนของ O (n 2 ) ดังนั้นสมการความซับซ้อนไปเป็น k + O (n 2 ) + O (n 2 ) ซึ่งในที่สุดผลตอบแทน O (n 2 )
แม้ว่าฉันจะไม่สามารถพิสูจน์ได้ว่าเป็นกรณีนี้ (เช่นคำถาม) ผู้อ่านสามารถยอมรับว่าความซับซ้อนของอัลกอริทึมที่ทำให้เข้าใจง่ายมีค่าน้อยกว่าของเดิม
[แก้ไข] และเพื่อพิสูจน์ว่าเวอร์ชันที่ง่ายคือกำลังสอง:
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)
อัตราผลตอบแทน:
0.02684807777404785
0.00019478797912597656
0.19134306907653809
0.0007600784301757812
0.6405444145202637
0.0018095970153808594
1.4873297214508057
0.003167390823364258
2.953308343887329
0.004908084869384766
เนื่องจากความแตกต่างที่สองมีค่าเท่ากันฟังก์ชันที่เรียบง่ายจึงเป็นกำลังสอง:
[แก้ไข] และยังมีข้อพิสูจน์เพิ่มเติม:
ถ้าฉันถือว่ากรณีที่เลวร้ายที่สุด (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)
อัตราผลตอบแทน:
iterations1 = 1000
iterations2 = 100
iterations1 = 8000
iterations2 = 400
iterations1 = 27000
iterations2 = 900
iterations1 = 64000
iterations2 = 1600
iterations1 = 125000
iterations2 = 2500
การใช้การทดสอบผลต่างครั้งที่สองผลลัพธ์ของกรณีที่เลวร้ายที่สุดคือสมการกำลังสอง