Có thuật toán nào tìm thấy các chuỗi được sắp xếp có kích thước ba trong thời gian


21

Tôi muốn chứng minh hoặc bác bỏ sự tồn tại của một thuật toán, với một mảng gồm các số nguyên, tìm thấy ba chỉ số i , jk sao cho i < j < kA [ i ] < A [ j ] < A [ k ] (hoặc thấy rằng không có bộ ba như vậy) trong thời gian tuyến tính.Ai,jki<j<kA[i]<A[j]<A[k]

Đây không phải là một câu hỏi bài tập về nhà; Tôi thấy nó trên một diễn đàn lập trình được đóng khung khi cố gắng thực hiện một thuật toán như vậy. Tôi nghi ngờ rằng điều đó là không thể sau nhiều thử nghiệm khác nhau. Trực giác của tôi nói với tôi như vậy, nhưng điều đó không thực sự được tính cho bất cứ điều gì.

Tôi muốn chứng minh nó chính thức. Bạn làm nó như thế nào? Tôi lý tưởng muốn thấy một bằng chứng được đưa ra từng bước, và sau đó nếu bạn rất có khuynh hướng, một số giải thích về cách đi về việc chứng minh / từ chối các câu hỏi đơn giản như thế này nói chung. Nếu nó giúp, một số ví dụ:

[1,5,2,0,3] → (1,2,3)
[5,6,1,2,3] → (1,2,3)
[1,5,2,3] → (1,2,3)
[5,6,1,2,7] → (1,2,7)
[5,6,1,2,7,8] → (1,2,7)
[1,2,999,3] → (1,2,999)
[999,1,2,3] → (1,2,3)
[11,12,8,9,5,6,3,4,1,2,3] → (1,2,3)
[1,5,2,0,-5,-2,-1] → (-5,-2,-1)

Tôi nghĩ rằng người ta có thể lặp lại , và mỗi lần có một i < j ( j hiện tại của chúng tôi ), chúng tôi tạo ra một bộ ba mới và đẩy nó vào một mảng. Chúng tôi tiếp tục bước và so sánh từng bộ ba cho đến khi một trong ba bộ của chúng tôi hoàn thành. Vì vậy, nó như thế , ! Nhưng tôi nghĩ rằng điều này phức tạp hơn chỉ là O ( n ) vì số lượng bộ ba trên mảng ba của chúng tôi trong trường hợp xấu nhất tương ứng với kích thước của danh sách đầu vào.Ai<jj[1,5,2,0,-5,-2,-1] → 1..2.. -5.. -2.. -1[1,5,2,0,-5,-2,3,-1] → 1..2.. -5.. -2.. 3O(n)



Lưu ý rằng trong trường hợp xấu nhất (mảng được sắp xếp), bạn thậm chí có nhiều gấp ba thích hợp. Vui lòng xem xét đưa ra thuật toán mà bạn đề xuất dưới dạng mã giả; Tôi nghĩ rằng lời giải thích của bạn không đầy đủ. Θ(n3)
Raphael

Câu trả lời:


14

Đây là biến thể của vấn đề tiếp theo tăng dài nhất ; đây là giải pháp được trình bày trên Wikipedia bằng hai mảng phụ P :MP

  • - lưu trữ vị trí k có giá trị nhỏ nhất A [ k ] sao cho có độ dài tăng dần j kết thúc tại A [ k ] trên phạm vi k i (lưu ý chúng tôi có j k i ở đây, bởi vì j đại diện cho độ dài của chuỗi con tăng dần và k đại diện cho vị trí chấm dứt của nó. Rõ ràng, chúng ta không bao giờ có thể có một chuỗi con tăng dài 13 kết thúc ở vị trí 11M[j]kA[k]jA[k]kijkijk1311. theo định nghĩa).ki
  • - lưu trữ vị trí của tiền thân của A [ k ] trong chuỗi tăng dài nhất kết thúc tại A [ k ] .P[k]A[k]A[k]

    Ngoài ra, thuật toán lưu trữ một biến đại diện cho độ dài của chuỗi tăng dài nhất được tìm thấy cho đến nay.L

Thuật toán này chạy trong trường hợp xấu nhất . Vấn đề của bạn là một trường hợp đặc biệt cho phép bạn quay lại khi L = 3 , điều này đẩy thời gian chạy xuống O ( n ) vì tìm kiếm nhị phân chỉ chạy trên các mảng có độ dài nhiều nhất là hai, do đó trong thời gian O ( 1 ) trái ngược với Θ ( log n ) trong trường hợp tổng quát.Θ(nlogn)L=3O(n)O(1)Θ(logn)

