Làm thế nào là thuật toán sắp xếp này (n³) chứ không phải Θ (n²), trường hợp xấu nhất?


52

Tôi mới bắt đầu tham gia một khóa học về Cấu trúc dữ liệu và thuật toán và trợ lý giảng dạy của tôi đã cho chúng tôi mã giả sau đây để sắp xếp một mảng các số nguyên:

void F3() {
    for (int i = 1; i < n; i++) {
        if (A[i-1] > A[i]) {
            swap(i-1, i)
            i = 0
        }
    }
}

Có thể không rõ ràng, nhưng ở đây n là kích thước của mảng Amà chúng ta đang cố gắng sắp xếp.

Trong mọi trường hợp, trợ lý giảng dạy đã giải thích với lớp rằng thuật toán này đang trong thời gian Θ(n3) (trường hợp xấu nhất, tôi tin), nhưng cho dù tôi có trải qua bao nhiêu lần với một mảng được sắp xếp ngược lại, dường như với tôi rằng nó phải là Θ(n2) và không Θ(n3) .

Ai đó có thể giải thích cho tôi tại sao đây là chứ không phải ?Θ(n3)Θ(n2)


Bạn có thể quan tâm đến một cách tiếp cận có cấu trúc để phân tích ; cố gắng tìm một bằng chứng cho mình!
Raphael

Chỉ cần thực hiện nó và biện pháp để thuyết phục chính mình. Một mảng có 10.000 phần tử theo thứ tự đảo ngược sẽ mất nhiều phút và một mảng có 20.000 phần tử theo thứ tự đảo ngược sẽ mất khoảng tám lần nữa.
gnasher729

@ gnasher729 Bạn đang không sai, nhưng giải pháp của tôi là khác nhau: nếu bạn cố gắng để chứng minh bạn ràng buộc bạn sẽ luôn thất bại, mà sẽ cho bạn biết somethings không ổn. (Tất nhiên, người ta có thể làm cả hai Vẽ / phù hợp chắc chắn nhanh hơn cho bác bỏ giả thuyết, nhưng. Ít đáng tin cậy Chừng nào bạn làm một số loại để phân tích chính thức / cấu trúc, không có hại được thực hiện.. Dựa trên lô là nơi rắc rối bắt đầu.)O(n2)
Raphael

1
i = 0tuyên bố
njzk2

Câu trả lời:


60

Thuật toán này có thể được viết lại như thế này

  1. Quét Acho đến khi bạn tìm thấy một sự đảo ngược .
  2. Nếu bạn tìm thấy một, trao đổi và bắt đầu lại.
  3. Nếu không có, chấm dứt.

Bây giờ có thể có tối đa và bạn cần quét thời gian tuyến tính để tìm từng - vì vậy thời gian chạy trong trường hợp xấu nhất là . Một ví dụ giảng dạy tuyệt vời khi nó đi lên theo cách tiếp cận phù hợp với nhiều mô hình!(n2)Θ(n2)Θ(n3)

Lưu ý: Người ta phải cẩn thận một chút: một số nghịch đảo xuất hiện sớm, một số muộn, do đó, sẽ không có gì là tầm thường khi các chi phí cộng lại như đã yêu cầu (đối với giới hạn dưới). Bạn cũng cần phải quan sát rằng các giao dịch hoán đổi không bao giờ đưa ra các nghịch đảo mới . Một phân tích chi tiết hơn về trường hợp với mảng được sắp xếp ngược lại sau đó sẽ mang lại một cái gì đó giống như trường hợp bậc hai của công thức Gauss '.

Như @ gnasher729 nhận xét một cách khéo léo, thật dễ dàng để thấy thời gian chạy trong trường hợp xấu nhất là bằng cách phân tích thời gian chạy khi sắp xếp đầu vào (mặc dù đầu vào này có lẽ không phải trường hợp xấu nhất).Ω(n3)[1,2,,n,2n,2n1,,n+1]

