Существует ли алгоритм, который находит отсортированные подпоследовательности размера три за


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)
Рафаэль

Ответы:


14

Это вариация самой длинной возрастающей проблемы подпоследовательности ; это решение, представленное в Википедии с использованием двух вспомогательных массивов и P :MP

  • - хранит позицию k наименьшего значения A [ k ] , так что существует возрастающая подпоследовательность длины j, заканчивающаяся на A [ k ] в диапазоне k i (обратите внимание, что здесь j k i , потому что j представляет длину возрастающей подпоследовательности, а k представляет позицию ее завершения. Очевидно, что у нас никогда не может быть возрастающей подпоследовательности длины 13, заканчивающейся в позиции 11M[j]kA[k]jA[k]kijkijk1311, по определению).ki
  • - сохраняет положение предшественника A [ k ] в самой длинной возрастающей подпоследовательности, заканчивающейся в A [ k ] .P[k]A[k]A[k]

    Кроме того, алгоритм хранит переменную представляющую длину самой длинной увеличивающейся подпоследовательности, найденной до сих пор.L

Этот алгоритм работает в наихудшее время . Ваша проблема - это особый случай, который позволяет вам вернуться, когда L = 3, что понижает время выполнения до O ( n ), потому что бинарный поиск выполняется только по массивам длиной не более двух, что, следовательно, во времени O ( 1 ), а не Θ ( журнал п ) в общем случае.Θ(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 как-то поможет, но не смог понять деталей. Я в настоящее время на работе и ограничен во времени. Будьте уверены, что я ценю ваш ответ. Было бы поверхностно с моей стороны поблагодарить вас за это, не понимая каждую строчку в нем.
Кристофер Совершено

@SaeedAmiri: Я согласен, что вы ожидаете больше «заполнения пробелов» здесь, но вы все равно должны дать хотя бы угловые аргументы доказательства (хотя и отрывочно). Что касается ОП, он, кажется, находится в Италии, поэтому, вероятно, крепко спал между вашим комментарием и ответом (и, скорее всего, он сейчас занят восточным).
Рафаэль

@ ChristopherDone, я не хочу тебя расстраивать, извини, это моя ошибка, ты определенно прав.

O(1)

ОК, выглядит хорошо Мне потребовалось некоторое время, чтобы разобраться в поведении общего алгоритма с наибольшей возрастающей последовательностью. После этого, максимальная длина == 3 изменения в порядке. Благодарность!
Кристофер Совершено

11

Примечание о методологии

Я немного подумал об этой проблеме и пришел к решению. Когда я прочитал ответ Саида Амири , я понял, что я разработал специализированную версию стандартного алгоритма поиска самой длинной подпоследовательности для последовательности длины 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)

Постановка алгоритма

Приведено в синтаксисе Python, но учтите, что я его не проверял.

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хранит индексы возрастающей подпоследовательности длины 2 в уже пройденной части массива, который имеет наименьший наибольший элемент, или, Noneесли такая последовательность не существует.

Когда return (index2[0], index2[1], i)работает, мы имеем value2[0] < value[1](это инвариант value2) и value[1] < A[i](очевидно из контекста). Если цикл заканчивается без вызова досрочного возврата, то value1 == Noneв этом случае нет увеличивающейся подпоследовательности длины 2, не говоря уже о 3, или value1содержит возрастающую подпоследовательность длины 2, которая имеет самый низкий самый большой элемент. В последнем случае, кроме того, у нас есть инвариант, что никакая возрастающая подпоследовательность длины 3 не заканчивается раньше, чем value1; следовательно, последний элемент любой такой подпоследовательности, добавленный к value2, будет формировать возрастающую подпоследовательность длины 3: поскольку у нас также есть инвариант, который value2не является частью увеличивающейся подпоследовательности длины 3, содержащейся в уже пройденной части массива, нет такой подпоследовательности во всем массиве.

Доказательство вышеупомянутых инвариантов оставлено в качестве упражнения для читателя.

сложность

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

Формальное доказательство

Оставлено в качестве упражнения для читателя.


8

O(n)O(n)

Во-первых, просмотрите массив слева направо, поддерживая стек и вспомогательный массив, который сообщает вам для каждого элемента, индекс элемента больше его и справа от него.

1

Каждый раз, когда вы рассматриваете новый элемент в массиве, если этот элемент больше, чем верхний элемент стека, вы извлекаете его из стека и устанавливаете для элемента массива aux, соответствующего верхнему, индекс нового элемента ниже рассмотрение.

Продолжайте выталкивать элементы из стека и устанавливать соответствующий индекс, пока текущий элемент больше. Как только у вершины есть элемент, который не меньше (или становится пустым), поместите текущий элемент в стек и перейдите к следующему элементу массива, повторяя вышеуказанный шаг.

Сделайте еще один проход (и еще один вспомогательный массив), но двигайтесь справа налево.

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). Очень тупой. Я благодарен за ваше объяснение, и я попытаюсь еще раз решить его.
Кристофер Совершено
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.