Подтвердить что ты не робот

Алгоритм для нахождения максимальной подпоследовательности массива положительных чисел. Поймать: не допускаются смежные элементы

Например, данный

A = [1,51,3,1,100,199,3], maxSum = 51 + 1 + 199 = 251.

ясно max(oddIndexSum,evenIndexSum) работает не.

Основная проблема заключается в том, что я не могу придумать критерий выбора для элемента. Критерий отклонения является тривиальным с учетом критерия выбора.

Стандартный максимальный алгоритм подпоследовательности здесь не применим. Я пробовал подход к динамическому программированию, но и не могу этого выдумать. Единственный подход, который я мог придумать, - это тот, который использовал генетический алгоритм.

Как вы подходите к этому?

4b9b3361

Ответ 1

Построить максимальную подпоследовательность можно шаг за шагом, если вы сохраняете два состояния:

def maxsubseq(seq):
  # maximal sequence including the previous item
  incl = []
  # maximal sequence not including the previous item
  excl = []

  for i in seq:
    # current max excluding i
    if sum(incl) > sum(excl):
      excl_new = incl
    else:
      excl_new = excl

    # current max including i
    incl = excl + [i]

    excl = excl_new

  if sum(incl) > sum(excl):
    return incl
  else:
    return excl


print maxsubseq([1,4,6,3,5,7,32,2,34,34,5])

Если вы также хотите иметь отрицательные элементы в своих списках, вам нужно добавить несколько ifs.

То же самое - в меньших строках

def maxsubseq2(iterable):
    incl = [] # maximal sequence including the previous item
    excl = [] # maximal sequence not including the previous item

    for x in iterable:
        # current max excluding x
        excl_new = incl if sum(incl) > sum(excl) else excl
        # current max including x
        incl = excl + [x]
        excl = excl_new

    return incl if sum(incl) > sum(excl) else excl

То же - устранение sum()

def maxsubseq3(iterable):
    incl = [] # maximal sequence including the previous item
    excl = [] # maximal sequence not including the previous item
    incl_sum, excl_sum = 0, 0
    for x in iterable:
        # current max excluding x
        if incl_sum > excl_sum:
            # swap incl, excl
            incl, excl = excl, incl
            incl_sum, excl_sum = excl_sum, incl_sum
        else:
            # copy excl to incl
            incl_sum = excl_sum #NOTE: assume `x` is immutable
            incl     = excl[:]  #NOTE: O(N) operation
        assert incl is not excl
        # current max including x
        incl.append(x)
        incl_sum += x
    return incl if incl_sum > excl_sum else excl

Хорошо, пусть оптимизирует его...

Версия с общим временем выполнения O (n):

def maxsubseq4(iterable):
    incl = [] # maximal sequence including the previous item
    excl = [] # maximal sequence not including the previous item
    prefix = [] # common prefix of both sequences
    incl_sum, excl_sum = 0, 0
    for x in iterable:
        if incl_sum >= excl_sum:
            # excl <-> incl
            excl, incl = incl, excl
            excl_sum, incl_sum = incl_sum, excl_sum
        else:
            # excl is the best start for both variants
            prefix.extend(excl) # O(n) in total over all iterations
            excl = []
            incl = []
            incl_sum = excl_sum
        incl.append(x)
        incl_sum += x
    best = incl if incl_sum > excl_sum else excl
    return prefix + best # O(n) once

Ответ 2

Ответ Криса терпит неудачу в списке [9,10,9], производя 10 вместо 9 + 9 = 18.

Джо не совсем прав. Путешественник требует, чтобы вы посетили каждый город, тогда как здесь нет аналогов.

Одним из возможных решений могло бы быть рекурсивное решение:

function Max_route(A)
    if A length = 1 
        A[0]
      else
        maximum of
          A[0]+Max_route(A[2...])
          Max_route[1...]

Это имеет ту же самую большую-O, что и наивная функция фибоначчи, и для некоторых из тех же оптимизаций (например, memoization), если вы заботитесь об эффективности в дополнение к простому правильному ответу.

- MarkusQ

[Изменить] ---

Потому что некоторые люди, кажется, не получают этого, я хочу объяснить, что я имел в виду под воспоминаниями и почему это имеет значение.

Вы можете обернуть функцию выше, чтобы она только вычисляла значение для каждого массива один раз (при первом его вызове), а при последующих вызовах просто возвращал сохраненный результат. Это займет O (n) пространство, но вернется в постоянное время. Это означает, что весь алгоритм вернется в O (n) раз, лучше, чем экспоненциальное время менее загроможденной версии выше. Я предполагал, что это было хорошо понято.

[Второе редактирование] ------------------------------

Если мы разложим выше сказанное и разделим его на части, получим:

f []      :- [],0
f [x]     :- [x],x
f [a,b]   :- if a > b then [a],a else [b],b
f [a,b,t] :- 
    ft = f t
    fbt = f [b|t]
    if a + ft.sum > fbt.sum
        [a|ft.path],a+ft.sum
      else
        fbt

Что мы можем развернуть в псевдо-базовый, используя только массивы размера n целых чисел и логических чисел, а также операции 1) индексации массива и индексации массива, 2) целочисленную математику, включая сравнение, 3) если /then/else, и 4) одна петля O (n):

dim max_sum_for_initial[n],next_to_get_max_of_initial[n],use_last_of_initial[n]

max_sum_for_initial[0] = 0
next_to_get_max_of_initial[0] = -1
use_last_of_initial[0] = false

max_sum_for_initial[1] = a[0]
next_to_get_max_of_initial[1] = -1
use_last_of_initial[1] = true

if a[0] > a[1]
    max_sum_for_initial[2] = a[0]
    next_to_get_max_of_initial[2] = 0
    use_last_of_initial[2] = false
  else
    max_sum_for_initial[2] = a[1]
    next_to_get_max_of_initial[1] = -1
    use_last_of_initial[2] = true

for i from 3 to n
    if a[i]+max_sum_for_initial[i-2] > max_sum_for_initial[i-1]
        max_sum_for_initial[i] = a[i]+max_sum_for_initial[i-2]
        next_to_get_max_of_initial[i] = i-2
        use_last_of_initial[i] = true
      else
        max_sum_for_initial[i] = max+sum_for_initial[i-1]
        next_to_get_max_of_initial[i] = i-1
        use_last_of_initial[i] = false

В конце мы можем извлечь результаты (в обратном порядке):

for i = n; i >= 0; i = next_to_get_max_of_initial[i]
    if use_last_of_initial[i] then print a[i]

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

Я надеюсь, что это достаточно ясно.

- MarkusQ

Это O (n).

Ответ 3

find_max(int t, int n)
{

     if(t>=n)
       return 0;
     int sum =0, max_sum =0;
     for(int i=t; i<n; ++i)
     {
       sum = sum + A[i];
       for(int j=i+2; j<n; ++j)
          sum = sum + find_max(A[j], n);
       if(sum > max_sum)
          max_sum = sum;
     }
     return max_sum;

}

Вышеупомянутое рекурсивное решение, не скомпилировало его. Это довольно тривиально, чтобы увидеть повторение и преобразовать это в DP. В ближайшее время опубликует.

Ответ 4

Рекурсивный ответ в странном псевдокоде Прологеска:

maxSum([]) = 0
maxSum([x]) = x
maxSum(A) = max(A[0] + maxSum(A[2..n]),
                A[1] + maxSum(A[3..n]))

При соответствующей обработке индексов за пределами диапазона.

Изменить:. Это уменьшает ответ MarcusQ:

maxSum([]) = 0
maxSum(A) = max(A[0] + maxSum(A[2..n]), maxSum(A[1..n]))

Изменить: Здесь версия, которая возвращает фактическую подпоследовательность, а не только ее сумму. Он растягивает границы моей псевдо-Prolog-C Chimera, поэтому я остановлюсь сейчас.

maxSub([]) = []
maxSub(A) = sub1 = [A[0]] + maxSub(A[2..n])
            sub2 = maxSub(A[1..n])
            return sum(sub1) > sum(sub2) ? sub1 : sub2

Ответ 5

@MarkusQ answer как Python oneliner (измененный как @recursive, предложенный в комментариях):

f = lambda L: L and max([L[0]] + f(L[2:]), f(L[1:]), key=sum)

Пример:

>>> f([1,51,3,1,100,199,3])
[51, 1, 199]

Он неэффективен, но может использоваться для тестирования более быстрых решений.

То же самое - в Emacs Lisp

(defun maxsubseq (L)
  "Based on MarkusQ and sth answers."
  (if (not L) L
    (let ((incl (cons (car L) (maxsubseq (cddr L))))
          (excl (maxsubseq (cdr L))))
      (if (> (sum incl) (sum excl)) incl excl))))