Hãy cẩn thận: đừng cho rằng một mảng được sắp xếp ngược sẽ nhất thiết phải là đầu vào trong trường hợp xấu nhất cho tất cả các thuật toán sắp xếp. Điều đó phụ thuộc vào thuật toán. Có một số thuật toán sắp xếp trong đó một mảng được sắp xếp ngược không phải là trường hợp xấu nhất và thậm chí có thể gần với trường hợp tốt nhất.


14
Nếu bạn lấy một mảng trong đó nửa đầu bao gồm các số từ 1 đến n / 2 theo thứ tự tăng dần và nửa sau là n đến n / 2 + 1 theo thứ tự đảo ngược, thì rõ ràng là bạn cần ít nhất n / 2 các bước để tìm từng nghịch đảo và sẽ có khoảng (n / 2) ^ 2/2 trong số đó. Và đó rất có thể không phải là trường hợp xấu nhất.
gnasher729

@AnthonyRossello Đó là một kết quả tiêu chuẩn (trong tổ hợp các hoán vị). Nói tóm lại, hãy đếm số lượng nghịch đảo trong mảng được sắp xếp ngược lại (rõ ràng đó có phải là trường hợp xấu nhất không?); đó là tổng Gauss.
Raphael

Người ta phải nhớ rằng không có vấn đề gì, tổng một phần của luôn là , đó chỉ là hệ số giảm nhanh chóng: (lưu ý hệ số khá lớn ). Vấn đề là, không quan tâm đến các hệ số. Θ(nα)Θ(nα+1)k=0nkα1α+1nα+11α+1Θ
yo '

2
@yo 'Và điều này liên quan đến câu trả lời (hoặc câu hỏi) như thế nào?
Raphael

7

Một cách khác để suy nghĩ về điều này là giá trị tối đa itrở thành trước khi nó được đặt lại. Điều này, như hóa ra, làm cho nó đơn giản hơn để suy luận về cách thứ tự sắp xếp trước Aảnh hưởng đến thời gian chạy của thuật toán.

Cụ thể, quan sát rằng khi iđặt giá trị tối đa mới của nó, hãy gọi nó là N, mảng [A[0], ..., A[N-1]]được sắp xếp theo thứ tự tăng dần.

Vậy điều gì xảy ra khi chúng ta thêm phần tử A[N]vào hỗn hợp?

Toán học:

Vâng, giả sử nó phù hợp ở vị trí . Sau đó, chúng ta cần các vòng lặp (mà tôi sẽ biểu thị ) để di chuyển nó đến các lần lặp , để di chuyển nó đến vị trí và nói chung:pNNstepsN1N+(N1)N2

stepsN(pN)=N+(N1)+(N2)++(pN+1)=12(N(N+1)pN(pN+1))

Đối với một mảng được sắp xếp ngẫu nhiên, lấy phân phối đồng đều trên cho mỗi , với:pN{0,1,,N}N

E(stepsN(pN))=a=1NP(pN=a)stepsN(a)=a=1N1N12(N(N+1)a(a+1))=12(N(N+1)13(N+1)(N+2))=13(N21)=Θ(N2)

tổng có thể được hiển thị bằng công thức Faulhaber hoặc liên kết Wolfram Alpha ở phía dưới.

Đối với một mảng được sắp xếp ngược, cho tất cả và chúng tôi nhận được:pN=0N

stepsN(pN)=12N(N+1)

chính xác, mất nhiều thời gian hơn bất kỳ giá trị nào khác của .pN

Đối với một mảng đã được sắp xếp, và , với các điều khoản bậc thấp trở nên có liên quan.pN=NstepsN(pN)=0

Tổng thời gian:

Để có được tổng số thời gian, chúng tôi tổng hợp các bước trên tất cả các . (Nếu chúng tôi cực kỳ cẩn thận, chúng tôi sẽ tổng hợp các giao dịch hoán đổi cũng như các vòng lặp và quan tâm đến các điều kiện bắt đầu và kết thúc, nhưng thật dễ dàng để thấy rằng chúng không góp phần vào sự phức tạp trong hầu hết các trường hợp) .N

Và một lần nữa, sử dụng tính tuyến tính của kỳ vọng và Công thức của Faulhaber's:

