2012-04-24 3 views
5

Я ищу быстрый алгоритм:быстрый алгоритм нахождения суммы в массиве

У меня есть целочисленный массив размера п, цель состоит в том, чтобы найти все шаблоны в массиве,
x1, x2, x3 are different elements in the array, such that x1+x2 = x3

Для Например, я знаю, что существует массив int размером 3: [1, 2, 3], тогда есть только одна возможность: 1 + 2 = 3 (рассмотреть 1 + 2 = 2 + 1)

Я подумываю о реализации пар и хашмапов, чтобы ускорить алгоритм , (Самый быстрый один я сейчас еще O(n^2))

Пожалуйста, поделитесь вашей идеи для этой проблемы, спасибо

ответ

8

Редактировать: Нижеприведенный ответ относится к версии этой проблемы, в которой вам нужен только один триплет, который складывается таким образом. Если вы хотите их всех, так как потенциально возможно хотя бы O (n^2) возможных выходов (как указано ex0du5) и даже O (n^3) в патологических случаях повторных элементов, вы не собираетесь бить простой алгоритм O (n^2), основанный на хешировании (сопоставление от значения к списку индексов с этим значением).


Это по существу the 3SUM problem. Без потенциально неограниченно больших элементов наиболее известные алгоритмы составляют приблизительно O(n^2), но мы только доказали, что для большинства моделей вычислений оно не может быть быстрее, чем O(n lg n).

Если целочисленные элементы находятся в диапазоне [u, v], вы можете сделать несколько другую его версию в O(n + (v-u) lg (v-u)) с помощью FFT. Я собираюсь описать процесс преобразования этой проблемы в эту проблему, решить ее там, а затем выяснить ответ на вашу проблему, основанный на этом преобразовании.

Проблема, которую я знаю, как решить с помощью БПФ, чтобы найти арифметическую последовательность длиной 3 в массиве: то есть, последовательность a, b, c с c - b = b - a, или что то же самое, a + c = 2b.

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


Назовём исходный массив X, который содержит целые числа x_1, ..., x_n. Мы хотим найти индексы i, j, k таких, что x_i + x_j = x_k.

  1. Найти минимальное и максимальное uv из X в O(n) времени.Пусть u' будет min(u, u*2) и v' be max(v, v*2).

  2. Построить двоичный массив (bitstring) Z длины v' - u' + 1; Z[i] будет иметь значение, если X или его номер [x_1*2, ..., x_n*2] содержит u' + i. Это значение O(n) для инициализации; просто пройдите по каждому элементу X и установите два соответствующих элемента: Z.

    Поскольку мы строим этот массив, мы можем сохранить индексы любых дубликатов, которые мы находим во вспомогательном списке Y. Как только Z будет завершен, мы просто проверяем на 2 * x_i за каждые x_i в Y. Если таковые имеются, мы закончили; в противном случае дубликаты не имеют значения, и мы можем забыть о Y. (Единственная ситуация немного сложнее, если 0 повторяется, то нам нужно три различных его копии, чтобы получить решение.)

    Теперь решение вашей проблемы, т.е. x_i + x_j = x_k, появится в Z как три evenly- а некоторые простые алгебраические манипуляции дают нам 2*x_j - x_k = x_k - 2*x_i. Обратите внимание, что элементы на концах являются нашими специальными удвоенными записями (от 2X), а один из них является регулярным (от X).

  3. Рассмотрим Z как представление полинома p, где коэффициент для члена степени i является Z[i]. Если X - [1, 2, 3, 5], то Z - 1111110001 (потому что у нас есть 1, 2, 3, 4, 5, 6 и 10); p затем 1 + х + х + х + х + х + х.

    Теперь, помните из средней школы алгебры, коэффициент х гр в произведении двух многочленов равна сумма по всем а, Ь с а + Ь = с коэффициента первых полиномиального по для x a раз второй коэффициент для x b. Таким образом, если мы рассмотрим д = р, коэффициент х 2j (для J с Z[j] = 1) будет сумма по всем я из Z[i] * Z[2*j - i]. Но так как Z является двоичным, это точно количество триплетов i, j, k, которые равномерно распределены в Z. Обратите внимание, что (j, j, j) всегда является таким триплетом, поэтому мы заботимся только о единицах со значениями> 1.

    Затем мы можем использовать Fast Fourier Transform найти P 2 в O(|Z| log |Z|) времени, где |Z| является v' - u' + 1. Мы выберем другой массив коэффициентов; назовите его W.

  4. Петля на каждом x_k в X. (Напомним, что наши желаемые равномерно распределенные элементы центрированы по элементу X, а не 2*X.) Если соответствующий W для этого дважды, то есть W[2*(x_k - u')], равен 1, мы знаем, что это не центр каких-либо нетривиальных прогрессий, и мы можем пропусти это. (Как утверждалось ранее, это должно быть только положительное целое.)

    В противном случае это может быть центр прогрессии, который мы хотим (поэтому нам нужно найти i и j). Но, к сожалению, это может быть также центр прогрессии, который не имеет нашей желаемой формы. Поэтому нам нужно проверить. Переверните остальные элементы x_i из X и проверьте, есть ли тройка с 2*x_i, x_k, 2*x_j для некоторых j (путем проверки Z[2*(x_k - x_j) - u']). Если это так, у нас есть ответ; если мы пройдем через все X без хита, тогда FFT найдет только ложные ответы, и мы должны проверить другой элемент W.

    Таким образом, последний шаг - это O (n * 1 + (число x_k с W [2 * (x_k - u ')]> 1, которые на самом деле не являются решениями)), возможно, O(n^2), что очевидно, не в порядке. Должен быть способ избежать генерации этих ложных ответов на выходе W; если бы мы знали, что у любого подходящего коэффициента W определенно был ответ, этот последний шаг будет O(n), и все будет хорошо.

    Я думаю, что для этого можно использовать несколько иной полином, но я не получил его на самом деле. Я подумаю об этом еще немного ....

частично на основе this answer.

+0

Набор не является массивом. Этот ответ неверен. Например, рассмотрим массив, в котором элементы _all_ равны нулю. Затем все пары элементов массива равны каждому отдельному элементу. Просто перечислить каждую пару для суммы O (n^2). Свяжите хэш-таблицу с контейнерами совпадающих элементов, и вы получите амортизацию O (n^2) для вычисления структуры данных, которая представляет все возможные тройки (или O (n^3) для печати в виде троек). – ex0du5

+1

@ ex0du5 здесь есть простая эквивалентность. Вы можете идентифицировать дублированные числа в одном проходе O (n), а затем обрабатывать эти суммы за время O (n). Если вы еще не нашли их, разделите их, и теперь вы уменьшите проблему до заданной проблемы, описанной выше. И наоборот, заданная задача разрешима, если у нас есть решение проблемы массива.Таким образом, они в основном одинаковы, хотя и различны в деталях. – btilly

+0

Спасибо, что указали, что вне @ ex0du5; Я добавил обработчик дубликатов (как было предложено btilly) к алгоритму. – Dougal

1

Он должен быть по крайней мере O (N^2), поскольку есть п (п-1)/2 разных суммы, которые можно проверить для других участников. Вы должны вычислить все это, потому что любая суммированная сумма может быть любым другим членом (начинайте с одного примера и переставляйте все элементы, чтобы убедить себя, что все должно быть проверено). Или посмотрите на fibonacci для чего-то конкретного.

Таким образом, вычисление этого и поиск элементов в хеш-таблице дает амортизацию O (n^2). Или используйте упорядоченное дерево, если вам нужен лучший худший случай.

+1

Вам не нужно сравнивать все пары, потому что для некоторых случаев x1 + x2 не может равняться x3. Если x3 меньше, чем x1 или x2, например. –

+0

@HunterMcMillen: да, но порядок остается n^2, даже если вы оптимизируете этот путь. – Borodin

+1

-1 + -1 = -2. -2, конечно, меньше, чем любой из них -1, верно? – ex0du5

1

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

Смежные вопросы