В python, как эффективно найти самый большой последовательный набор чисел в списке, которые не обязательно смежны?
Например, если у меня есть список
[1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11]
Этот алгоритм должен возвращать [1,2,3,4,5,6,7,8,9,10,11].
Чтобы уточнить, самый длинный список должен работать вперед. Мне было интересно, что это алгоритмически эффективный способ сделать это (желательно не O (n ^ 2))?
Кроме того, я открыт для решения не в python, так как алгоритм имеет значение.
Спасибо.
Ответы
Ответ 1
Вот простое однопроходное решение O (n):
s = [1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11,42]
maxrun = -1
rl = {}
for x in s:
run = rl[x] = rl.get(x-1, 0) + 1
print x-run+1, 'to', x
if run > maxrun:
maxend, maxrun = x, run
print range(maxend-maxrun+1, maxend+1)
Логика может быть немного более очевидной, если вы думаете о терминах вместо отдельных переменных для конечной точки и длины выполнения:
rl = {}
best_range = xrange(0)
for x in s:
run = rl[x] = rl.get(x-1, 0) + 1
r = xrange(x-run+1, x+1)
if len(r) > len(best_range):
best_range = r
print list(best_range)
Ответ 2
Не тот умный, а не O (n), мог бы немного оптимизировать. Но он работает.
def longest(seq):
result = []
for v in seq:
for l in result:
if v == l[-1] + 1:
l.append(v)
else:
result.append([v])
return max(result, key=len)
Ответ 3
Вы можете использовать "Сортировать по терпению" Самый большой восходящий суб- алгоритм последовательности
def LargAscSub(seq):
deck = []
for x in seq:
newDeck = [x]
i = bisect.bisect_left(deck, newDeck)
deck[i].insert(0, x) if i != len(deck) else deck.append(newDeck)
return [p[0] for p in deck]
И вот результаты теста
>>> LargAscSub([1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> LargAscSub([1, 2, 3, 11, 12, 13, 14])
[1, 2, 3, 11, 12, 13, 14]
>>> LargAscSub([11,12,13,14])
[11, 12, 13, 14]
Порядок сложности - O (nlogn)
В ссылке wiki была одна заметка, в которой утверждалось, что вы можете достичь O (n.loglogn), полагаясь на дерево Ван Эмде Боас
Ответ 4
Как насчет использования измененного Radix Sort? Как отметила Яннекарила, решение не является O (n). Он использует сортировку Radix, о которой говорит wikipedia Radix sort efficiency is O(k·n) for n keys which have k or fewer digits.
Это будет работать, только если вы знаете диапазон чисел, с которыми мы имеем дело, так что это будет первый шаг.
-
Посмотрите на каждый элемент в стартовом списке, чтобы найти самый низкий, l
и самый высокий, h
номер. В этом случае l
равно 1 и h
равно 11. Примечание. Если вы уже знаете диапазон по какой-либо причине, вы можете пропустить этот шаг.
-
Создайте список результатов размером нашего диапазона и установите для каждого элемента значение null.
-
Посмотрите на каждый элемент в списке и добавьте их в список результатов в соответствующем месте, если это необходимо. т.е. элемент равен 4, добавьте 4 в список результатов в позиции 4. result[element] = starting_list[element]
. Вы можете выбросить дубликаты, если хотите, они просто будут перезаписаны.
-
Перейдите в список результатов, чтобы найти самую длинную последовательность без нулевых значений. Сохраните element_counter
, чтобы узнать, какой элемент в списке результатов мы просматриваем. Удерживайте curr_start_element
в начале элемента текущей последовательности и сохраняйте curr_len
, как долго текущая последовательность. Также сохраняйте longest_start_element
и `longest_len ', которые начинаются как ноль и обновляются по мере перемещения по списку.
-
Верните список результатов, начинающийся с longest_start_element
и принимающий longest_len
EDIT: добавлен код. Протестировано и работает
#note this doesn't work with negative numbers
#it certainly possible to write this to work with negatives
# but the code is a bit hairier
import sys
def findLongestSequence(lst):
#step 1
high = -sys.maxint - 1
for num in lst:
if num > high:
high = num
#step 2
result = [None]*(high+1)
#step 3
for num in lst:
result[num] = num
#step 4
curr_start_element = 0
curr_len = 0
longest_start_element = -1
longest_len = -1
for element_counter in range(len(result)):
if result[element_counter] == None:
if curr_len > longest_len:
longest_start_element = curr_start_element
longest_len = curr_len
curr_len = 0
curr_start_element = -1
elif curr_start_element == -1:
curr_start_element = element_counter
curr_len += 1
#just in case the last element makes the longest
if curr_len > longest_len:
longest_start_element = curr_start_element
longest_len = curr_len
#step 5
return result[longest_start_element:longest_start_element + longest_len-1]
Ответ 5
Если результат действительно должен быть подпоследовательностью последовательных восходящих целых чисел, а не просто восходящими целыми числами, то нет необходимости помнить каждую целую последовательную подпоследовательность, пока вы не определите, какая из них самая длинная, вам нужно только помнить начальное и конечное значения каждой подпоследовательности. Таким образом, вы можете сделать что-то вроде этого:
def longestConsecutiveSequence(sequence):
# map starting values to largest ending value so far
map = collections.OrderedDict()
for i in sequence:
found = False
for k, v in map.iteritems():
if i == v:
map[k] += 1
found = True
if not found and i not in map:
map[i] = i + 1
return xrange(*max(map.iteritems(), key=lambda i: i[1] - i[0]))
Если я запустил это на исходной дате выборки (т.е. [1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11]
), я получаю:
>>> print list(longestConsecutiveSequence([1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11]))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Если я запустил его на одном из образцов Abhijit [1,2,3,11,12,13,14]
, я получаю:
>>> print list(longestConsecutiveSequence([1,2,3,11,12,13,14]))
[11, 12, 13, 14]
К сожалению, этот алгоритм O (n * n) в худшем случае.
Ответ 6
Предупреждение: Это обманный способ сделать это (иначе я использую python...)
import operator as op
import itertools as it
def longestSequence(data):
longest = []
for k, g in it.groupby(enumerate(set(data)), lambda(i, y):i-y):
thisGroup = map(op.itemgetter(1), g)
if len(thisGroup) > len(longest):
longest = thisGroup
return longest
longestSequence([1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11, 15,15,16,17,25])
Ответ 7
Вам нужна Максимальная непрерывная сумма (Оптимальная субструктура):
def msum2(a):
bounds, s, t, j = (0,0), -float('infinity'), 0, 0
for i in range(len(a)):
t = t + a[i]
if t > s: bounds, s = (j, i+1), t
if t < 0: t, j = 0, i+1
return (s, bounds)
Это пример динамического программирования и O (N)
Ответ 8
Решение O (n) работает, даже если последовательность не начинается с первого элемента.
Предупреждение не работает, если len (A) = 0.
A = [1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11]
def pre_process(A):
Last = {}
Arrow = []
Length = []
ArgMax = 0
Max = 0
for i in xrange(len(A)):
Arrow.append(i)
Length.append(0)
if A[i] - 1 in Last:
Aux = Last[A[i] - 1]
Arrow[i] = Aux
Length[i] = Length[Aux] + 1
Last[A[i]] = i
if Length[i] > Max:
ArgMax = i
Max = Length[i]
return (Arrow,ArgMax)
(Arr,Start) = pre_process(A)
Old = Arr[Start]
ToRev = []
while 1:
ToRev.append(A[Start])
if Old == Start:
break
Start = Old
New = Arr[Start]
Old = New
ToRev.reverse()
print ToRev
Питонизации приветствуются!
Ответ 9
Хорошо, вот еще одна попытка в python:
def popper(l):
listHolders = []
pos = 0
while l:
appended = False
item = l.pop()
for holder in listHolders:
if item == holder[-1][0]-1:
appended = True
holder.append((item, pos))
if not appended:
pos += 1
listHolders.append([(item, pos)])
longest = []
for holder in listHolders:
try:
if (holder[0][0] < longest[-1][0]) and (holder[0][1] > longest[-1][1]):
longest.extend(holder)
except:
pass
if len(holder) > len(longest):
longest = holder
longest.reverse()
return [x[0] for x in longest]
Примеры входов и выходов:
>>> demo = list(range(50))
>>> shuffle(demo)
>>> demo
[40, 19, 24, 5, 48, 36, 23, 43, 14, 35, 18, 21, 11, 7, 34, 16, 38, 25, 46, 27, 26, 29, 41, 8, 31, 1, 33, 2, 13, 6, 44, 22, 17,
12, 39, 9, 49, 3, 42, 37, 30, 10, 47, 20, 4, 0, 28, 32, 45, 15]
>>> popper(demo)
[1, 2, 3, 4]
>>> demo = [1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11]
>>> popper(demo)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>>
Ответ 10
Это должно сделать трюк (и есть O (n)):
target = 1
result = []
for x in list:
for y in result:
if y[0] == target:
y[0] += 1
result.append(x)
Для любого стартового номера это работает:
result = []
for x in mylist:
matched = False
for y in result:
if y[0] == x:
matched = True
y[0] += 1
y.append(x)
if not matched:
result.append([x+1, x])
return max(result, key=len)[1:]