การแปลรหัสเป็นคณิตศาสตร์
ให้ความหมายในการปฏิบัติการอย่างเป็นทางการ (มากกว่าหรือน้อยกว่า) คุณสามารถแปลรหัส (หลอก) ของอัลกอริทึมได้อย่างแท้จริงในนิพจน์ทางคณิตศาสตร์ที่ให้ผลลัพธ์กับคุณโดยที่คุณสามารถจัดการนิพจน์ให้เป็นรูปแบบที่มีประโยชน์ นี้ทำงานได้ดีสำหรับสารเติมแต่งมาตรการค่าใช้จ่ายเช่นจำนวนของการเปรียบเทียบสัญญาแลกเปลี่ยนงบหน่วยความจำเข้าถึงรอบบางความต้องการของเครื่องที่เป็นนามธรรมและอื่น ๆ
ตัวอย่าง: การเปรียบเทียบใน Bubblesort
พิจารณาอัลกอริธึมที่จัดเรียงอาร์เรย์ที่กำหนดA:
 bubblesort(A) do                   1
  n = A.length;                     2
  for ( i = 0 to n-2 ) do           3
    for ( j = 0 to n-i-2 ) do       4
      if ( A[j] > A[j+1] ) then     5
        tmp    = A[j];              6
        A[j]   = A[j+1];            7
        A[j+1] = tmp;               8
      end                           9
    end                             10
  end                               11
