Найдите минимальное количество необходимых элементов, чтобы их сумма была равна или больше S

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

Есть ли улучшения по сравнению с nlog(n).

Можно считать, что все числа положительные.


person Shamim Hafiz    schedule 11.05.2011    source источник
comment
@equality: Да, я исправил свой пост.   -  person Shamim Hafiz    schedule 11.05.2011
comment
Был ли алгоритм O(n log(n)) недостаточным, чтобы убедить их, что вы хороший кандидат? Это звучит как нормальное решение для меня.   -  person Simen S    schedule 11.05.2011
comment
Не обязательно. Но не уверен, почему это имеет значение?   -  person Shamim Hafiz    schedule 11.05.2011
comment
@equality: S не обязательно должно быть постоянным, его просто нужно знать. Если бы он был постоянным или ограниченным, вы могли бы улучшить реальную производительность, заранее выделив сегменты, но это не влияет на теоретическую сложность алгоритма.   -  person verdesmarald    schedule 11.05.2011
comment
@равенство: О, хорошо. Алгоритм нужно запустить только один раз. Следовательно, S эффективно постоянно. @veredesmarald: Согласен   -  person Shamim Hafiz    schedule 11.05.2011
comment
@equality: это 50, а не формула. Я не уверен, что вопрос неправильно описан, чтобы вы подумали, что это функция, а не постоянное значение.   -  person Shamim Hafiz    schedule 11.05.2011
comment
@equality: когда я сказал «известно», я имел в виду «известно до любых вычислений». Причина, по которой существует жесткое ограничение n lg(n) на сортировку сравнением, заключается в том, что вы не знаете заранее, в каком диапазоне находятся значения, сколько их и т. д. Зная часть этой информации заранее, вы сможете выполнять несравнительную сортировку. работать лучше в некоторых случаях.   -  person verdesmarald    schedule 11.05.2011
comment
Я думаю, что мое решение должно быть принято. Это дает вам оптимальный ответ, не требуя какой-либо быстрой сортировки на основе целых чисел или каких-либо предположений о входных данных.   -  person Rob Neuhaus    schedule 12.05.2011


Ответы (5)


Вот алгоритм, который O(n + size(smallest subset) * log(n)). Если наименьшее подмножество намного меньше массива, это будет O(n).

Прочтите http://en.wikipedia.org/wiki/Heap_%28data_structure%29 если мое описание алгоритма неясно (в нем мало деталей, но все детали есть).

  1. Превратите массив в кучу, организованную таким образом, чтобы самый большой элемент был доступен за время O(n).
  2. Неоднократно извлекайте самый большой элемент из кучи, пока их сумма не станет достаточно большой. Это занимает O(size(smallest subset) * log(n)).

Это почти наверняка тот ответ, на который они надеялись, хотя его отсутствие не должно нарушать условия сделки.

Изменить. Вот еще один вариант, который часто работает быстрее, но может быть и медленнее.

Walk through elements, until the sum of the first few exceeds S.  Store current_sum.
Copy those elements into an array.
Heapify that array such that the minimum is easy to find, remember the minimum.
For each remaining element in the main array:
    if min(in our heap) < element:
        insert element into heap
        increase current_sum by element
        while S + min(in our heap) < current_sum:
            current_sum -= min(in our heap)
            remove min from heap

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

person btilly    schedule 11.05.2011
comment
Это O(n + K log n) (а не O(n + K log K), как вы утверждаете), не так ли? +1 однако. Это определенно лучший ответ на данный момент. - person ; 11.05.2011
comment
@Moron: Ой, ты прав. У меня был еще один, прежде чем он мог достичь нижней границы, но понял, что обычно это будет хуже. Я добавлю это решение позже. - person btilly; 11.05.2011
comment
@Moron: добавлено второе решение. - person btilly; 11.05.2011
comment
+1, это гораздо лучшее решение. Мое кунг-фу слабое, когда дело доходит до кучи; Я не знал, что их создание может быть линейным по времени, если вы уже знаете все элементы, спасибо! - person verdesmarald; 12.05.2011

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

