Рассмотрим все целые числа в интервале [2 i .. 2 i + 1 - 1]. И предположим, что все целые числа меньше 2 i могут быть образованы из суммы чисел из данного массива. Также предположим, что мы уже знаем C, который представляет собой сумму всех чисел ниже 2 i. Если C> = 2 i + 1 - 1, каждое число в этом интервале может быть представлено как сумма заданных чисел. В противном случае мы могли бы проверить, содержит ли interval [2 i .. C + 1] какое-либо число из заданного массива. А если такого числа нет, мы искали C + 1.
Вот набросок алгоритма:
- Для каждого входного числа определите, к какому интервалу он принадлежит, и обновите соответствующую сумму:
S[int_log(x)] += x
.
- Вычислить сумму префикса для массива S:
foreach i: C[i] = C[i-1] + S[i]
.
- Отфильтруйте массив C, чтобы оставить только записи со значениями ниже следующей степени 2.
- Просканируйте входной массив еще раз и обратите внимание, какой из интервалов [2 i .. C + 1] содержит хотя бы одно входное число:
i = int_log(x) - 1; B[i] |= (x <= C[i] + 1)
.
- Найдите первый интервал, который не отфильтрован на шаге №3 и соответствующий элемент
B[]
не установлен на шаге №4.
Если не очевидно, почему мы можем применить шаг 3, вот доказательство. Выберите любое число от 2 i до C, затем последовательно вычтите из него все числа ниже 2 i в порядке убывания. В конце концов мы получаем либо какое-то число меньше последнего вычтенного числа, либо ноль. Если результат равен нулю, просто сложите все вычтенные числа, и мы получим представление выбранного числа. Если результат не равен нулю и меньше последнего вычтенного числа, этот результат также меньше 2 i, поэтому он «представимый», и ни одно из вычтенных чисел не используется для его представления. Когда мы складываем эти вычтенные числа обратно, мы получаем представление выбранного числа. Это также предполагает, что вместо фильтрации интервалов по одному мы могли бы пропустить сразу несколько интервалов, перейдя непосредственно к int_log C.
Сложность времени определяется функцией int_log()
, которая представляет собой целочисленный логарифм или индекс самого высокого установленного бита в числе. Если наш набор инструкций содержит целочисленный логарифм или любой его эквивалент (подсчет ведущих нулей или трюки с числами с плавающей запятой), то сложность равна O (n). В противном случае мы могли бы использовать некоторый битовый взлом, чтобы реализовать int_log()
в O (log log U) и получить временную сложность O (n * log log U). (Здесь U - наибольшее число в массиве).
Если шаг 1 (помимо обновления суммы) также обновит минимальное значение в заданном диапазоне, шаг 4 больше не нужен. Мы могли бы просто сравнить C [i] с Min [i + 1]. Это означает, что нам нужен только один проход по входному массиву. Или мы могли бы применить этот алгоритм не к массиву, а к потоку чисел.
Несколько примеров:
Input: [ 4 13 2 3 1] [ 1 2 3 9] [ 1 1 2 9]
int_log: 2 3 1 1 0 0 1 1 3 0 0 1 3
int_log: 0 1 2 3 0 1 2 3 0 1 2 3
S: 1 5 4 13 1 5 0 9 2 2 0 9
C: 1 6 10 23 1 6 6 15 2 4 4 13
filtered(C): n n n n n n n n n n n n
number in
[2^i..C+1]: 2 4 - 2 - - 2 - -
C+1: 11 7 5
Для входных чисел с разной точностью этот подход требует времени O (n * log M) и пространства O (log M). Где M - наибольшее число в массиве. То же время нужно просто для чтения всех чисел (а в худшем случае нам понадобится каждый их бит).
Тем не менее, этот результат может быть улучшен до O (n * log R), где R - значение, найденное этим алгоритмом (фактически, его вариант, чувствительный к выходу). Единственная модификация, необходимая для этой оптимизации, состоит в том, чтобы вместо обработки целых чисел сразу обрабатывать их по цифрам: первый проход обрабатывает младшие биты каждого числа (например, биты 0..63), второй проход - следующие биты (например, 64..127) и т. Д. Мы можем игнорировать все старшие биты после нахождения результата. Также это уменьшает требования к пространству до O (K) чисел, где K - количество битов в машинном слове.
person
Evgeny Kluev
schedule
12.01.2014
j
входного массива: просмотрите дерево и для каждого узлаn
добавьтеj + n
к дереву, если он еще не находится в дереве, затем добавьтеj
к дереву. По завершении просмотрите дерево, проверяя, равен ли текущий узел последнему узлу +1. Если нет, вы нашли решение. Если вы дойдете до конца дерева, решением будет значение последнего листа + 1. Однако может быть лучшая структура данных, учитывая количество вставок. - person Conspicuous Compiler   schedule 12.01.2014{ 1 3 5 7 }
выбор{ }
(пустой диапазон) возвращает0
. Так что это никогда не бывает невозможным числом. - person DavidO   schedule 13.01.2014