시간 에서 크기가 3 인 정렬 된 서브 시퀀스를 찾는 알고리즘이 있습니까?


21

정수 배열 가 주어지면 및 와 같이 세 개의 인덱스 및 찾는 알고리즘의 존재를 증명하거나 반증하고 싶습니다 선형 시간으로 (또는 그러한 트리플이 없다는 것을 알게 됨).i , j k i < j < k A [ i ] < A [ j ] < A [ k ]Ai,jki<j<kA[i]<A[j]<A[k]

이것은 숙제 문제가 아닙니다. 나는“그런 알고리즘을 구현하려고 시도해라”라는 프로그래밍 포럼에서 그것을 보았다. 나는 다양한 실험 후에 불가능하다고 생각한다. 내 직감이 그렇게 말하지만 실제로는 아무것도 계산하지 않습니다.

공식적으로 증명하고 싶습니다. 어떻게합니까? 이상적으로 단계별로 증명 된 증거를보고 싶고, 만약 당신이 그렇게 기울어 졌다면 일반적으로 이와 같은 간단한 질문을 증명 / 반증하는 방법에 대한 설명이 있습니다. 도움이된다면 몇 가지 예 :

[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)

나는 에 대해 반복 할 수 있다고 가정했으며 , (현재 )가있을 때마다 새로운 트리플을 만들고 배열에 밀어 넣습니다. 우리는 트리플 중 하나가 완료 될 때까지 각 트리플을 계속 스테핑하고 비교합니다. 이처럼 그래서 , ! 그러나 트리플 배열의 트리플 수가 최악의 경우 입력 목록의 크기에 해당하므로 보다 더 복잡하다고 생각 합니다.i < j j O ( n )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)



최악의 경우 (정렬 된 배열) 많은 적합한 트리플이 있습니다. 제안한 알고리즘을 의사 코드로 제공하십시오. 귀하의 설명이 완전하지 않다고 생각합니다. Θ(n3)
Raphael

답변:


14

이것은 가장 긴 증가 하는 서브 시퀀스 문제의 변형입니다 . 이것은 두 개의 보조 배열 과 사용하여 Wikipedia에 제시된 솔루션입니다 .PMP

  • k A [ k ] j A [ k ] k i j k i j k 13 11 k iM[j] — 범위 에서 끝나는 길이 하위 시퀀스가 ​​증가 하도록 가장 작은 값 의 위치 를 저장합니다 (참고 : 있기 때문에 여기에서는, 증가하는 시퀀스의 길이를 나타내며, 그 종단의 위치를 나타냅니다. 분명히, 우리는 길이의 증가 서브 가질 수 없어 의 위치에서 끝나는 . 정의에 의해).kA[k]jA[k]kijkijk1311ki
  • A [ k ] A [ k ]P[k] — 에서 끝나는 가장 긴 하위 시퀀스에서 의 선행 작업 위치를 저장합니다 .A[k]A[k]

    또한, 알고리즘은 지금까지 발견 된 가장 긴 증가하는 서브 시퀀스의 길이를 나타내는 변수 저장한다 .L

이 알고리즘은 최악의 시간 됩니다. 귀하의 문제는 당신이 반환 할 수 있습니다 특별한 경우 일 때 푸시가 아래로 런타임 바이너리 서치는 시간에 따라서 인이 기껏해야 길이의 배열에서만 실행되기 때문에 에 반대 일반적인 경우 .L = 3 O를 ( N ) O ( 1 ) Θ ( 로그 N )Θ(nlogn)L=3O(n)O(1)Θ(logn)

수정 된 의사 코드를 고려하십시오.

 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 나는 의견을 보았지만 아직 그것을 검토 할 시간이 없었습니다 (잠자리에 들기 전에 질문을 게시했습니다). 귀하의 링크에서 우리의 특별한 경우 L = 3이 어떻게 든 도움이 될 것이라고 생각했지만 세부 사항을 이해할 수는 없었습니다. 나는 현재 일하고 있고 시간이 제한되어있다. 귀하의 답변에 감사드립니다. 그 안에있는 모든 줄을 완전히 이해하지 못한 채 감사합니다.
Christopher Done

@SaeedAmiri : 여기에 더 많은 "갭 필링"을 기대하지만, 여전히 증거 (그러나 스케치)의 모퉁이 주장을 제시해야합니다. OP와 관련하여 그는 이탈리아에 기반을 둔 것으로 보이므로 귀하의 의견과 답변 사이에서 잠이 빨리 들었을 것입니다.
Raphael

@ChristopherDone, 난 당신을 화나게하고 싶지 않아, 미안 그것은 내 실수, 당신은 확실히 맞아.

+1 : 이것은 잘 일반화되고 한 번만 통과하며O(1) 공간입니다.
Aryabhata