Поскольку диапазон значений ограничен, вы можете использовать несравнительный алгоритм сортировки, такой как сортировка Pigeonhole. или Сортировка по основанию, чтобы перейти ниже n lg(n).

Обратите внимание, что эти методы зависят от некоторой функции S, поэтому, если S становится достаточно большим (а n остается достаточно маленьким), возможно, вам лучше вернуться к сравнительной сортировке.

person verdesmarald    schedule 11.05.2011
comment
Я могу ошибаться, но если бы вы сделали что-то вроде голубиной дыры, вы могли бы просто выполнить первую часть сортировки (поместить данные в дырки), а затем тянуть с конца добавления данных, пока не нажмете ›= S , это избавит вас от необходимости возвращать элементы в отсортированный порядок. - person pstrjds; 11.05.2011
comment
Вы знаете, что целые числа находятся в диапазоне от 0 до S, только если предполагаете целые числа фиксированного размера. Если вы используете bigints, вы вернулись к n log n. - person hammar; 11.05.2011
comment
@hammar: Что ты имеешь в виду? Максимальное значение типа данных не важно, важно только значение S; как я отметил в своем ответе, для целей этого вопроса значения, превышающие S, эквивалентны S, поскольку, если в массиве есть какие-либо значения ›= S, ответ равен 1. - person verdesmarald; 11.05.2011
comment
@veredesmarald: этот аргумент работает, только если S имеет фиксированный размер. - person hammar; 11.05.2011
comment
@hammar: извини, но я не понимаю, что ты имеешь в виду. Очевидно, что для достаточно большого S сравнительная сортировка работает лучше (как я сказал изначально), но какое отношение имеет программное представление значения S? - person verdesmarald; 11.05.2011
comment
Другими словами: подход с ячейками даст вам алгоритм O (n + S). Во многих случаях это будет лучше, чем O (n log (n)). В частности, если S ненамного больше n. - person Simen S; 11.05.2011
comment
@veredesmarald: размер S равен log(S), поэтому, если ваши числа ограничены сверху S, вам нужно будет сделать < i>log(S) проходит успешно, если используется сортировка по основанию. Теперь, если S имеет фиксированный размер, log(S) будет постоянным и исчезнет, ​​но если это не так, вы не потеряете коэффициент журнала. - person hammar; 11.05.2011
comment
@hammar: Вы прочитали мой ответ полностью? Я уже отмечал, что все несравнительные сортировки зависят от некоторой функции S. При принятии решения о подходе к конкретному экземпляру этой проблемы необходимо учитывать относительный размер S и n, а не просто размер S. Даже если S оказывается равным 2 ^ 35, вам все равно лучше использовать сортировку на основе ведра, если n оказывается равным 2 ^ 33. Это не имеет ничего общего с bigint или фиксированным размером, все дело в S:n - person verdesmarald; 11.05.2011
comment
@veredesmarald, @hammer: я совершенно уверен, что нет ни одного случая, когда этот ответ был бы лучше, чем тот, который я дал. - person btilly; 11.05.2011

Одно улучшение (асимптотически) по сравнению с Theta(nlogn), которое вы можете сделать, состоит в том, чтобы получить алгоритм времени O(n log K), где K — требуемое минимальное количество элементов.

Таким образом, если K постоянно или, скажем, log n, это лучше (асимптотически), чем сортировка. Конечно, если K равно n^epsilon, то это не лучше, чем Theta(n logn).

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

Теперь выполните бинарный поиск K, начиная с i=1 (наибольшего) и удваивая i и т. д. на каждом ходу.

Вы находите ith по величине, находите сумму i по величине элементов и проверяете, больше ли она S или нет.