end                                 12
สมมติว่าเราต้องการทำการวิเคราะห์อัลกอริทึมการเรียงลำดับตามปกติซึ่งนับจำนวนการเปรียบเทียบองค์ประกอบ (บรรทัดที่ 5) เราทราบได้ทันทีว่าปริมาณนี้ไม่ได้ขึ้นอยู่กับเนื้อหาของอาร์เรย์Aเพียงกับความยาวของnดังนั้นเราจึงสามารถแปล (ซ้อน) - ลูปค่อนข้างเป็นผลรวม (ซ้อน) รวมกัน; ตัวแปรลูปจะกลายเป็นตัวแปรการสรุปรวมและช่วงการดำเนินการ เราได้รับ:nfor
Ccmp(n)=∑i=0n−2∑j=0n−i−21=⋯=n(n−1)2=(n2) ,
โดยที่คือค่าใช้จ่ายสำหรับการดำเนินการแต่ละบรรทัด 5 (ซึ่งเรานับ)1
ตัวอย่าง: Swaps ใน Bubblesort
ฉันจะแสดงโดยโปรแกรมย่อยที่ประกอบด้วยบรรทัดไปและโดยค่าใช้จ่ายสำหรับการดำเนินการโปรแกรมย่อยนี้ (หนึ่งครั้ง)Pi,jijCi,j
ตอนนี้ขอบอกว่าเราต้องการนับแลกเปลี่ยนที่เป็นวิธีการที่มักจะถูกดำเนินการ นี่คือ "บล็อกพื้นฐาน" ซึ่งเป็นโปรแกรมย่อยที่จะดำเนินการทางอะตอมเสมอและมีค่าใช้จ่ายคงที่ (ที่นี่ ) การทำสัญญาบล็อกดังกล่าวเป็นสิ่งที่มีประโยชน์อย่างหนึ่งที่เรามักใช้โดยไม่ต้องคิดหรือพูดถึงมันP6,81
ด้วยการแปลที่คล้ายกันข้างต้นเรามาถึงสูตรต่อไปนี้:
Cswaps(A)=∑i=0n−2∑j=0n−i−2C5,9(A(i,j))j)})
A(i,j)หมายถึงรัฐอาเรย์ก่อนย้ำ -th ของ{5,9}(i,j)P5,9
โปรดทราบว่าฉันใช้แทนเป็นพารามิเตอร์ เราจะเห็นว่าทำไมในไม่ช้า ฉันไม่ได้เพิ่มและเป็นพารามิเตอร์ของเนื่องจากค่าใช้จ่ายไม่ได้ขึ้นอยู่กับพวกเขาที่นี่ (ในรูปแบบค่าใช้จ่ายสม่ำเสมอนั่นคือ); โดยทั่วไปพวกเขาอาจจะAnijC5,9
เห็นได้ชัดว่าค่าใช้จ่ายของขึ้นอยู่กับเนื้อหาของ (ค่าและโดยเฉพาะ) ดังนั้นเราจึงต้องคำนึงถึงสิ่งนั้น ตอนนี้เราเผชิญกับความท้าทาย: เราจะ "แกะ"อย่างไร เราสามารถพึ่งพาเนื้อหาของอย่างชัดเจน:P5,9AA[j]A[j+1]C5,9A
C5,9(A(i,j))=C5(A(i,j))+{10,A(i,j)[j]>A(i,j)[j+1],else{}
สำหรับอาเรย์อินพุตที่กำหนดค่าใช้จ่ายเหล่านี้มีการกำหนดไว้อย่างดี แต่เราต้องการคำแถลงทั่วไปมากขึ้น เราต้องทำให้สมมติฐานที่แข็งแกร่งขึ้น ให้เราตรวจสอบสามกรณีทั่วไป
- กรณีที่เลวร้ายที่สุด - เพียงแค่ดูที่ผลรวมและสังเกตว่า , เราสามารถหาขอบเขตบนเล็กน้อยสำหรับค่าใช้จ่าย:C5,9(A(i,j))∈{0,1} - Cswaps(A)≤∑i=0n−2∑j=0n−i−21=n(n−1)2=(n2){2} - แต่สิ่งนี้สามารถเกิดขึ้นได้หรือไม่นั่นคือมีสำหรับขอบเขตบนนี้หรือไม่ ตามที่ปรากฎว่าใช่: ถ้าเราใส่อาเรย์เรียงขององค์ประกอบที่แตกต่างกันแบบคู่กลับกันผกผันทุกการวนซ้ำจะต้องทำการสลับ ดังนั้นเราจึงได้จำนวนบับเบิลพอร์ตที่เลวร้ายที่สุดอย่างแน่นอนA 
- กรณีที่ดีที่สุด - ตรงกันข้ามมีขอบเขตล่างที่น่ารำคาญ: - Cswaps(A)≥∑i=0n−2∑j=0n−i−20=00 - สิ่งนี้สามารถเกิดขึ้นได้: ในอาร์เรย์ที่เรียงลำดับแล้ว Bubblesort จะไม่ทำการสลับครั้งเดียว 
- กรณีเฉลี่ย - กรณีที่แย่ที่สุดและดีที่สุดเปิดช่องว่างค่อนข้าง แต่จำนวน swaps ทั่วไปคืออะไร? เพื่อที่จะตอบคำถามนี้เราจำเป็นต้องกำหนดความหมาย "ทั่วไป" ในทางทฤษฎีเราไม่มีเหตุผลที่จะชอบสิ่งใดสิ่งหนึ่งเหนือสิ่งอื่นดังนั้นเราจึงมักจะมีการแจกแจงแบบสม่ำเสมอในทุก ๆ ทางที่เป็นไปได้ เรา จำกัด ตัวเองให้อยู่ในอาร์เรย์ด้วยองค์ประกอบที่แตกต่างกันตามลำดับและสมมติว่าเป็นแบบจำลองการเปลี่ยนแปลงแบบสุ่ม - จากนั้นเราสามารถเขียนค่าใช้จ่ายเช่นนี้²: - E[Cswaps]=1n!∑A∑i=0n−2∑j=0n−i−2C5,9(A(i,j)) - ตอนนี้เราต้องไปไกลกว่าการจัดการกับผลรวมอย่างง่าย โดยการดูอัลกอริทึมเราทราบว่าการสลับทุกครั้งจะลบการผกผันเดียวใน (เราเพียงแค่สลับneighbours³) นั่นคือจำนวนของสัญญาแลกเปลี่ยนดำเนินการในเป็นว่าจำนวน inversionsของ ดังนั้นเราสามารถแทนที่ผลรวมสองภายในและรับAAinv(A)A - E[Cswaps]=1n!∑Ainv(A)(A) - โชคดีที่เรามีจำนวนผู้รุกรานโดยเฉลี่ยที่พิจารณาแล้วว่าเป็น - E[Cswaps]=12⋅(n2) - ซึ่งเป็นผลสุดท้ายของเรา โปรดทราบว่านี่เป็นครึ่งราคาที่เลวร้ายที่สุด 
- โปรดทราบว่าอัลกอริทึมได้รับการกำหนดอย่างรอบคอบเพื่อให้การวนซ้ำ "ครั้งสุดท้าย" i = n-1ของลูปด้านนอกที่ไม่เคยทำสิ่งใด ๆ
- " " เป็นสัญลักษณ์ทางคณิตศาสตร์สำหรับ "ค่าที่คาดหวัง" ซึ่งนี่เป็นค่าเฉลี่ยE
- เราเรียนรู้ไปตามวิธีที่ไม่มีอัลกอริทึมที่แลกเปลี่ยนองค์ประกอบใกล้เคียงเท่านั้นที่สามารถ asymptotically เร็วกว่า Bubblesort (แม้โดยเฉลี่ย) - จำนวนการรุกรานนั้นมีขอบเขตต่ำกว่าสำหรับอัลกอริธึมดังกล่าวทั้งหมด นี้นำไปใช้เช่นการแทรกเรียงและการเลือกเรียง
วิธีการทั่วไป
เราได้เห็นในตัวอย่างที่เราต้องแปลโครงสร้างการควบคุมเป็นคณิตศาสตร์ ฉันจะนำเสนอชุดกฎการแปลทั่วไป นอกจากนี้เรายังเห็นว่าค่าใช้จ่ายของโปรแกรมย่อยใด ๆ อาจขึ้นอยู่กับสถานะปัจจุบันนั่นคือ (ประมาณ) มูลค่าปัจจุบันของตัวแปร เนื่องจากอัลกอริทึม (ปกติ) แก้ไขสถานะวิธีการทั่วไปจึงค่อนข้างยุ่งยากที่จะสังเกตเห็น หากคุณเริ่มรู้สึกสับสนฉันขอแนะนำให้คุณกลับไปที่ตัวอย่างหรือทำขึ้นของคุณเอง
เราแสดงสถานะปัจจุบันด้วย (ลองจินตนาการว่ามันเป็นชุดของการกำหนดตัวแปร) เมื่อเรารันโปรแกรมที่เริ่มต้นใน stateเราจะสิ้นสุดใน state ( ถูกยกเลิก)ψPψψ/PP
- งบส่วนบุคคล - รับเพียงคำเดียว- S;, คุณกำหนดค่าใช้จ่ายปอนด์ต่อตารางนิ้ว) โดยทั่วไปจะเป็นฟังก์ชันคงที่CS(ψ)
 
