Архив автора: admin

Алгоритм Кнута-Морриса-Пратта (КМП)

Алгоритм был разработан Кнутом (Knuth) и Праттом (Pratt) и независимо от них Моррисом (Morris) в 1977 г.

Он относится к «правильным» подходам решения поставленной задачи, в отличии от тривиального подхода, рассмотренного ранее.

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

Для понимания: Префикс строки A[..i] — это строка из i первых символов строки AСуффикс строки A[j..] — это строка из |A|-j+1 последних символов. Подстроку из Aбудем обозначать как A[i..j], а A[i] — i-ый символ строки. 

В статье http://habrahabr.ru/post/111449/ сказано:

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

Многие люди ошибаются в этом пункте, считая, что не надо возвращаться назад, а можно продолжать обработку строки A с текущей позиции. Почему это не так легко продемонстрировать на примере поиска X=«AAAB» в A=«AAAAB». Первая гипотеза нас приведет к четвертому символу A: «AAAAB», где мы обнаружим несоответствие. Если не откатиться назад, то вхождение мы так и не обнаружим, хотя оно есть.

Для реализации данного алгоритма, нам необходимо рассмотреть, так называемую, префикс функцию.

Префикс-функция — это массив чисел, вычисляющийся, как наибольшая длина суффикса, совпадающего с её префиксом. Как пример, берем каждый возможный префикс строки и смотрим самое длинное совпадение начала с концом префикса (не учитывая тривиальное совпадение самого с собой).

Вот пример для «ababcaba»:

суффикспрефиксp
aa0
abab0
abaaba1
abababab2
ababcababc0
ababcaababca1
ababcabababcab2
ababcabaababcaba3

Итак, идея ясна? Ну тогда попробум написать префикс функцию в лоб:

def predkompil(x):
#первый символ всегда 0, поэтому заносим и пропускаем
d = {0:0}
for i in xrange(1,len(x)):
# проходоим от конца к началу
j = i
sdvig = 0
while j>0:
j -= 1
if x[j] == x[i-sdvig]:
sdvig += 1
else:
j += sdvig
sdvig = 0
d[i] = sdvig
return d

Вроде работает, но как то много сравнений и проходов. Попробуем оптимизировать.
Начать можно с замены строки j = i, на j = d[i-1]+1.

Что нам это дает? А то, что незачем просматривать каждый раз с конца и до начала.  Можно заметить, что если мы на предыдущем шаге (i-1) нашли вхождение > 0 (d[i-1]), то и начинать стоит с данной позиции (d[i-1]).

Пример:

При i=4 ABAAB -> 2, тогда на i+1, т.е. i=5  смотрим не с j=i(5), а с 3! Т.к. либо счетчик вырастет и станет 3 в случае ABAABA, либо сбросится в 0 при любой другой букве.
И хотя мы делаем меньше сравнений(пропускаем середину слова), однако весь совпадающий префикс мы проходим, что не есть хорошо.

Это уже не плохо, однако, мы с Вами так и не научились использовать  информацию, полученную на предыдущих и

Тибетская диета

Тип диеты — низкокалорийная

Убыль веса —   3-4 кг

Продолжительность —  7 дней

Если вы хотите не только хотите похудеть, но и омолодить свой организм обязательно попробуйте тибетскую диету. Основа диеты – молоко и продукты растительного происхождения – овощи и фрукты. Мясо, птица, яйца запрещены. Исключение составляет рыба, которая присутствует в диете в незначительном количестве, да и то не каждый день.
Основные правили тибетской диеты:

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

— перекусы между основными приемами пищи запрещены;

— питьевой режим не ограничен, но воду пить рекомендуется за час до еды, лучше родниковую или минеральную без газов;

— стараться не есть на ночь, последний прием пищи должен быть не позднее чем за два часа до сна;

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

Читать

Использование try-finally

Хочу обратить внимание на маленькую особенность написания конструкции try-finally.

Возьмём для примера многопоточность, а конкретно блокировки.

Где-то (наверное, в конструкторе класса) мы создали объект блокировки:

self.locker = threading.RLock()

Затем в каком-то методе мы пытаемся использовать эту блокировку в try-finally statement. Да, я знаю что RLock поддерживает context manager protocol и может использоваться в with statement. Так будет даже лучше, но мы сейчас говорим о другом варианте использования.