Таким образом, вы запустите O (log K) запусков алгоритма выбора (то есть O (n)) для общего времени работы O (n log K).

person Community    schedule 11.05.2011
comment
Я бы не подумал об этом, потому что я знаю более быстрое стандартное решение. Но это умный ответ. +1 - person btilly; 11.05.2011
comment
@btilly: Только теоретически :-) Ваш ответ лучше всего даже практически. Я думаю, что знал стандартное решение, но мой сонный разум, должно быть, каким-то образом заблокировал его! - person ; 11.05.2011

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

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

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

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

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

def Solve(arr, s):
  # We could get rid of worse case O(n^2) behavior that basically never happens 
  # by selecting the median here deterministically, but in practice, the constant
  # factor on the algorithm will be much worse.
  p = random_element(arr)
  left_arr, right_arr = partition(arr, p)
  # assume p is in neither left_arr nor right_arr
  right_sum = sum(right_arr)
  if right_sum + p >= s:
    if right_sum < s:
      # solved it, p forms the cut off
      return len(right_arr) + 1    
    # took too much, at least we eliminated left_arr and p
    return Solve(right_arr, s) 
  else:
    # didn't take enough yet, include all elements from and eliminate right_arr and p
    return len(right_arr) + 1 + Solve(left_arr, s - right_sum - p)  
person Rob Neuhaus    schedule 12.05.2011
comment
+1 - но алгоритмы разделения вокруг случайного поворота (быстрая сортировка и т. д.) могут иметь плохую производительность в худшем случае, когда поворот всегда оказывается несбалансированным. Я не уверен, означает ли это O (n ^ 2) или O (n log n) в данном случае. Кроме того, повторное суммирование (наивно реализованное) нарушило бы требования к производительности - вам нужно отслеживать, как сумма изменяется с течением времени, когда разбиение изменяет массив и как изменяются верхние/нижние границы, чтобы преодолеть эту проблему. - person Steve314; 12.05.2011
comment
Да, это O (n ^ 2) в худшем случае. Вы можете избавиться от этого фактора и превратить его в худший случай O (n), выполнив детерминистический медианный поиск и вращаясь вокруг него, но такое решение в основном всегда будет медленнее на практике (примите случайность!). Я улучшил псевдокод, чтобы он не пересчитывал сумму (right_arr), но это не обязательно для асимптотического поведения, только постоянный коэффициент. Как только мы исключим часть массива, нам больше не нужно будет ни вычислять их сумму, ни проверять их снова (когда мы отбрасываем левые, мы никогда не берем их, когда мы отбрасываем правые, мы берем их все). - person Rob Neuhaus; 12.05.2011
comment
Хорошо, при пересчете я вам доверяю - обычно мои догадки по интуиции более надежны, чем мои тщательные (но никогда не достаточно тщательные) математические расчеты, но, к сожалению, более надежные часто все еще довольно ненадежны. - person Steve314; 12.05.2011
comment
Хороший ответ. Я предпочитаю это моему. Также это можно сделать O(n log(S)) в худшем случае, не нанося ущерба среднему случаю. У нас есть массив целых чисел в ограниченном диапазоне. Сделайте так, чтобы каждый второй поворот находился в середине целых чисел в этом диапазоне, а не в значениях в массиве. Это гарантированно сойдется за O(log(S)) шагов. (В качестве альтернативы вы можете выполнить детерминированный медианный поиск в любое время, когда последние 3 выбора не уменьшили пространство поиска достаточно. Это делает его O(n) и в среднем случае не повредит производительности.) - person btilly; 20.05.2011

  1. исключить числа ‹ S, если вы найдете какое-то число == S, то решено
  2. классная сортировка чисел ‹ S

Суммируйте элементы от большего к меньшему в отсортированном порядке, пока не превысите S.

person BiGYaN    schedule 15.05.2011
comment
Пожалуйста, добавьте анализ сложности. Спасибо. - person Shamim Hafiz; 15.05.2011