- การแสดงออก - หากคุณมีการแสดงออก- Eของแบบฟอร์ม- E1 ∘ E2(พูดการแสดงออกทางคณิตศาสตร์ที่- ∘อาจจะเพิ่มหรือการคูณคุณจะเพิ่มค่าใช้จ่ายซ้ำ:
 - CE(ψ)=c∘+CE1(ψ)+CE2(ψ)ปอนด์ต่อตารางนิ้ว) - สังเกตได้ว่า - 
- ค่าใช้จ่ายในการดำเนินงานอาจไม่คงที่ แต่ขึ้นอยู่กับค่าของและและc∘E1E2
- การประเมินผลของนิพจน์อาจเปลี่ยนแปลงสถานะในหลายภาษา
 - ดังนั้นคุณอาจต้องยืดหยุ่นกับกฎนี้ 
- ลำดับ - เมื่อให้โปรแกรม- Pเป็นลำดับของโปรแกรม- Q;Rคุณจะต้องเพิ่มค่าใช้จ่าย
 - CP(ψ)=CQ(ψ)+CR(ψ/Q){Q}) 
- เงื่อนไข - ให้โปรแกรม- Pของแบบฟอร์ม- if A then Q else R endค่าใช้จ่ายขึ้นอยู่กับสถานะ:
 - CP(ψ)=CA(ψ)+{CQ(ψ/A)CR(ψ/A),A evaluates to true under ψ,else - โดยทั่วไปการประเมิน- Aอาจเปลี่ยนแปลงสถานะได้ดีดังนั้นการอัพเดทค่าใช้จ่ายของแต่ละสาขา
 
- สำหรับลูป - รับโปรแกรม- Pของแบบฟอร์ม- for x = [x1, ..., xk] do Q endกำหนดค่าใช้จ่าย
 - CP(ψ)=cinit_for+∑i=1kcstep_for+CQ(ψi∘{x:=xi}) - ที่เป็นรัฐก่อนการประมวลผลสำหรับค่าคือหลังจากที่ซ้ำกับที่ถูกตั้งค่าให้, ... ,ψi- Q- xi- x- x1- xi-1
 - สังเกตค่าคงที่พิเศษสำหรับการบำรุงรักษาลูป ต้องสร้างตัวแปรลูป ( ) และกำหนดค่า ( ) สิ่งนี้มีความเกี่ยวข้องตั้งแต่cinit_forcstep_for - 
- การคำนวณครั้งต่อไปxiอาจมีค่าใช้จ่ายสูงและ
- - forลูปที่มีเนื้อความว่างเปล่า (เช่นหลังจากทำให้การตั้งค่าที่ดีที่สุดในกรณีที่ง่ายที่สุดด้วยค่าใช้จ่ายเฉพาะ) ไม่มีค่าใช้จ่ายใด ๆ หากมันทำการวนซ้ำ
 
- ในขณะที่ลูป - รับโปรแกรม- Pของแบบฟอร์ม- while A do Q endกำหนดค่าใช้จ่าย
 - CP(ψ) =CA(ψ)+{0CQ(ψ/A)+CP(ψ/A;Q),A evaluates to false under ψ, else - โดยการตรวจสอบอัลกอริทึมการกลับเป็นซ้ำนี้มักจะแสดงได้อย่างดีว่าเป็นผลรวมที่ใกล้เคียงกับลูปสำหรับลูป - ตัวอย่าง:พิจารณาอัลกอริธึมสั้น ๆ นี้: - while x > 0 do    1
  i += 1          2
  x = x/2         3
end               4
 - โดยการใช้กฎเราจะได้รับ - C1,4({i:=i0;x:=x0}) =c<+{0c+=+c/+C1,4({i:=i0+1;x:=⌊x0/2⌋}),x0≤0, else - ด้วยค่าใช้จ่ายคงที่บางรายการสำหรับแต่ละข้อความสั่ง เราถือว่าโดยปริยายว่าสิ่งเหล่านี้ไม่ได้ขึ้นอยู่กับสถานะ (ค่าของและ); สิ่งนี้อาจจะใช่หรือไม่ใช่จริงใน "ความจริง": คิดว่าล้น!c…- i- x
 - ตอนนี้เราต้องแก้ปัญหาการเกิดซ้ำนี้{1,4} เราทราบว่าจำนวนการทำซ้ำไม่ใช่ค่าใช้จ่ายของลูปบอดี้นั้นขึ้นอยู่กับค่าของดังนั้นเราจึงสามารถวางได้ เราถูกทิ้งให้อยู่กับการเกิดซ้ำนี้:C1,4- i
 - C1,4(x)={c>c>+c+=+c/+C1,4(⌊x/2⌋),x≤0, else - นี้แก้ด้วยวิธีการประถมศึกษาเพื่อ - C1,4(ψ)=⌈log2ψ(x)⌉⋅(c>+c+=+c/)+c> , - แนะนำสถานะเต็มรูปแบบให้เป็นสัญลักษณ์ ถ้าแล้ว5ψ={…,x:=5,…}ψ(x)=5 
- ขั้นตอนการโทร - รับโปรแกรม- Pของฟอร์ม- M(x)สำหรับพารามิเตอร์บางตัวโดย- xที่- Mโพรซีเดอร์ที่มีพารามิเตอร์ (ตั้งชื่อ)- pกำหนดค่าใช้จ่าย
 - CP(ψ)=ccall+CM(ψglob∘{p:=x})\}) - โปรดทราบอีกครั้งว่าค่าคงที่พิเศษ (ซึ่งอันที่จริงแล้วขึ้นอยู่กับ !) การเรียกใช้โปรซีเจอร์นั้นมีราคาแพงเนื่องจากวิธีการใช้งานบนเครื่องจริงและบางครั้งก็ควบคุมการรันไทม์ (เช่นการประเมินจำนวนฟีโบนัชชีซ้ำ ๆ อย่างไร้เดียงสา)ccallψ - ฉันแก้ไขปัญหาทางความหมายบางอย่างที่คุณอาจมีกับรัฐที่นี่ คุณจะต้องการแยกแยะสถานะโกลบอลและโลคอลการเรียกโพรซีเดอร์ ขอเพียงถือว่าเราผ่านรัฐเดียวทั่วโลกที่นี่และ- Mได้รับรัฐท้องถิ่นใหม่เริ่มต้นได้โดยการตั้งค่าของการ- p- xนอกจากนี้- xอาจเป็นนิพจน์ที่เรา (ปกติ) คิดว่าจะได้รับการประเมินก่อนส่ง
 - ตัวอย่าง:พิจารณาขั้นตอน - fac(n) do                  
  if ( n <= 1 ) do         1
    return 1               2
  else                     3
    return n * fac(n-1)    4
  end                      5
end                        
 - ตามกฎเราได้รับ: - Cfac({n:=n0})=C1,5({n:=n0})=c≤+{C2({n:=n0})C4({n:=n0}),n0≤1, else=c≤+{creturncreturn+c∗+ccall+Cfac({n:=n0−1}),n0≤1, else - โปรดทราบว่าเราไม่สนใจสถานะโลกเนื่องจาก- facเห็นได้ชัดว่าไม่สามารถเข้าถึงได้ การเกิดซ้ำโดยเฉพาะนี้ง่ายต่อการแก้ไข
 - Cfac(ψ)=ψ(n)⋅(c≤+creturn)+(ψ(n)−1)⋅(c∗+ccall) 
เราได้ครอบคลุมคุณสมบัติภาษาที่คุณจะพบในรหัสหลอกทั่วไป ระวังค่าใช้จ่ายแอบแฝงเมื่อวิเคราะห์โค้ดหลอกระดับสูง หากมีข้อสงสัยคลี่ออก สัญกรณ์อาจดูยุ่งยากและแน่นอนว่าเป็นเรื่องของรสนิยม แม้ว่าแนวคิดที่ระบุไว้จะไม่สามารถเพิกเฉยได้ อย่างไรก็ตามด้วยประสบการณ์บางอย่างคุณจะสามารถเห็นได้ทันทีว่าส่วนใดของรัฐที่เกี่ยวข้องกับการวัดค่าใช้จ่ายตัวอย่างเช่น "ขนาดปัญหา" หรือ "จำนวนจุดยอด" ส่วนที่เหลือสามารถทิ้งได้ - สิ่งนี้ทำให้สิ่งต่าง ๆ ง่ายขึ้นอย่างมาก!
หากคุณคิดว่าตอนนี้มันซับซ้อนเกินกว่าจะได้รับคำแนะนำ: มันคือ ! การได้รับต้นทุนที่แน่นอนของอัลกอริทึมในแบบจำลองใด ๆ ที่อยู่ใกล้กับเครื่องจักรจริงเพื่อเปิดใช้งานการคาดการณ์แบบรันไทม์ และนั่นไม่ได้พิจารณาถึงการแคชและผลกระทบที่น่ารังเกียจอื่น ๆ ในเครื่องจริง
ดังนั้นการวิเคราะห์อัลกอริธึมจึงง่ายขึ้นจนถึงจุดที่สามารถใช้วิธีทางคณิตศาสตร์ได้ ตัวอย่างเช่นหากคุณไม่ต้องการค่าใช้จ่ายที่แน่นอนคุณสามารถประเมินค่าสูงหรือต่ำเกินไปได้ทุกจุด (สำหรับผู้ที่มีภาระน้อยกว่า): ลดกลุ่มค่าคงที่, กำจัดเงื่อนไข, ลดความซับซ้อนของผลรวมและอื่น ๆ
หมายเหตุเกี่ยวกับค่าใช้จ่ายที่เชิงเส้นกำกับ
สิ่งที่คุณมักจะพบในวรรณกรรมและบนเว็บคือ "การวิเคราะห์ Big-Oh" คำที่เหมาะสมคือการวิเคราะห์เชิงเส้นกำกับซึ่งหมายความว่าแทนที่จะทำให้ได้ค่าใช้จ่ายที่แน่นอนตามที่เราทำในตัวอย่างคุณเพียงแค่ให้ค่าใช้จ่ายถึงปัจจัยคงที่และในขีด จำกัด (พูดโดยประมาณ "สำหรับใหญ่")n
นี่คือ (มัก) ยุติธรรมเนื่องจากงบนามธรรมมีบางส่วนค่าใช้จ่าย (ไม่ทราบโดยทั่วไป) ในความเป็นจริงขึ้นอยู่กับเครื่องระบบปฏิบัติการและปัจจัยอื่น ๆ และเวลาการทำงานสั้นอาจถูกครอบงำโดยระบบปฏิบัติการการตั้งค่าขั้นตอนในสถานที่แรกและ whatnot ดังนั้นคุณจะได้รับการรบกวนบ้าง
นี่คือวิธีการวิเคราะห์เชิงความสัมพันธ์กับวิธีการนี้
- ระบุการดำเนินการที่โดดเด่น (ที่ชักจูงต้นทุน) นั่นคือการดำเนินการที่เกิดขึ้นบ่อยที่สุด (สูงถึงปัจจัยคงที่) ในตัวอย่างของ Bubblesort หนึ่งทางเลือกที่เป็นไปได้คือการเปรียบเทียบในบรรทัดที่ 5 - อีกทางเลือกหนึ่งคือผูกค่าคงที่ทั้งหมดไว้สำหรับการดำเนินการเบื้องต้นด้วยการตอบสนองสูงสุด (จากด้านบน) ขั้นต่ำของพวกเขา (จากด้านล่าง) และดำเนินการวิเคราะห์ตามปกติ 
- ทำการวิเคราะห์โดยใช้จำนวนการดำเนินการของการดำเนินการนี้เป็นต้นทุน
- เมื่อลดความซับซ้อนให้ประมาณการ ใช้ความระมัดระวังเพื่อให้การประมาณการจากด้านบนหากเป้าหมายของคุณเป็นขอบเขตสูงสุด ( ) resp จากด้านล่างหากคุณต้องการลดขอบเขต ( )OΩ
ให้แน่ใจว่าคุณเข้าใจความหมายของสัญลักษณ์กุ๊บ โปรดจำไว้ว่าขอบเขตดังกล่าวอยู่สำหรับทั้งสามกรณี ; การใช้ไม่ได้แปลว่าเป็นกรณีที่เลวร้ายที่สุดO
อ่านเพิ่มเติม
มีความท้าทายและลูกเล่นเพิ่มเติมในการวิเคราะห์อัลกอริทึม นี่คือคำแนะนำในการอ่าน
มีคำถามมากมายที่ติดแท็กการวิเคราะห์อัลกอริทึมโดยใช้เทคนิคที่คล้ายกับสิ่งนี้