Expected Total Steps=E(N=1nstepsN(pN))=N=1nE(stepsN(pN))=Θ(n3)

Tất nhiên, nếu vì một lý do nào đó không phải là (ví dụ: phân phối mảng mà chúng tôi đang xem đã rất gần với việc sắp xếp), thì điều này không phải lúc nào cũng cần là trường hợp. Nhưng phải mất các bản phân phối rất cụ thể trên để đạt được điều này!stepsN(pN)Θ(N2)pN

Đọc có liên quan:


@Raphael - cảm ơn những cải tiến được đề xuất, tôi đã thêm một chút chi tiết. Chà, các biến ngẫu nhiên là (từ , tập hợp các thứ tự ), vì vậy các kỳ vọng được thực hiện về mặt kỹ thuật so vớipiΩAΩ
David E

Khác nhau ; Tôi có nghĩa là một Landau. Ω
Raphael

3

Tuyên bố miễn trừ trách nhiệm:

Đây không phải là một bằng chứng (có vẻ như một số người nghĩ rằng tôi đã đăng nó như thể nó là). Đây chỉ là một thử nghiệm nhỏ mà OP có thể thực hiện để giải quyết những nghi ngờ của mình về nhiệm vụ:

bất kể bao nhiêu lần tôi đi qua nó với một mảng được sắp xếp ngược lại, đối với tôi nó dường như là chứ không phải .Θ(n2)Θ(n3)

Với một mã đơn giản như vậy, sự khác biệt giữa và không khó để nhận ra và trong nhiều trường hợp thực tế, đây là một cách tiếp cận hữu ích để kiểm tra linh cảm hoặc điều chỉnh kỳ vọng.Θ(n2)Θ(n3)


@Raphael đã trả lời câu hỏi của bạn đã có, nhưng chỉ cần cho đá, phù hợp của chương trình này ra để sử dụng kịch bản gnuplot này báo cáo giá trị số mũ của và và sản xuất các lô sau ( đầu tiên là quy mô bình thường và thứ hai là quy mô log-log):f(x)=axb+cx2.997961668332222.99223727692339

bình thường loglog

Tôi hy vọng điều này sẽ giúp¨


2
Bạn có thể phù hợp với bất kỳ chức năng cho các giá trị này. Xem thêm tại đây .
Raphael

3
@Raphael Nếu bạn không sử dụng nitpick theo cách này thì không, bạn không thể phù hợp với bất kỳ chức năng nào (ví dụ: bạn sẽ không thể điều chỉnh một chức năng không đổi với bất kỳ độ chính xác hợp lý nào). Đây không phải là một bằng chứng, nhưng đã có một câu trả lời cung cấp một bản phác thảo. Về tính hữu dụng, tôi có thể trích dẫn bài đăng của chính bạn mà bạn đã liên kết: "Tôi phải đồng ý rằng đây là một cách tiếp cận rất hữu ích thậm chí đôi khi không được sử dụng". Hơn nữa, OP cho biết anh ta nghĩ rằng nó nên là chứ không phải , vậy tại sao không thử nghiệm và xem liệu linh cảm của anh ta có đúng không? Tiếp Θ(n2)Θ(n3)
dtldarek

2
Điều này cung cấp bằng chứng cho thấy thuật toán là nhưng câu hỏi hỏi tại sao . Đó là yêu cầu một lời giải thích về hiện tượng này, không phải là một xác nhận về nó. Θ(n3)
David Richerby

2
@DavidR Richby Điều này có nghĩa là câu trả lời này không hữu ích?
dtldarek

3
@Magicsowon Đó là một trang web câu hỏi và câu trả lời, không phải là một diễn đàn. Chúng tôi đang tìm kiếm câu trả lời cho câu hỏi, không phải thảo luận xung quanh nó.
David Richerby

3

Giả sử bạn có một mảng.

array a[10] = {10,8,9,6,7,4,5,2,3,0,1}

Thuật toán của bạn thực hiện như sau

Scan(1) - Swap (10,8) => {8,10,9,6,7,4,5,2,3,0,1}  //keep looking at "10"
Scan(2) - Swap (10,9) => {8,9,10,6,7,4,5,2,3,0,1}
...
Scan(10) - Swap(10,1) => {8,9,6,7,4,5,2,3,0,1,10}