Hãy xem xét mã giả đã sửa đổi:

 L = 0
 for i = 1, 2, ... n:
    binary search for the largest positive j ≤ L
      such that X[M[j]] < X[i] (or set j = 0 if no such value exists)
    P[i] = M[j]
    if j == L or X[i] < X[M[j+1]]:
       M[j+1] = i
       L = max(L, j+1)
   if L==3 : return true; // you can break here, and return true.
return false; // because L is smaller than 3.

@SaeedAmiri Tôi đã xem bình luận nhưng tôi chưa có thời gian để xem lại (tôi đã đăng câu hỏi trước khi đi ngủ). Tôi nghi ngờ từ liên kết của bạn rằng trường hợp đặc biệt L = 3 của chúng tôi sẽ giúp bằng cách nào đó nhưng không có cơ hội hiểu chi tiết. Tôi hiện đang làm việc và thời gian bị hạn chế. Hãy yên tâm rằng tôi đánh giá cao câu trả lời của bạn. Tôi sẽ cảm thấy hời hợt khi cảm ơn bạn vì điều đó mà không hiểu đầy đủ từng dòng trong đó.
Christopher Xong

@SaeedAmiri: Tôi đồng ý rằng bạn mong đợi nhiều hơn "điền vào chỗ trống" ở đây, nhưng bạn vẫn phải đưa ra các lập luận góc của một bằng chứng (tuy nhiên sơ sài). Về OP, anh ấy dường như có trụ sở tại Ý nên có lẽ đã ngủ rất nhanh giữa bình luận và câu trả lời của bạn (và rất có thể anh ấy đang bận rộn với phương đông bây giờ).
Raphael

@ChristopherDone, tôi không muốn làm bạn thất vọng, xin lỗi đó là lỗi của tôi, bạn chắc chắn đúng.

+1: Điều này khái quát độc đáo, chỉ thực hiện một lần và là không gian . O(1)
Aryabhata

OK, có vẻ tốt. Tôi phải mất một thời gian để tìm hiểu hành vi của thuật toán trình tự tăng dài nhất nói chung. Sau đó, độ dài tối đa == 3 thay đổi là tốt. Cảm ơn!
Christopher Xong

11

Một lưu ý về phương pháp luận

Tôi nghĩ một chút về vấn đề này, và đi đến một giải pháp. Khi tôi đọc câu trả lời của Saeed Amiri , tôi nhận ra rằng những gì tôi nghĩ ra là một phiên bản chuyên biệt của thuật toán tìm kiếm dài nhất tiêu chuẩn cho một chuỗi dài 3. Tôi đang đăng tải cách tôi đưa ra giải pháp, bởi vì tôi nghĩ nó là một ví dụ thú vị về giải quyết vấn đề.

Phiên bản hai yếu tố

Chúng ta hãy bắt đầu nhỏ: thay vì tìm kiếm ba chỉ số theo đó các phần tử theo thứ tự, chúng ta hãy tìm hai: sao cho A [ i ] < A [ j ] .i<jA[i]<A[j]

Ai<j,A[i]A[j]i,A[i]A[i+1]iA[i]<A[i+1]

Trường hợp này rất đơn giản; chúng tôi sẽ cố gắng khái quát nó. Nó cho thấy rằng vấn đề như đã nêu là không thể giải quyết được: các chỉ số được yêu cầu không phải lúc nào cũng tồn tại. Vì vậy, chúng tôi sẽ yêu cầu thuật toán trả về các chỉ số hợp lệ, nếu chúng tồn tại hoặc tuyên bố chính xác rằng không có chỉ số nào như vậy tồn tại.

Đến với thuật toán

A(A[i1],,A[im])i1<<imA(A[i],A[i+1],,A[i+m1])

Chúng tôi chỉ thấy rằng các chỉ số được yêu cầu không phải lúc nào cũng tồn tại. Chiến lược của chúng tôi sẽ là nghiên cứu khi các chỉ số không tồn tại. Chúng tôi sẽ làm điều này bằng cách giả sử chúng tôi đang cố gắng tìm các chỉ số và xem cách tìm kiếm của chúng tôi có thể sai. Sau đó, các trường hợp tìm kiếm không sai sẽ cung cấp một thuật toán để tìm các chỉ số.

4,3,2,1,0

j=i+1k=j+1A[i]<A[i+1]<A[i+2]

4,3,2,1,2,3,2,1,0

A[j]<A[j+1]iA[i]<A[j]kA[j+1]<A[k]

4,3,2,2,5,1,5,0,5,1,0

ik

3,2,1,3,5,2,5,1,5,0,5, -0,5,1,25, -0,25 3,2,1,2,5,1,5,0,5,2,1,0

ijki

