Эффективность обнаружения палиндрома

Мне стало любопытно опрос Jon Limjap nishferp и начал искать эффективные способы обнаружения палиндрома. Я проверил ответы palindrome golf, и мне кажется, что в ответах есть только два алгоритма, реверсирование строки и проверка с хвоста и головы.

def palindrome_short(s):
    length = len(s)
    for i in xrange(0,length/2):
        if s[i] != s[(length-1)-i]: return False
    return True

def palindrome_reverse(s):
    return s == s[::-1]

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

Хорошим способом может быть параллелизация первой версии в подходе "разделяй и властвуй", назначая пару массивов char 1..n и length-1-n..length-1 для каждого потока или процессора.

Что было бы лучше?

Знаете ли вы кого-нибудь?

Ответ 1

Учитывая только один палиндром, вам нужно будет сделать это в O (N), да. Вы можете повысить эффективность работы с несколькими процессорами, разделив строку, как вы сказали.

Теперь скажите, что вы хотите выполнить точный поиск ДНК. Эти строки являются длинными тысячами символов, и они очень повторяются. Это дает нам возможность оптимизировать.

Скажем, вы разделили длинную строку 1000- char на 5 пар из 100 100. Код будет выглядеть так:

isPal(w[0:100],w[-100:]) and isPail(w[101:200], w[-200:-100]) ...

и т.д. В первый раз, когда вы делаете эти совпадения, вам придется их обработать. Однако вы можете добавить все результаты, которые вы сделали, в пары отображения хэш-таблицы для booleans:

isPal = {("ATTAGC", "CGATTA"): True, ("ATTGCA", "CAGTAA"): False}

и т.д., но это займет слишком много памяти. Для пар 100 100 хэш-карта будет иметь 2 * 4 ^ 100 элементов. Скажем, что вы сохраняете только два 32-х битных хэша строк в качестве ключа, вам понадобится что-то вроде 10 ^ 55 мегабайт, что смешно.

Возможно, если вы используете меньшие строки, проблема может быть приемлемой. Тогда у вас будет огромный hashmap, но, по крайней мере, палиндром, пусть говорят, что пары 10x10 возьмут O (1), поэтому проверка, если строка 1000 является палиндром, займет 100 поисковых запросов вместо 500 сравнений. Он все еще O (N), хотя...

Ответ 2

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

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

Если вы хотите распараллелить его, вы можете разделить строку на N частей и иметь процессор i сравнить сегмент [i*n/2, (i+1)*N/2) с сегментом [L-(i+1)*N/2, L-i*N/2).

Ответ 3

Нет, если вы не выполняете нечеткое совпадение. Это то, что они, вероятно, делают в ДНК (я выполнил поиск EST в ДНК с помощью smith-waterman, но это, очевидно, намного сложнее, чем сопоставление для палиндрома или обратного дополнения в последовательности).

Ответ 4

Они оба находятся в O (N), поэтому я не думаю, что какая-либо проблема эффективности с любым из этих решений. Может быть, я недостаточно творческий, но я не вижу, как можно было бы сравнивать N элементов меньше чем N шагов, поэтому что-то вроде O (log N), безусловно, не возможно ИМХО.

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

Ответ 5

Другой вариант вашей второй функции. Нам не нужно проверять равные правильные части нормальных и обратных строк.

def palindrome_reverse(s):
  l = len(s) / 2
  return s[:l] == s[l::-1]

Ответ 6

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

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

Ответ 7

С Python короткий код может быть быстрее, поскольку он загружает нагрузку в более быструю внутреннюю часть виртуальной машины (и есть весь кеш и другие подобные вещи)

def ispalin(x):
   return all(x[a]==x[-a-1] for a in xrange(len(x)>>1))

Ответ 8

Вы можете использовать хеш-таблицу, чтобы поместить символ и иметь переменную счетчика, значение которой увеличивается каждый раз, когда вы находите элемент не в таблице/карте. Если u проверяет и находит элемент, который уже в таблице уменьшает счетчик.

For odd lettered string the counter should be back to 1 and for even it should hit 0.I hope this approach is right.

See below the snippet.
s->refers to string
eg: String s="abbcaddc";
Hashtable<Character,Integer> textMap= new Hashtable<Character,Integer>();
        char charA[]= s.toCharArray();
        for(int i=0;i<charA.length;i++)
        {

            if(!textMap.containsKey(charA[i]))
            {   
                textMap.put(charA[i], ++count);

            }
            else
                {
                textMap.put(charA[i],--count);


        }
        if(length%2 !=0)
        {
            if(count == 1)
            System.out.println("(odd case:PALINDROME)");
            else
                System.out.println("(odd case:not palindrome)");
        }
        else if(length%2==0)    
        {
            if(count ==0)
                System.out.println("(even case:palindrome)");
            else
                System.out.println("(even case :not palindrome)");
        }