Về cơ bản, nó di chuyển đến cuối mảng, phần tử cao nhất và khi thực hiện nó sẽ bắt đầu quá mức ở mỗi lần quét một cách hiệu quả khi O(n^2)di chuyển .. chỉ cho một phần tử đó. Tuy nhiên, có n yếu tố nên chúng ta sẽ phải lặp lại nlần này . Đây không phải là một bằng chứng chính thức, nhưng nó giúp hiểu theo cách "không phù hợp" tại sao thời gian chạy là O(n^3).


4
Điều này thêm vào những câu trả lời khác là gì? Một lời giải thích về những gì thuật toán đã được đưa ra, và lý do của bạn cho thời gian chạy là sơ sài. (Trường hợp xấu nhất không hành xử tuyến tính!)
Raphael

2
Đôi khi có giá trị trong việc giải thích cùng một ý tưởng theo nhiều cách (với chủ nghĩa hình thức; với một ví dụ đơn giản để "bơm trực giác"), đặc biệt khi người đặt câu hỏi là mới đối với lĩnh vực này. Vì vậy, dường như với tôi điều này thêm vào là nó được trình bày theo cách có thể hỗ trợ trực giác.
DW

Vì tôi đã nhận được câu trả lời cho nhận xét của mình trong một lá cờ (đừng làm vậy!): "Trường hợp xấu nhất không hành xử tuyến tính!" - Ý tôi là các tính chất đại số của toán tử trường hợp xấu nhất. Nói một cách đơn giản, bạn đang sử dụng WorstCase (1 + ... + n) "=" WorstCase (1) + ... + WorstCase (n) nhưng danh tính này không giữ được.
Raphael

1
Tôi mới tham gia vào lĩnh vực này và đưa ra lời giải thích với một ví dụ cụ thể, được đánh vần chắc chắn đã giúp tôi có được trực giác về vấn đề này. Bây giờ giải pháp được chấp nhận có ý nghĩa hơn với tôi.
vaer-k

0

Logic dường như sắp xếp các phần tử trong mảng theo thứ tự tăng dần.

Giả sử số nhỏ nhất nằm ở cuối mảng (a [n]). Để nó đến đúng vị trí của nó - các thao tác (n + (n-1) + (n-2) + ... 3 + 2 + 1) là bắt buộc. = O (n2).

Đối với một phần tử duy nhất trong mảng O (n2) ops là bắt buộc. Vì vậy, với n eements, nó là O (n3).


5
Điều này thêm vào những câu trả lời khác là gì? Một lời giải thích về những gì thuật toán đã được đưa ra, và lý do của bạn cho thời gian chạy là sơ sài. (Trường hợp xấu nhất không hành xử tuyến tính!)
Raphael

Giải thích tuyệt vời. Điều này cung cấp một quan điểm khác, trực quan hơn về vấn đề, không được giải thích trong các câu trả lời khác. (Không đề cập đến rất ngắn và dễ hiểu.)
2501

1
@ 2501 Không, nó sai. Hãy thử sử dụng "trực giác" này trên thuật toán của Dijkstra và bạn sẽ nhận được thời gian chạy bậc hai (theo số lượng nút), điều này là sai.
Raphael

@Raphael Không, nó đúng, như đã giải thích trong câu trả lời. Giải thích này hoạt động cho thuật toán này, không phải cho người khác. Mặc dù điều này có thể sai đối với họ, nhưng tuyên bố này không chứng minh điều đó là sai đối với điều này.
2501

@Raphael Tôi không hiểu lời giải thích trong câu trả lời được chấp nhận. Vì vậy, tôi đã giải quyết vấn đề này và cố gắng giải thích nó bằng những thuật ngữ đơn giản mà không có bất kỳ thuật ngữ kỹ thuật nào .. vì vậy, đây là dành cho những thành viên như tôi không thể hiểu câu trả lời được chấp nhận .. Tôi rất vui khi ai đó tìm thấy điều này hữu ích.
mk ..
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.