좋아, 좋아 보인다 가장 오래 걸리는 시퀀스 알고리즘의 동작을 파악하는 데 시간이 걸렸습니다. 그 후, 최대 길이 = 3의 변화는 괜찮습니다. 감사!
Christopher Done

11

방법론에 대한 메모

나는이 문제에 대해 조금 생각하고 해결책을 찾았습니다. Saeed Amiri의 답변을 읽었을 때 , 내가 생각해 낸 것은 일련의 길이 3에 대한 표준 가장 긴 하위 시퀀스 찾기 알고리즘의 특수 버전이라는 것을 깨달았습니다. 문제 해결의 흥미로운 예입니다.

두 요소 버전

i<jA[i]<A[j]

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

이 경우는 매우 간단합니다. 우리는 그것을 일반화하려고 노력할 것입니다. 명시된 문제는 해결할 수 없음을 나타냅니다. 요청한 인덱스가 항상 존재하지는 않습니다. 따라서 알고리즘이 유효한 인덱스를 반환하거나 해당 인덱스가 존재하지 않는다고 올바르게 주장하도록 요청합니다.

알고리즘으로 올라와

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

요청한 인덱스가 항상 존재하지는 않습니다. 우리의 전략은 지수가 존재하지 않을 때 연구하는 것입니다. 우리는 인덱스를 찾으려고 시도하고 어떻게 검색이 잘못되는지를보고 가정합니다. 그러면 검색이 잘못되지 않는 경우 인덱스를 찾기위한 알고리즘이 제공됩니다.

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)

알고리즘 진술

파이썬 구문으로 주어졌지만 테스트하지 않았 음을주의하십시오.

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

증거 스케치

index1이미 순회 된 배열 부분의 최소 인덱스입니다 (여러 번 발생하면 첫 번째 항목을 유지함). 또는 None첫 번째 요소를 처리하기 전입니다. index2가장 큰 요소를 갖는 배열의 이미 순회 된 부분에 또는 None그러한 시퀀스가 ​​존재하지 않는 경우 길이 2의 증가하는 하위 시퀀스의 인덱스를 저장 합니다.

return (index2[0], index2[1], i)실행 하면 value2[0] < value[1](이것은 변하지 않습니다 value2) 및 value[1] < A[i](컨텍스트 와는 분명합니다). 루프가 초기 리턴을 호출하지 않고 종료 value1 == None하는 경우, 길이 3의 하위 시퀀스가 ​​증가하지 않고 3 value1가장 큰 요소를 갖는 길이 2의 하위 시퀀스가 ​​증가합니다. 후자의 경우, 우리는 길이 3의 하위 시퀀스가 ​​더 이상 끝나지 않는다는 불변성을 갖는다 value1. 따라서 이러한 하위 시퀀스의 마지막 요소는에 추가되어 value2길이 3의 하위 시퀀스를 증가시킵니다. 3 : value2배열의 이미 순회 된 부분에 포함 된 길이 3의 하위 시퀀스 증가의 일부가 아닌 불변 값도 있으므로 전체 배열에서 이러한 하위 시퀀스가 ​​아닙니다.

상기 한 불변량을 증명하는 것은 독자의 연습으로 남겨둔다.

복잡성

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

공식적인 증거

독자에게 연습으로 남겨 두십시오.


8

O(n)O(n)

먼저 스택을 유지하면서 배열을 왼쪽에서 오른쪽으로 이동하고 보조 배열은 각 요소에 대해 요소보다 큰 요소의 색인과 오른쪽을 알려줍니다.

1

배열에서 새 요소를 고려할 때마다 해당 요소가 스택의 최상위 요소보다 큰 경우 스택에서 해당 요소를 꺼내고 맨 위에 해당하는 보조 배열 요소를 설정하여 새 요소의 색인을 고려.

현재 요소가 더 큰 동안 스택에서 요소를 계속 팝하고 해당 인덱스를 설정하십시오. 상단에 더 작거나 비어있는 요소가 있으면 현재 요소를 스택으로 밀고 배열의 다음 요소로 진행하여 위 단계를 반복합니다.

다른 패스 (및 다른 보조 배열)를 만들지 만 오른쪽에서 왼쪽으로갑니다.

1

O(n)

ki

첫 번째 패스의 의사 코드는 다음과 같습니다.

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);
}

“배열의 각 요소를 일정한 횟수 만 고려하기 때문에 이것은 O (n) 시간입니다.”오, 크럼 스. 어떻게 든 여러 상수 패스를 배제하여 O (n)이 아닌 것으로 버렸습니다. 매우 어리석은. 설명해 주셔서 감사 드리며 다시 한 번 해결해 드리겠습니다.
Christopher Done
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.