КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Прямой поиск строки
END Поиск в таблице BEGIN BEGIN m:=(L+R) DIV 2; { выбрираем любой элемент между элементами с номерами L и R } IF a[ m ]=x THEN found:=TRUE ELSE IF a[ m ]<x THEN L:=m+1 ELSE R:=m-1; END; IFfound THEN writeln(‘искомый элемент ‘,a [ m ]);
Инвариант цикла, т. е. условие, выполняющееся перед каждым шагом, таков: (L ≤ R) & (A k: 1 ≤ k < L: ak < х) & (A k: R < k ≤ N: ak > х) (1.41) из чего выводится результат found OR ((L > R) & (A k: 1 ≤ k < L: ak < х) & (A k: R < k ≤ N: ak > х)) откуда следует (am = x) OR (A k: 1 ≤ k ≤ N: ak ≠ х). Выбор m совершенно произволен в том смысле, что корректность алгоритма от него не зависит. Однако на его эффективность выбор влияет. Ясно, что наша задача — исключить на каждом шагу из дальнейшего поиска, каким бы ни был результат сравнения, как можно больше элементов. Оптимальным решением будет выбор среднего элемента, так как при этом в любом случае будет исключаться половина массива. В результате максимальное число сравнений равно log2(N), округленному до ближайшего целого. Таким образом, приведенный алгоритм существенно выигрывает по сравнению с линейным поиском, ведь там ожидаемое число сравнений - N/2. Эффективность можно несколько улучшить, поменяв местами заголовки условных операторов. Проверку на равенство можно выполнять во вторую очередь, так как она встречается лишь единожды и приводит к окончанию работы. Но более существенно следующее соображение: нельзя ли, как и при линейном поиске, отыскать такое решение, которое опять бы упростило условие окончания. И мы действительно находим такой быстрый алгоритм, как только отказываемся от наивного желания кончить поиск при фиксации совпадения. На первый взгляд это кажется странным, однако при внимательном рассмотрении обнаруживается, что выигрыш в эффективности на каждом шаге превосходит потери от сравнения с несколькими дополнительными элементами. Напомним, что число шагов в худшем случае – log2(N). Быстрый алгоритм основан на следующем инварианте:
(A k: 1 ≤ k < L: ak < х) & (A k: R < k ≤ N: ak ≥ х) (1.42) причем поиск продолжается до тех пор, пока обе секции не «накроют» массив целиком.
L:=1; R:=N+1; WHILE L<R DO j:= (L+R) DIV 2; IF a [ j ] < x THEN L:=j+1 ELSE R:=j; END; IF a [ R ] = x THEN writeln(‘искомый элемент ‘,a [ R ]);
Условие окончания – L ≥ R, но достижимо ли оно? Для доказательства этого нам необходимо показать, что пр и всех обстоятельствах разность R - L на каждом шаге убывает. В начале каждого шага L < R. Для среднего арифметического m справедливо условие L ≤ m < R. Следовательно, разность действительно убывает, ведь либо L увеличивается при присваивании ему значения m + 1, либо R уменьшается при присваивании значения m. При L = R повторение цикла заканчивается. Однако наш инвариант и условие L = R еще не свидетельствуют о совпадении. Конечно, при R = N+1 никаких совпадений нет. В других же случаях мы должны учитывать, что элемент а [ R ] в сравнениях никогда не участвует. Следовательно, необходима дополнительная проверка на равенство а [ R ] = х. В отличие от первого нашего решения (1.40) приведенный алгоритм, как и в случае линейного поиска, находит совпадающий элемент с наименьшим индексом. Поиск в массиве иногда называют поиском, в таблице, особенно если ключ сам является составным объектом, таким, как массив чисел или символов. Часто встречается именно последний случай, когда массивы символов называют строками или словами. Строковый тип определяется так: String =ARRAY[0.. М - 1] OF CHAR (1.44) соответственно определяется и отношение порядка для строк: (х = у) = (A j: 0 ≤ j < M: xj = yj) (х < у) = E i: 0 ≤ i < N: ((A j: 0 ≤ j < i: xj = yj ) & (xi < yi )) Для того чтобы установить факт совпадения, мы должны, очевидно, убедиться, что все символы сравниваемых строк соответственно равны один другому. Поэтому сравнение составных операндов сводится к поиску их несовпадающих частей, т. е. к поиску «на неравенство». Если неравных частей не существует, то можно говорить о равенстве. Предположим, что размер слов достаточно мал, скажем, меньше 30. В этом случае мы будем использовать линейный поиск и поступать таким образом.
Для большинства практических приложений желательно исходить из того, что строки имеют переменный размер. Это предполагает, что размер указывается в каждой отдельной строке. Если исходить из ранее описанного типа, то размер не должен превосходить максимального размера M. Такая схема достаточно гибка и подходит для многих случаев, в тоже время она позволяет избежать сложностей динамического распределения памяти. Чаще всего используются два таких представления размера строк: ü Размер неявно указывается путем добавления концевого символа, больше этот символ нигде не употребляется. Обычно для этой цели используется «непечатаемый» символ со значением 0С. (Для дальнейшего важно, что это минимальный символ из всего множества символов.) ü Размер явно хранится в качестве первого элемента массива, т.е. строка s имеет следующий вид: s= s0, s1, s2,..., sn-i. здесь si,..., sn-i — фактические символы строки, a s0 = CHR(N). Такой прием имеет то преимущество, что размер явно доступен, недостаток же в том, что этот размер ограничен размером множества символов. В последующем алгоритме поиска мы отдаем предпочтение первой схеме. В этом случае сравнение строк выполняется так: i:=0; WHILE (x[ i ] = y[ i ]) & (x[ i ] <> 0C) DO i:=i+1; (1.45) Концевой символ работает теперь как барьер, а инвариант цикла таков: A j: 0 ≤ j < i: xj = yj ≠ 0C Результирующее же условие выглядит следующим образом: ((xi ≠ yi ) OR (xj = 0C)) & (A j: 0 ≤ j < i: xi = yj ≠ 0C) При условии xi = yi считается, что х и у совпадают, если же х; <: у), то х < у. Теперь мы готовы вернуться к задаче поиска в таблице. Он требует «вложенных» поисков, а именно: поиска по строчкам таблицы, а для каждой строчки последовательных сравнений—между компонентами, Например, пусть таблица Т и аргумент поиска х определяются таким образом:
Т: ARRAY [0... N-1] OF String; x: String; Допустим, N достаточно велико, а таблица упорядочена в алфавитном порядке. Воспользуемся поиском делением пополам. Используя соответствующие алгоритмы (1.43) и сравнения строк (1.45), о которых шла речь выше, получаем такой фрагмент программы: L:=0; R:=N; (1.46) WHILE L<R DO BEGIN m:= (L+R) DIV 2; i:=0; WHILE (T[m,i] = x[i]) AND (x[i] ≠ 0С) DO i: =i+1; IF T[m,i] < x[i] THEN L:=m+1 ELSE R:=m END; IF R<N THEN BEGIN i:=0; WHILE (T[R,i] = x[i]) AND (x[i] ≠ 0С) DO i:=i+l (* (R<N) & (T[R,i] = x[i]) фиксирует совпадение *) Часто приходится сталкиваться со специфическим поиском, так называемым поиском строки. Его можно определить следующим образом. Пусть задан массив s из N элементов и массив р из М элементов, причем 0 < М ≤ N. Описаны они так: s: ARRAY[ 1.. N ] OF item p: ARRAY[ 1.. М ] OF item Поиск строки обнаруживает первое вхождение р в s. Обычно item —это символы, т. е. s можно считать некоторым текстом, а р - образом или словом, и мы хотим найти первое вхождение этого слова в указанном тексте. Это действие типично для любых систем обработки текстов, отсюда и очевидная заинтересованность в поиске эффективного алгоритма для этой задачи. Однако, прежде чем обращать внимание на эффективность, давайте сначала разберем некий «прямолинейный» алгоритм поиска. Мы его будем называть прямым поиском строки. Еще до определения алгоритма нужно более точно сформулировать, что же мы от него желаем получить. Будем считать результатом индекс i, указывающий на первое с начала строки совпадение с образом. С этой целью введем предикат P(i, j): P(i, j) = A k: 1 ≤ k < j: si+k=pk (1.47) Наш результирующий индекс, очевидно, должен удовлетворять предикату Р(i, М). Но этого недостаточно. Поскольку поиск должен обнаружить первое вхождение образа Q(i) = A k: 1 ≤ k < i: ~ P(k,M) (1.48) Поставленная задача сразу же наводит на мысль оформить поиск как повторяющееся сравнение, и мы предлагаем такой вариант: i:=0; REPEAT (* Q(i)*) i:=i+1; found:= P(i,M) UNTIL found OR (i=N-M); Вычисление Р, естественно, вновь сводится к повторяющимся сравнениям отдельных символов. Если применить к Р теорему Моргана, то получается, что итерации должны «искать» несовпадение между образом и строкой символов.
P(i, j)=(A k: 1 ≤ k < j: si+k=pk)=(~E k: 1 ≤ k < j: si+k≠pk ) В результате этого уточнения мы приходим к повторению внутри повторения. Предикаты Р и Q включаются в соответствующие места программы как примечания. Они представляют собой инварианты циклов упомянутых итеративных процессов. i:=0; REPEAT (* Q(i)*) i:=i+1; j:=0; WHILE (j < M) AND (s[ i+j ]=p[ j+1 ]) DO j:=j+1 (* P(i,j+1) *) (* Q(i) & P(i, j) & ((j=M) OR (s[ i+j ] ≠ p[ j ])) *) UNTIL (j=M) OR (i=N-M); В действительности выражение j = M в условии окончания соответствует условию found, так как из него следует P(i, М). Выражение i=N-М имплицирует Q(N—M) и тем самым свидетельствует, что нигде в строке совпадения нет. Если итерации продолжаются при j < М, то они должны продолжаться и при si+k=pk. В соответствии с (1.47) отсюда следует ~P(i, j), затем из-за (1.48) следует Q(i +1), что и подтверждает справедливость Q(i) после очередного увеличения i. Анализ прямого поиски в строке. Этот алгоритм работает достаточно эффективно, если мы допускаем, что несовпадение пары символов происходит по крайней мере после всего лишь нескольких сравнений во внутреннем цикле. При большой мощности типа item это достаточно частый случай. Можно предполагать, что для текстов, составленных из 128 символов, несовпадение будет обнаруживаться после одной или двух проверок. Тем не менее в худшем случае производительность будет внушать опасение. Возьмем, например, строку, состоящую из N-1 символов А и единственного В, а образ содержит М-1 символов А и опять В. Чтобы обнаружить совпадение в конце строки, требуется произвести порядка N*M сравнений. К счастью, как мы скоро увидим, есть прием, который существенно улучшает обработку этого неблагоприятного варианта.
Дата добавления: 2014-01-13; Просмотров: 617; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |