Ищем алгоритм (версия двумерного бинарного поиска)

Простая задача и известный алгоритм:

У меня есть большой массив из 100 элементов. Первые X членов равны 0, а остальные 1. Найдите X.

Я решаю это с помощью бинарного поиска: проверить элемент 50, если он равен 0, проверить элемент 75 и т. д., пока не найду соседние 0 и 1.

Я ищу оптимизированный алгоритм для решения той же задачи в двух измерениях:

У меня есть двумерный массив 100*100. Те элементы, которые находятся в строках 0-X И в столбцах 0-Y равны 0, а остальные равны 1. Как найти Y и X?


person Igor Oks    schedule 02.08.2011    source источник


Ответы (4)


Простое решение: двигаться сначала в направлении X, а затем в направлении Y.

Чек (0,50); Если это 0, проверьте (0,75); пока не найдете соседние 0 и 1. Затем идите оттуда в направлении Y.

Второе решение:

Проверьте член (50,50). Если это 1, проверяйте (25,25), пока не найдете 0. Продолжайте, пока не найдете соседние (X,X) и (X+1,X+1), которые равны 0 и 1. Затем проверьте (X,X +1) и (Х+1,Х). Ни один, ни один из них не будет 1. Если ни то, ни другое, вам конец. Если только один, например (X+1,X), то Вы знаете, что размер коробки находится между (X+1,X) и (100,X). Используйте бинарный поиск, чтобы найти высоту коробки.

РЕДАКТИРОВАНИЕ. Как заметил Крис, кажется, что простой подход работает быстрее.

Второе решение (модифицированное):

Проверьте член (50,50). Если это 1, проверяйте (25,25), пока не найдете 0. Продолжайте, пока не найдете соседние (X,X) и (X+1,X+1), которые равны 0 и 1. Затем проверьте (X,X +1). Если это 1, то выполните двоичный поиск в строке (X, X + 1)... (X, 100). В противном случае выполните двоичный поиск в строке (X, X)... (100, X).

Даже тогда я, вероятно, бью здесь дохлую лошадь. Если и будет быстрее, то на ничтожно малую величину. Это чисто теоретическое развлечение. :)

РЕДАКТИРОВАНИЕ 2 Как выразились Фезвез и Крис, бинарный поиск наиболее эффективно делит область поиска на две части; Мой подход делит площадь на 1/4 и 3/4 части. Фезвес указал, что это можно исправить, предварительно рассчитав коэффициент деления (но это будет дополнительный расчет). В модифицированной версии моего алгоритма я выбираю направление, куда идти (направление X или Y), что также эффективно делит пространство поиска на две части, а затем провожу бинарный поиск. В заключение, это показывает, что этот подход всегда будет немного медленнее. (и сложнее в реализации).

Спасибо, Игорь Окс, за интересный вопрос. :)

person Rauni Lillemets    schedule 02.08.2011
comment
Мне нравится этот подход. Он эффективно выполняет бинарный поиск как по X, так и по Y одновременно, пока вы не определите один, а затем просто продолжите поиск другого, уже сузив область поиска. Более эффективно, чем делать X и Y независимо. - person Chris; 02.08.2011
comment
@Chris: Это определенно не более эффективно, чем два независимых поиска. Это потому, что здесь пространство поиска квадратично больше. Таким образом, теоретическая сложность Big O равна log(N*N) == 2*log(N). Это решение просто менее простое и более сложное. Я думаю, что это решение менее эффективно, чем два независимых поиска, потому что оно не делит пространство поиска поровну на каждой итерации. - person Juraj Blaho; 02.08.2011
comment
Это может быть непонятно из моего поста, но я задумал его как бинарный поиск. (разделяя пространство поиска поровну). Кроме того, сложность Big O — это одно, а время выполнения — другое. Я бы предположил, что эта программа в среднем в x раз быстрее, где 1 ‹ x ‹ 2. Но это нужно доказать. - person Rauni Lillemets; 02.08.2011
comment
Возможно. Я постоянно путаю математику в голове. Я согласен, что они оба одного порядка, но я все еще чувствую, что этот метод потребует меньше сравнений. Я подозреваю, что единственный способ удовлетворить себя — это написать и запустить и посмотреть, что из этого получится. Может быть, сегодня вечером... Не думаю, что мой босс одобрил бы, если бы я делал это в рабочее время. :) - person Chris; 02.08.2011
comment
И я постараюсь написать доказательство. Также не следует делать это в рабочее время :) - person Rauni Lillemets; 02.08.2011
comment
Ммм... Не говорите моему боссу, но я написал программу для проверки этого... Похоже, независимый подход быстрее. Похоже, это во многом связано с двумя дополнительными тестами, необходимыми для определения того, является ли предмет, который вы нашли по диагонали, краем X или Y. По мере увеличения размера области поиска эти два становятся менее значимыми, но всегда достаточно, чтобы развернуть ее. кажется. - person Chris; 02.08.2011
comment
Думая о доказательстве, потому что зависимый случай всегда использует еще 2 проверки, чтобы найти первый результат, полученные знания должны быть лучше, чем те, которые можно было бы получить с помощью 2 дополнительных поисков в независимом случае. Двоичный поиск достаточно эффективен, поэтому 2 дополнительных поиска означают, что требуется много дополнительных знаний, и я не думаю, что вы это понимаете. - person Chris; 02.08.2011
comment
Я думаю, ты в теме, Крис. Мой алгоритм можно изменить так, чтобы он выполнял только 1 проверку вместо 2: проверять только (X,X+1)==1, а если ложно, то выполнять бинарный поиск по (X,X)...(100,X) ; иначе выполните бинарный поиск по (X, X+1)... (X, 100). Я пытался доказать, что быстрее, но не могу понять. Они кажутся очень близкими друг к другу - Крис, ты можешь проверить, что быстрее на практике? - person Rauni Lillemets; 02.08.2011
comment
Я думаю, что Фезвез лучше всего сказал в своем ответе, что для того, чтобы сделать это правильно, все, что вам нужно сделать, это разделить пространство поиска пополам на каждом шаге и отбросить его. Метод x first then y делает это очень просто, и я думаю, что все остальное, особенно то, что требует дополнительных проверок, чтобы определить, какой раздел выбросить, не может быть быстрее. - person Chris; 03.08.2011

Редактировать: оптимальное решение состоит из двух простых бинарных поисков.

Я очень извиняюсь за длинный и запутанный пост, который я сделал ниже. В чем суть проблемы, так это в том, чтобы найти точку в пространстве, содержащем 100*100 элементов. Лучшее, что вы можете сделать, это разделить на каждом шаге это пространство пополам. Вы можете сделать это запутанным способом (тот, который я сделал в остальной части поста). Но если вы понимаете, что бинарный поиск по оси X по-прежнему делит пространство исследования на два на каждом шаге (то же самое касается Y оси) то вы понимаете что это оптимально.

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


Если вам нужен простой алгоритм (хотя и не оптимальный), просто запустите двоичный поиск дважды, как это предлагается.

Однако, если вам нужен оптимальный алгоритм, вы можете одновременно искать границу на X и на Y. (Вы должны отметить, что два алгоритма имеют одинаковую асимптотическую сложность, но оптимальный алгоритм все равно будет быстрее)

На всех следующих рисунках точка (0, 0) находится в левом нижнем углу.

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

Выбор точки в 2D

Если вы выбираете точку (черный крест) и результат равен 1 (красные линии), это означает, что искомая точка не может находиться в сером поле (таким образом, она должна находиться в оставшаяся белая область)

введите здесь описание изображения

С другой стороны, если значение равно 0 (синие линии), это означает, что искомая точка не может находиться в серой области (таким образом, она должна находиться в оставшейся белой области).

введите здесь описание изображения

Итак, если вы получите один результат 0 и один результат 1, вы получите следующее:

введите здесь описание изображения

Точка, которую вы ищете, находится в прямоугольнике 1, 2 или 3. Вам просто нужно проверить два угла прямоугольника 3, чтобы узнать, какой из 3 прямоугольников является хорошим.

Итак, алгоритм следующий:

  • Обратите внимание, где находятся нижний левый и верхний правый углы прямоугольника, с которым вы работаете.

  • Выполняйте двоичный поиск по диагонали прямоугольника, пока не наткнетесь хотя бы один раз на результат 1 и один раз на результат 0.

  • Проверьте 2 других угла прямоугольника 3 (вам необходимо уже знать значения двух углов по диагонали). Можно проверить только один угол, чтобы узнать правильный прямоугольник (но вам нужно будет проверить два угла). если правый прямоугольник является прямоугольником 3)

  • Определите, находится ли искомая точка в прямоугольнике 1, 2 или 3

  • Повторяйте, уменьшая задачу до правильного прямоугольника, пока последний прямоугольник не уменьшится до точки: это значение, которое вы ищете.


Редактировать: если вам нужна оптимальная оптимальность, вы бы не выбрали точку (50, 50), вы не разрезаете пространство на равные части. Один в три раза больше другого. В идеале вы должны выбрать точку, которая делит пространство на две равные области (по площади).

Вы должны вычислить один раз в начале значение factor = (1.0 - 1.0/sqrt(2.0)). Затем, когда вы хотите разрезать между значениями a и b, выберите точку разреза как a + factor*(b-a). Когда вы разрезаете первоначальный прямоугольник 100x100 в точке (100*фактор, 100*фактор), две области будут иметь площадь (100*100)/2, поэтому сходимость будет быстрее.

person B. Decoster    schedule 02.08.2011
comment
Это кажется гораздо лучшим объяснением алгоритма, предложенного Рауни. Но, как я отметил там, мне интересно, действительно ли это быстрее, чем просто два линейных поиска. Кажется, это просто сложнее. - person Juraj Blaho; 02.08.2011
comment
Забавно, что вы сказали, и да, они похожи. Что делает алгоритм Рауни, так это то, что он продолжает поиск синих и красных линий, пока оставшиеся прямоугольники не станут шириной в 1 пиксель. Затем он ищет (X, X+1) и (X+1, X), что аналогично поиску двух других углов третьего прямоугольника. Теперь, что касается сложности, я не слишком уверен. Я просто думаю, что когда вы выполняете один линейный поиск, вы отбрасываете половину информации (вас не волнует другая ось), тогда как здесь учитывается вся информация. - person B. Decoster; 02.08.2011
comment
@Fezvez: Ваши комментарии в редактировании того факта, что бинарный поиск в основном просто хочет разделить пространство на две части, являются отличным пониманием, к которому я изо всех сил пытался прийти. +1 только за это. :) - person Chris; 03.08.2011
comment
@ Крис: Спасибо! Что ж, я очень зол из-за того, что так много и ничего не искал, но я был очень рад указать на тот факт, что на самом деле вы хотите сделать на каждой итерации разделение пространства пополам! Спасибо за поддержку! знак равно - person B. Decoster; 03.08.2011

Запустите бинарный поиск дважды. Сначала определите X, запустив двоичный поиск в последней строке, а затем определите Y, запустив двоичный поиск в последнем столбце.

person Juraj Blaho    schedule 02.08.2011

Используйте бинарный поиск для обоих измерений и случая 1D:

  1. Начните с j=50. Теперь одномерный массив, полученный путем изменения i, имеет желаемую форму, поэтому найдите X из одномерного случая.
  2. Если X = 100 (т. е. ни одного), то сделайте j = 75 (середина диапазона в измерении j) и повторите.
  3. Если X ‹ 100, то вы его нашли. Осталось только зафиксировать i=X и найти Y из одномерного случая.
person Petar Ivanov    schedule 02.08.2011