try:
self.locker.acquire()
do_some_work()
finally:
self.locker.release()

В чём ошибка? .acquire() может выбросить исключение. Блокировка не будет захвачена и попытка её освободить в .release() выбросит новое (другое) исключение. Что крайне нежелательно. Особенно в python 2.x, где нет цепочек исключений. Т.е. ошибка в .acquire() будет просто скрыта, понять в чём было дело невозможно.

Правильно писать так:

self.locker.acquire()
try:
do_some_work()
finally:
self.locker.release()

Если было исключение в .acquire() — то блокировка не захвачена и освобождать её не нужно. Пусть обработка исключения разворачивается своим ходом, .release() в finally block совершенно не нужен.

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

Проблема усугубляется тем, что обычно .acquire() работает успешно, и лишь в редких случаях выбрасывает исключение. Которое мы видим в логах (все используют логи, верно?) и недоумеваем, что именно произошло.

Это замечание относится к любому коду, выполняемому в finally block.

Переменные, блокировки, захват ресурсов, открытие файлов и т.д. должны быть выполнены перед try.

P.S.

На открытие файлов хочу обратить особое внимание как на самый частый случай. Куда более частый чем работа с многопоточностью. Правильно писать:

f = open('filename')
try:
f.read()
finally:
f.close()

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

Автор: Andrew Svetlov

Аксессуары своими руками

Украшения легко и просто

Как нам хочется щеголять весной во всей красе, не уставая удивлять окружающих все новыми и новыми нарядами и аксессуарами. С наступлением весны мы все сами пробуждаемся, у нас находится куча дел. Нужно все время куда-то бежать, что-то делать и с кем-то договариваться. Когда найти время на рукоделие? А ведь на украшения хэндмэйд нередко нужно потратить немало времени, запастить терпением и усидчивостью. Так бывает часто, но не в этот раз. Сегодня мы представляем мастер класс для ленивых или очень занятых  девушек. Бижутерия легко и просто, и, самое важное, быстро представляется вашему вниманию, дорогие читательницы.

Бижутерия своими руками

Бижутерия своими руками

Читать

batch resize, или массовое изменение размера картинок в Linux Debian/Ubuntu

Столкнулся с необходимостью быстро отресайзить папку фотографий.  Так как под рукой только Linux Ubuntu, пришлось искать способы под него.
Конечно, была идея отресайзить все картинки через Гимп, но это стрельба из пушки по воробьям. Причём медленная стрельба.
А простой и красивый способ нашёлся следующий.

Читать

South. Документация. Конвертирование приложения

Конвертирование приложения

Конвертировать приложение для  использования South очень легко:
  • Отредактируйте ваш settings.py и укажите south в INSTALLED_APPS (мы предполагаем, что Вы установили south туда, куда нужно)
  • Запустите ./manage.py syncdb для того, чтобы загрузить таблицу South в БД. Обратите внимание, что теперь syncdb не такой как раньше — South его изменил
  • Запустите ./manage.py convert_to_south myapp — South автоматически подготовит и выполнит первую миграцию
Обратите внимание, что Вы должны конвертировать приложение до того, как Вы сделаете изменения; South обнаруживает изменения по сравнению с замороженным состоянием последней миграции, так что он не сможет обнаружить изменения, сделанные до конвертирования в South.

Конвертирование других установок и серверов

Команда convert_to_south рабоатет целиком на первой машине, на которой Вы его запустите. После того, как Вы подтвердите начальную миграцию, которую он сделал, в системе управления версиями, Вы должны запустить ./manage.py migrate myapp 0001 —fake на всех машинах, которые содержат копию кода (убедитесь, что они имеют обновлённый код и схему).
(Для тех, кому интересно, это нужно поскольку начальная миграция, которую делает convert_to_south, проверит и создат все существующие таблицы; вместо этого Вы говорите South, что это уже сделано при помощи —fake, так что следующие миграции будут применены корректно).
Помните, что новые установки кода после этого не потребуют этих шагов, Вам нужно будет лишь сделать syncdb а, затем, провести нормальную миграцию.

Автор: Ishayahu Lastov