В настоящей статье я хотел бы сделать обзор популярных алгоритмов для решения задачи нахождения максимальной общей подпоследовательности или LCS (longest common sequense). Так как акцент сделан на обучении, а не на реальном использовании, в качестве языка для реализации выбран Python, что позволит сократить количество кода и сконцентрироваться на основных идеях.
Определения
Последовательность представляет собой упорядоченный набор элементов. Строка — это частный случай последовательности, дальнейшие примеры будут для простоты рассматривать именно строки, но без изменений их можно использовать и для произвольного текста или чего-нибудь последовательного еще.
Пусть имеется последовательность x, состоящая из элементов x1x2. xm и последовательность y, состоящая из элементов y1y2. yn. z — подпоследовательность x в том случае, если существует строго возрастающий набор индексов элементов x, из которых получается z.
Общей подпоследовательностью для x и y считаем такую последовательность z, которая является одновременно подпоследовательностью x и подпоследовательностью y.
Программа по поиску максимума последовательности и сумме всех чисел
Максимальная общая подпоследовательность — это общая подпоследовательность с максимальной длинной. Далее по тексту будем использовать сокращение LCS.
В качестве примера, пусть x=HABRAHABR, y=HARBOUR, в этом случае LCS(x, y)=HARBR. Можно уже переходить непосредственно к алгоритму вычисления LCS, но, хорошо бы понять, для чего нам может это может понадобиться.
Применение на практике
Наиболее частое применение — использование в программах для сравнения файлов, например GNU diff. Имея найденную для двух текстов LCS, составить список элементарных изменений для превращения x в y или обратно задача тривиальная. В качестве бонуса, на основе длины общей подпоследовательности можно задать метрику для определения схожести двух последовательностей. Все, теперь точно можно переходить к делу.
Первый подход или народное творчество
- Если для последовательностей x и y нами уже вычислена LCS(x, y)=z, то, LCS для последовательностей полученных из x и y путем добавления одинакового элемента, будет состоять из z и этого добавленного элемента
- Если мы добавим к последовательностям x и y по одному разному элементу, то для полученных таким образом xa и yb, LCS должна быть большая из двух: LCS(x,yb) или LCS(xa,y)
def LCS_RECURSIVE(x, y): if len(x) == 0 or len(y) == 0: return [] if x[-1] == y[-1]: return LCS_RECURSIVE(x[:-1], y[:-1]) + [x[-1]] else: left = LCS_RECURSIVE(x[:-1], y) right = LCS_RECURSIVE(x, y[:-1]) return left if len(left) > len(right) else right
Теперь можно подумать, что с этой реализацией не так. В наихудшем случае, когда между x и y нет одинаковых элементов LCS_RECURSIVE вызовется 2 len(x)+len(y) раз, при уровне рекурсии len(x)+len(y). Даже если закрыть глаза на производительность, код:
from uuid import uuid4 x = [uuid4().hex for _ in xrange(500)] y = [uuid4().hex for _ in xrange(500)] print LCS_RECURSIVE(x,y)
без дополнительного вызова sys.setrecursionlimit удачно не выполнится. Не самая удачная реализация.
2.6 Задачи по материалам недели. Последовательность. Программирование на Python. Курс Stepik
Динамическое программирование или 64 кб хватит всем
Рассматриваемый алгоритм также известен как алгоритм Нидлмана—Вунша (Needleman-Wunsch).
Весь подход сводится к поэтапному заполнению матрицы, где строки представляют собой элементы x, а колонки элементы y. При заполнении действуют два правила, вытекающие из уже сделанных наблюдений:
1. Если элемент xi равен yj то в ячейке (i,j) записывается значение ячейки (i-1,j-1) с добавлением единицы
2. Если элемент xi не равен yj то в ячейку (i,j) записывается максимум из значений(i-1,j) и (i,j-1).
Заполнение происходит в двойном цикле по i и j с увеличением значений на единицу, таким образом на каждой итерации нужные на этом шаге значения ячеек уже вычислены:
def fill_dyn_matrix(x, y): L = [[0]*(len(y)+1) for _ in xrange(len(x)+1)] for x_i,x_elem in enumerate(x): for y_i,y_elem in enumerate(y): if x_elem == y_elem: L[x_i][y_i] = L[x_i-1][y_i-1] + 1 else: L[x_i][y_i] = max((L[x_i][y_i-1],L[x_i-1][y_i])) return L
Ячейки, в которых непосредственно происходило увеличение значения подсвечены. После заполнения матрицы, соединив эти ячейки, мы получим искомый LCS. Соединять при этом нужно двигаясь от максимальных индексов к минимальным, если ячейка подсвечена, то добавляем соответствующий элемент к LCS, если нет, то двигаемся вверх или влево в зависимости от того где находится большее значение в матрице:
def LCS_DYN(x, y): L = fill_dyn_matrix(x, y) LCS = [] x_i,y_i = len(x)-1,len(y)-1 while x_i >= 0 and y_i >= 0: if x[x_i] == y[y_i]: LCS.append(x[x_i]) x_i, y_i = x_i-1, y_i-1 elif L[x_i-1][y_i] > L[x_i][y_i-1]: x_i -= 1 else: y_i -= 1 LCS.reverse() return LCS
Сложность алогоритма — O(len(x)*len(y)), такая же оценка по памяти. Таким образом, если я захочу построчно сравнить два файла из 100000 строк, то нужно будет хранить в памяти матрицу из 10 10 элементов. Т.е. реальное использование грозит получением MemoryError почти на ровном месте. Перед тем как перейти к следующему алгоритму заметим, что при заполнении матрицы L на каждой итерации по элементам x нам нужна только строка, полученная на предыщем ходу. Т.е. если бы задача ограничивалась только нахождением длины LCS без необходимости вычисления самой LCS, то можно было бы снизить использование памяти до O(len(y)), сохраняя одновременно только две строки матрицы L.
Борьба за место или Алгоритм Хишберга (Hirschberg)
Идея в основе этого алгоритма проста: если разделить входную последовательность x=x1x2. xm на две произвольные части по любому граничному индексу i на xb=x1x2. xi и xe=xi+1xi+2. xm, то найдется способ аналогичного разбиения y (найдется такой индекс j, разбивающий y на yb=y1y2. yj и ye=yj+1yj+2. yn), такой, что LCS(x,y) = LCS(xb,yb) + LCS(xe,ye).
Для того, чтобы найти это разбиение y предлагается:
- Заполнить динамическую матрицу L с помощью fill_dyn_matrix для xs и y. L1 приравняем последней строке вычисленной матрицы L
- Заполнить матрицу L для *xe и *y (обратные последовательности для xe и y, в терминах Python: list(reversed(x)), list(reversed(y))). Приравняем *L2 последнюю строку вычисленной матрицы L, L2 это риверс от *L2
- Принять j равным индексу, для которого сумма L1[j]+L2[j] максимальна
Это можно представить, как заполнение матрицы L с двух противоположных концов:
Заметим, что раз есть необходимость только в последней строке матрицы L, то при вычислении хранить всю матрицу не нужно. Немного изменив реализацию fill_dyn_matrix получим:
def lcs_length(x, y): curr = [0]*(1 + len(y)) for x_elem in x: prev = curr[:] for y_i, y_elem in enumerate(y): if x_elem == y_elem: curr[y_i + 1] = prev[y_i] + 1 else: curr[y_i + 1] = max(curr[y_i], prev[y_i + 1]) return curr
Теперь непосредственно, о самом алгоритме. Он представляет собой классический divide and conquer: пока в последовательности x есть элементы, мы делим x пополам, находим подходящее разбиение для y и возвращаем сумму рекурсивных вызовов для пар последовательностей (xb,yb) и (xe,ye). Заметим, что в тривиальном случае, если x состоит из одного элемента и встречается в y мы просто возвращаем последовательность из этого единственного элемента x.
def LCS_HIRSHBERG(x, y): x_len = len(x) if x_len == 0: return [] elif x_len == 1: if x[0] in y: return [x[0]] else: return [] else: i = x_len // 2 xb, xe = x[:i], x[i:] L1 = lcs_length(xb, y) L2 = reversed(lcs_length(xe[::-1], y[::-1])) SUM = (l1 + l2 for l1,l2 in itertools.izip(L1, L2)) _, j = max((sum_val, sum_i) for sum_i, sum_val in enumerate(SUM)) yb, ye = y[:j], y[j:] return LCS_HIRSHBERG(xb, yb) + LCS_HIRSHBERG(xe, ye)
Теперь требования по памяти у нас O(len(y)), время, необходимое для вычисления, удвоилось, но асимптотически по-прежнему O(len(x)len(y)).
Алгоритм Ханта-Шуманского (Hunt-Szymanski Algorithm)
Первое что нам потребуется это создание хэш таблицы matchlist, которая будет содержать индексы элементов второй последовательностей. Время необходимое для этого O(len(y)). Следующий код на питоне делает это:
y_matchlist = <> for index, elem in enumerate(y): indexes = y_matchlist.setdefault(elem, []) indexes.append(index) y_matchlist[elem] = indexes
x_length, y_length = len(x), len(y) min_length = min(x_length, y_length) THRESH = list(itertools.repeat(y_length, min_length+1)) LINK_s1 = list(itertools.repeat(None, min_length+1)) LINK_s2 = list(itertools.repeat(None, min_length+1)) THRESH[0], t = -1, 0 for x_index,x_elem in enumerate(x): y_indexes = y_matchlist.get(x_elem, []) for y_index in reversed(y_indexes): k_start = bisect.bisect_left(THRESH, y_index, 1, t) k_end = bisect.bisect_right(THRESH, y_index, k_start, t) for k in xrange(k_start, k_end+2): if THRESH[k-1] < y_index and y_index < THRESH[k]: THRESH[k] = y_index LINK_x[k] = (x_index, LINK_x[k-1]) if k >t: t = k
После этого нам остается только собрать из LINK_x саму последовательность.
Время работы этого алгоритма равно O((r + n) log n), где n — длина большей последовательности, а r — количество совпадений, в худшем случае при r = n*n, мы получаем время работы хуже чем в предыдущем варианте решения. Требования по памяти остаются порядка O(r+n).
Итого
Алгоритмов решающих данную проблему довольно много, асимптотически, самый эффективный алгоритм (Masek and Paterson) имеет оценку по времени O(n*n/log n). Учитывая общую небольшую эффективность при вычислениях LCS, на практике перед работой алгоритма выполняются простейшие подготовки, вроде отбрасывания одинаковых элементов в начале и в конце последовательностей и поиск тривиальных отличий между последовательностями. Также, существуют некоторые оптимизации с использованием битовых операций, не влияющие на асимптотику времени работы.
скачать весь код с примерами
Источник: habr.com
Определение последовательности
Воспользуйтесь нашим простым онлайн-калькулятором, чтобы определить последовательность любой серии чисел.
- Главная
- Определите последовательность
Как пользоваться калькулятором идентификации последовательности
Шаг 1
Введите свой набор чисел в поле ввода. Цифры следует разделять запятыми.
Шаг 2
Нажмите Enter на клавиатуре или на стрелку справа от поля ввода.
Шаг 3
Во всплывающем окне выберите «Определить последовательность». Вы также можете воспользоваться поиском.
Что такое определение последовательности в математике
Последовательность — это такой набор элементов числа, который:
— Для каждого натурального числа вы можете указать элемент этого набора.
— Этот номер является номером позиции и указывает позицию позиции в последовательности.
— Для любого элемента (члена) последовательности вы можете указать последовательность, которая следует за ним.
Таким образом, последовательность является результатом последовательного выбора элементов данного набора. Если какой-либо окончательный набор элементов назвать образцом окончательного объема, последовательность окажется образцом бесконечного объема.
Онлайн-калькулятор медианы позволяет каждому легко вычислить медианное значение любого набор чисел в 3 простых шага. Расчет медианы, среднего, режима, диапазона, значений частоты, нет независимо от того, есть ли у вас набор целых чисел или дробей. Формула медианы осталась прежней. Хотя расчет прост и может быть выполнен даже в Excel, для большого набора символов вам может понадобиться более удобный инструмент.
Источник: mediancalculator.com
Основания биоинформатики
Примеры сравнения данных
Поиск схожих последовательностей в базах данных
Прежде чем проводить анализ биологических последовательностей, необходимо эти последовательности отыскать в базах данных. Например, если вы определили последовательность нового гена или нашли в геноме человека ген, ответственный за какое-то заболевание, то вы, возможно, захотите узнать, нет ли таких генов у других видов. Идеальный метод — тот, который с одной стороны чувствителен (который определяет даже дальнее родство), а, с другой стороны, селективен (благодаря которому все полученные родственные связи — истинные).
Методы поиска в базах данных подразумевают компромисс между чувствительностью и селективностью. Находит ли метод все или большинство из последовательностей, которые на самом деле существуют, или же он упускает большую их часть? А также, сколько из выданных этим методом результатов являются неправильными?
Предположим, база данных содержит 1000 последовательностей глобина. Предположим, поиск в этой базе данных по глобинам выдал 900 находок, 700 из них действительно последовательности глобина, а 200 таковыми не являются. Про такой поиск можно сказать, что у него 300 ложных отрицательных (false negatives) результатов (упущенных, не обнаруженных последовательностей) и 200 ложных положительных (false positives) результатов (обнаруженные последовательности в действительности не являются искомыми). Уменьшая порог допустимости, мы получим меньше ложных отрицательных результатов, но больше ложных положительных результатов.
Часто лучше работать с низкими порогами, чтобы быть уверенным, что ничего из того, что могло бы быть важным, не утеряно; но тогда потребуется детальная проверка результатов, для того чтобы устранить ложные находки.
Мощным инструментом для поиска последовательностей в базах данных, по имеющейся у нас последовательности, является программа BLAST (Basic Linear Alignment Sequence Tool), которую можно использовать с сайта NCBI http://www.ncbi.nlm.nih.gov/ (рисунок 30).
Рисунок 30 — Веб-страница NCBI; ссылка на программу BLAST показана в нижнем правом углу рисунка
Переход по ссылке «BLAST» http://blast.ncbi.nlm.nih.gov/Blast.cgi показан на рисунке 31. На рисунке отображена та часть страницы, которая относится только к основным (Basic) подпрограммам. Она включает:
✵ nucleotide blast — поиск данной последовательности нуклеотидов в базах данных нуклеиновых кислот используя алгоритмы blastn, megablast, dmegablast (discontiguous megablast);
✵ protein blast — поиск данной аминокислотной последовательности в базах данных белков используя алгоритмы blastp, psi-blast, phi-blast;
✵ blastx — переводит изучаемую нуклеотидную последовательность в кодируемые аминокислоты, а затем сравнивает её с имеющейся базой данных аминокислотных последовательностей белков;
✵ tblastn — изучаемая аминокислотная последовательность сравнивается с транслированными последовательностями базы данных секвенированных нуклеиновых кислот;
✵ tblastx — переводит изучаемую нуклеотидную последовательность в аминокислотную, а затем сравнивает её с транслированными последовательностями базы данных секвенированных нуклеиновых кислот.
Рисунок 31 — Веб-страница программы BLAST
megablast — быстрое сравнение с целью поиска высоко сходных последовательностей;
dmegablast — быстрое сравнение с целью поиска дивергировавших последовательностей, обладающих незначительным сходством;
blastn — медленное сравнение с целью поиска всех сходных нуклеотидных последовательностей;
blastp — медленное сравнение с целью поиска всех сходных белковых (protein) последовательностей;
psi-blast — Position-Specific Iterated BLAST — сравнение с целью поиска последовательностей, обладающих незначительным сходством;
phi-blast — Pattern Нit Initiated BLAST — поиск белков, содержащих определённый пользователем паттерн.
Паттерн — (от англ. pattern — образец, шаблон, модель) — это либо фрагмент последовательности, либо (реже) некий стандартный набор процедур, применяемый к разным объектам.
Пример 4. Г омологи РАХ-6 гена человека.
Гены РАХ-6 контролируют развитие глаза в широком наборе видов.
Глаза человека, мухи и осьминога сильно различаются по строению. Ранее, принимая во внимание то конкурентное преимущество, которое даёт зрение, считалось, что глаза возникли независимо в каждой эволюционной ветви. Поэтому большим сюрпризом стал тот факт, что ген, контролирующий развитие человеческого глаза, имеет гомолога, управляющего развитием глаза дрозофилы.
Ген РАХ-6 был клонирован вначале у мыши и человека. Он является главным регуляторным геном, контролирующим сложный каскад событий в развитии глаза.
Мутации в гене человека вызывают клиническое состояние — аниридию — дефект в развитии глаза, при котором радужная оболочка отсутствует или деформирована.
Гомолог гена РАХ-6 в дрозофиле называется — eyeless-ген (имеет сходную функцию контроля развития глаза). Мухи, мутантные по этому гену, развиваются без глаз; и обратно, экспрессия этого гена на лапке мухи или на антенне мухи — вызывает появление эктопических (то есть находящихся не на месте) глаз. Дрозофила, мутантная по гену eyeless, была впервые описана в 1915 г. Никто и не подозревал о его родстве с геном млекопитающих. Гены насекомого и млекопитающего схожи не только по последовательности, они так близкородственны, что их активность выходит за рамки видов. Экспрессия мышиного РАХ-6 в мухе вызывает эктопическое развитие глаза, также как и собственный eyeless ген мухи.
Гомологи РАХ-6 представлены и в других классах, включая плоских червей, асцидий, морских ежей и нематод. Наблюдение, что родопсины (семейство белков, содержащих ретин в качестве хромофора) функционируют, как светочувствительные пигменты в различных классах организмов, является дополнительным доказательством общего происхождения различных систем фоторецепторов.
Настоящие структурные различия в макроскопическом строении различных глаз отражают дивергенцию и независимость развития высокоорганизованных структур.
Ген РАХ-6 человека кодирует белок, имеющий Swiss-Prot-идентификатор — Р26367. Значение этого идентификатора можно получить, если в окно поиска программы UniProt (рисунок 23) ввести «РАХ-6» и нажать «Search».
Из окна программы «BLAST» http://blast.ncbi.nlm.nih.gov/Blast.cgi (рисунок 31) запускаем «protein blast» и вводим идентификатор sp|P26367 в окно «Enter Query Sequence» (рисунок 32).
Выбираем алгоритм «PSI-BLAST» в окне «Program Selection» и запускаем поиск, нажав кнопку «BLAST» в нижнем левом углу окна (рисунок 32).
Результат поиска представляет собой огромное (в длину) окно, большую часть которого занимает список записей схожих с последовательностью, заданной для поиска, сортированный в порядке убывания статистической значимости.
Начало этого списка показано на рисунке 33.
Рисунок 32 — Окно ввода программы BLAST
Рисунок 33 — Окно вывода программы BLAST
Каждая строка содержит одно совпадение с каким-либо геном. Рассмотрим, например, третью от начала строку, в которой приведены результаты для:
В первом столбце «Accession» располагается идентификатор гена (NP_001035735.1). Это гомолог Paired box protein Pax-6 [Bos taurus]. Базы данных обозначены в последнем столбце, в данном случае это базы данных UniGene, Gene Structure и Map Viewer.
Число 868 — это количество очков, присвоенное обнаруженному совпадению.
Значимость данного совпадения (E-value) измерена как Е = 0.0.
E-value (expectation value) определяется вероятностью того, что данная степень сходства может быть случайной.
E-value — это ожидаемое количество последовательностей, которые совпадут также или лучше чем данная, если поиск будет производиться базе данных такого же размера, но со случайными последовательностями.
Е = 0.0 означает полное соответствие.
Подробнее параметры выравнивания будут рассмотрены в п. 8.5.
Результат попарного выравнивания генов РАХ-6 человека и РАХ-6 буйвола (который находится в этом же окне внизу после списка совпадений) демонстрирует их абсолютное подобие (рисунок 34).
Рисунок 34 — Парное выравнивание генов РАХ-6 человека и буйвола в окне вывода программы BLAST
Чем больше значение Е, тем больше отклонений при сравнении последовательностей. Так, например, для гомолога twin eyeless Drosophila (NP_524638.3) значение E = 7∙10 -145 (рисунок 35(a)).
Рисунок 35 — Схожие гены РАХ-6 человека и twin eyeless дрозофилы: а — результат поиска; б — парное выравнивание в окне вывода программы BLAST
Парное выравнивание генов РАХ-6 человека и twin eyeless дрозофилы показывает уже значительные различия в последовательностях (рисунок 35(6)).
Контрольные вопросы и задания
1. Перечислите уровни иерархии биологической номенклатуры на примере человека и плодовой мушки.
2. Какие органы называются гомологичными?
3. Чем отличается дивергентная и конвергентная эволюции?
4. На какие три империи разделил все организмы Карл Вёзе, основываясь на анализе рибосомных РНК?
5. Какими символами в окне результатов программы ClustalW2 обозначаются: одинаковая аминокислота; сходные аминокислоты; вставки; отсутствие сходства в последовательностях?
6. Чем различаются подобие и гомология последовательностей?
7. Что такое ложные отрицательные и ложные положительные результаты поиска?
8. Для чего предназначена программа BLAST?
9. Что такое nucleotide blast?
10. Перечислите алгоритмы, которые используются в nucleotide blast.
11. Что такое protein blast?
12. Перечислите алгоритмы, которые используются в protein blast.
13. Что такое blastx?
14. Что такое tblastnl
15. Что такое tblastx?
16. Что такое megablast?
17. Что такое psi-blast!
18. Что такое паттерн?
19. Что такое E-value Е-значение) последовательностей?
Источник: lifelib.info