(defun sum (L) (apply '+ L))

Итеративная версия (O (N), если имеется хвостовая рекурсия)

Он основан на @sth answer:

(defun maxsubseq-iter-impl (L excl incl)
  (let ((next (if (> (car excl) (car incl)) excl incl)) (x (car L)))
    (if (not L) (cdr next)
      (maxsubseq-iter-impl (cdr L) next
                           (cons (+ x (car excl)) (cons x (cdr excl)))))))

(defun maxsubseq-iter (L) (reverse (maxsubseq-iter-impl L '(0) '(0))))

Пример:

(require 'cl)
(loop for f in '(maxsubseq maxsubseq-iter) 
      collect (loop for L in '((1 51 3 1 100 199 3) (9 10 9)) 
      collect (f L)))

Вывод:

(((51 1 199) (9 9)) ((51 1 199) (9 9)))

Ответ 6

Вот ответ, выполненный с использованием динамического программирования с использованием той же базовой концепции, что и MarkusQ. Я просто вычисляю сумму, а не фактическую последовательность, которая может быть произведена простой модификацией этого примера кода. Я удивлен, что никто не упоминал об этом, потому что динамическое программирование выглядит лучше, чем рекурсия + воспоминания!

int maxSeqSum(int *arr, int size) {
  int i, a, b, c;
  b = arr[0];
  a = (arr[1] > arr[0]) ? arr[1]: arr[0];
  for(i=2;i<size;i++) {
    c = (a > (b + arr[i]))? a : (b + arr[i]);
    b = a;
    a = c;
  }
  return a;
}

Ответ 7

max (oddIndexSum, evenIndexSum) не работает

В примере, который вы указали, он имеет - однако, если у вас есть что-то вроде: A = [1, 51, 3, 2, 41, 23, 20], вы можете иметь 51 + 2 + 23 = 76, или можете 51 + 41 + 20 = 112, который явно больше и позволяет избежать смежных элементов. Это то, что вы ищете?

Ответ 8

Пока вы использовали кучу причудливых слов, разве это не просто простая проблема старого графа коммивояжера?

За исключением этого случая вы ищете самый дорогой маршрут через (плотный) график? В этом случае вершины являются только самими числами, ребра не направлены и не имеют веса, а все вершины связаны, за исключением вершин, которые были смежны с ними в исходном списке?

Ответ 9

Чтобы избежать рекурсии, мы можем взять от реверса вперед,

ie) для Array A [1..n] →

     maxSum(A,n): for all n

         if n=0, maxSum = 0 else
         if n=1, maxSum=A[1] else
                maxSum = max(A[n] + maxSum(A,n-2), maxSum(A,n-1))

Чтобы избежать вычисления Max (A, n-2) при расширении maxSum (A, n-1), его можно сохранить и вычислить. Вот почему я прошу обратить вспять. т.е. maxSum (A, n-1) = max (A [n-1] + maxSum (A, n-3), maxSum (A, n-2)), где в Max (A, n-2) уже получить, и нет необходимости пересчитывать). В других словах вычисляют maxSum (A, n) для всех n, начиная с 1 по n, используя приведенную выше формулу, чтобы избежать перекомпоновки.

ie) n = 2, maxSum = max (A [1] + maxSum (A, 0), maxSum (A, 1)) т.е. n = 3, maxSum = max (A [2] + maxSum (A, 2), maxSum (A, 2)) и т.д. и достигают последнего n. это будет o (n).

Ответ 10

Мы можем использовать вспомогательный массив B [0..n-1], где B [i] - максимальная сумма элементов A [0..i] и C [0..n-1], где C [i] является булевым, если A [i] находится в подпоследовательности максимальной суммы:

B[0]=max(A[0],0); C[0]=(A[0]>0)
B[1]=max(B[0],A[1]); C[1]=(A[1]>B[0])
for i in [2..n-1]
  if A[i]+B[i-2] > B[i-1]
      C[i]=True
      B[i]=A[i]+B[i-2]
  else
      C[i]=False
      B[i]=B[i-1]
mssq=[]
i=n-1
while i>=0
  if C[i]
    push(A[i],mssq)
    i=i-2
  else
    i=i-1
return mssq

Это явно работает в O (n) времени и пространстве. Фактически, это то же самое, что решение MarcusQ, только отменяемое и оптимизированное.

Ответ 11

Edit: Это действительно обман sth, но я не понимал этого, пока не опубликовал его.

Вы можете сделать это в постоянном пространстве и линейном времени, считая, что вам не нужно отслеживать, какие элементы вносят вклад в окончательную сумму.

псевдокод:

sum_excluded_last_item= 0
sum_included_last_item= 0

for each item in list
    if (item>0)
        last_sum_excluded_last_item= sum_excluded_last_item
        sum_excluded_last_item= max(sum_included_last_item, sum_excluded_last_item + item)
        sum_included_last_item= last_sum_excluded_last_item + item
    else
        sum_excluded_last_item= max(sum_excluded_last_item, sum_included_last_item)
        sum_included_last_item= sum_excluded_last_item

max_sum= max(sum_excluded_last_item, sum_included_last_item)

Получение фактического списка - это упражнение, оставленное читателю. Или меня, если вы добавите больше комментариев. Но это должно быть очевидно из алгоритма.

Ответ 12

Код MarkusQ, похоже, полностью пропускает [2]. Я недостаточно умен, чтобы понять, где он должен фигурировать в расчете.

Ответ 13

while you still have elements
     find the largest element, add it to the sum
     remove the element before and after the current