2.1,3,2,1,2,5,1,5,0,5,2,1,0 1,2,0,2,5,1,5,0,5

i(i,j)ki(i,j)(i,j)i>jA[i]<A[i]ii(i,j)jA[j]<A[j](i,j)

Báo cáo thuật toán

Đưa ra theo cú pháp Python, nhưng hãy cẩn thận mà tôi đã không kiểm tra nó.

def subsequence3(A):
    """Return the indices of a subsequence of length 3, or None if there is none."""
    index1 = None; value1 = None
    index2 = None; value2 = None
    for i in range(0,len(A)):
        if index1 == None or A[i] < value1:
            index1 = i; value1 = A[i]
        else if A[i] == value1: pass
        else if index2 == None:
            index2 = (index1, i); value2 = (value1, A[i])
        else if A[i] < value2[1]:
            index2[1] = i; value2[1] = A[i]
        else if A[i] > value2[1]:
            return (index2[0], index2[1], i)
    return None

Bằng chứng phác thảo

index1là chỉ số tối thiểu của một phần của mảng đã được duyệt qua (nếu nó xảy ra nhiều lần, chúng tôi giữ lại lần xuất hiện đầu tiên) hoặc Nonetrước khi xử lý phần tử đầu tiên. index2lưu trữ các chỉ số về độ tăng dần của chiều dài 2 trong phần đã đi qua của mảng có phần tử lớn nhất thấp nhất hoặc Nonenếu một chuỗi như vậy không tồn tại.

Khi return (index2[0], index2[1], i)chạy, chúng ta có value2[0] < value[1](đây là một bất biến value2) và value[1] < A[i](rõ ràng từ bối cảnh). Nếu vòng lặp kết thúc mà không gọi trả về sớm, value1 == Nonethì trong trường hợp đó, không có phần tăng dần của chiều dài 2, hãy để riêng 3 hoặc value1chứa phần tăng dần của chiều dài 2 có phần tử lớn nhất thấp nhất. Trong trường hợp sau, chúng ta còn có một bất biến là không tăng phần sau của chiều dài 3 kết thúc sớm hơn value1; do đó, phần tử cuối cùng của bất kỳ phần tiếp theo nào như vậy, được thêm vào value2, sẽ tạo thành phần tiếp theo tăng dần của chiều dài 3: vì chúng ta cũng có bất biến value2không phải là một phần của phần tiếp theo tăng 3 có trong phần đã đi qua của mảng, ở đó không có như vậy trong toàn bộ mảng.

Chứng minh các bất biến đã nói ở trên là một bài tập cho người đọc.

Phức tạp

O(1)O(1)O(n)

Bằng chứng chính thức

Còn lại như một bài tập cho người đọc.


8

O(n)O(n)

Đầu tiên, di chuyển mảng từ trái sang phải để duy trì một ngăn xếp và một mảng phụ trợ cho bạn biết từng phần tử, chỉ mục của một phần tử lớn hơn nó và bên phải của nó.

1

Mỗi lần bạn xem xét một phần tử mới trong mảng, nếu phần tử đó lớn hơn phần tử trên cùng của ngăn xếp, bạn bật nó ra khỏi ngăn xếp và đặt phần tử mảng phụ tương ứng với phần trên để có chỉ mục của phần tử mới bên dưới cân nhắc.

Tiếp tục bật các phần tử ra khỏi ngăn xếp và thiết lập chỉ mục tương ứng, trong khi phần tử hiện tại lớn hơn. Khi đỉnh có một phần tử không nhỏ hơn (hoặc trở nên trống rỗng), đẩy phần tử hiện tại lên ngăn xếp và tiến hành phần tử tiếp theo của mảng, lặp lại bước trên.

Thực hiện một đường chuyền khác (và một mảng bổ trợ khác), nhưng đi từ phải sang trái.

1

O(n)

ki

Mã giả cho lần đầu tiên có thể trông như thế này:

Stack <Pair<Elem, Index>> greats;
Elem auxArr[inputArr.Length];

for (Index i = 0; i < inputArr.Length; i++) {

    while (!greats.IsEmpty() && inputArr[i] > greats.PeekTop().Elem) {
        Pair top = greats.Pop();
        auxArr[top.Index] = i;
    }

    Pair p;
    p.Elem = inputArr[i];
    p.Index = i;

    greats.Push(p);
}

Vì bạn coi mỗi phần tử của mảng chỉ là một số lần không đổi, đây là thời gian O (n). Bằng cách nào đó tôi đã loại trừ nhiều đường chuyền liên tục, loại bỏ nó không phải là O (n). Rất ngốc. Tôi biết ơn lời giải thích của bạn và tôi sẽ cố gắng giải quyết nó một lần nữa.
Christopher Xong
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.