Как искать основанный на строке набор ключа/значения быстро

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

См. также: A хороший список лучших практик

Я бы добавил, очень важно, хорошо использовать модификатор final. Использование "окончательной" модификатор, когда это применимо в Java

Сводка:

  1. Используйте модификатор final для обеспечения хорошей инициализации.
  2. Избегайте возврата null в методы, например, при возврате пустых коллекций.
  3. Использовать аннотации @NotNull и @Nullable
  4. Быстрое завершение работы и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, когда они не должен быть пустым.
  5. Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитают valueOf() поверх toString ().
  7. Используйте null safe StringUtils StringUtils.isEmpty(null).

13
задан templatetypedef 27 November 2014 в 21:24
поделиться

6 ответов

Вы получили бы какое-либо преимущество, имеющее Ваши trie ключи, сопоставимые с размером регистра машины? Таким образом, если Вы находитесь на поле на 32 бита, можно ли сравнить 4 символа сразу вместо каждого символа индивидуально? Я не знаю, как плохо, который увеличил бы размер Вашего приложения.

0
ответ дан 2 December 2019 в 00:19
поделиться

Вот ВЗМАХ для Вас. я никоим образом не Knuthian в своем здравом смысле алгоритма

Хорошо, таким образом, наивный Trie кодирует строковые ключи путем запуска в корне дерева и спуска по ответвлениям, которые соответствуют каждой букве в ключе, запускающемся в первой букве ключа. Таким образом, ключевое "нечто" было бы отображено на (root)->f->fo->foo, и значение будет сохранено в месте, на которое указывает узел 'нечто'.

Вы ищете ЛЮБУЮ подстроку в ключе, не только подстроки, которые запускаются в начале ключа.

Так, то, что необходимо сделать, является партнером узел с ЛЮБЫМ ключом, который содержит ту конкретную подстроку. В примере нечто я дал прежде, Вы НЕ будете находить ссылку на значение нечто под узлами 'f' и 'fo'. В TST, который поддерживает тип поисков, которые Вы надеетесь делать, Вы не только нашли бы объект нечто под всеми тремя узлами ('f', 'fo', и 'нечто'), Вы также найдете его под 'o' и 'oo' также.

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

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

С другой стороны, Вы могли бы также найти, что можно сжаться, дерево через токенизацию (как сжатие zip делает), или путем сжатия узлов, которые не переходят вниз (т.е. если Вы имеете 'w '->'o '->'o '->, и первый 'o' не переходит, можно безопасно свернуть его к 'w '->'oo'). Возможно, даже хеш злой задницы мог сделать вещи легче...

Во всяком случае, КАЧАЙТЕ, как я сказал.

2
ответ дан 2 December 2019 в 00:19
поделиться

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

Вы испытываете необходимость в 2 деревьях; 1-й является значением хэш-функции к объекту области. 2-е дерево является строками поиска к значению хэш-функции. 2-е дерево имеет несколько ключей к тому же значению хэш-функции.

дерево В качестве примера 1: STCKVRFLW-> объект области

дерево 2: стек-> STCKVRFLW, STCK по-> STCKVRFLW, VRBRD, VR

, Настолько использующий поиск на 2-м дереве, дает Вам список ключей для поиска на 1-м дереве.

0
ответ дан 2 December 2019 в 00:19
поделиться

Суффиксный Массив и q - индекс

грамма, Если Ваши строки имеют строгую верхнюю границу на размере, Вы могли бы рассмотреть использование суффиксный массив : Просто дополните все свои строки к той же максимальной длине с помощью специального символа (например, пустой символ). Тогда свяжите все строки и создайте суффиксный индекс массива по ним.

Это дает Вам время выполнения поиска m * журнал n, где m является длиной Вашей строки запроса и , n является полной длиной Ваших объединенных строк. Если это все еще не достаточно хорошо и Ваш , m имеет фиксированную, маленькую длину, и Ваш алфавит ОЈ ограничивается в размере (скажите, ОЈ < 128 различных символов), можно дополнительно создать q - индекс грамма. Это позволит извлечение в [1 136] постоянное время . Однако q - таблица грамма требует ОЈ <глоток> m записи (= 8 мебибайт в случае всего 3 символов и 1 гибибайт для 4 символов!).

Создание индекса, меньшего

, Это могло бы быть возможно к [1 137], уменьшают размер q - таблица грамма (экспоненциально, в лучшем случае) путем корректировки хеш-функции. Вместо того, чтобы присвоить уникальное число каждому возможному q - грамм Вы могли бы использовать хеш-функцию с потерями. Таблица тогда должна была бы сохранить списки возможных суффиксных индексов массива вместо всего одной суффиксной записи массива, соответствующей точному совпадению. Это повлекло бы за собой, что поиск больше не является постоянным, тем не менее, потому что все записи в списке нужно было бы рассмотреть.

Между прочим, я не уверен, знакомы ли Вы с [1 138], как q - индексные работы грамма начиная с Интернета не полезны по этой теме. Я упомянул это прежде в другой теме. Я поэтому включал описание и алгоритм для конструкции в моем тезис бакалавра .

Подтверждение концепции

я записал очень маленькое подтверждение концепции C# (так как Вы заявили иначе, что работали с C#). Это работает, однако это очень медленно по двум причинам. Во-первых, суффиксное создание массива просто сортирует суффиксы. Это одно имеет время выполнения <глоток> n 2 журнал n. Существуют намного превосходящие методы. Хуже, однако, то, что я использую SubString для получения суффиксов. К сожалению.NET создает копии целого суффикса для этого. Для использования этого кода на практике удостоверьтесь, что Вы используете оперативные методы, которые не копируют данных вокруг излишне. То же верно для получения q - граммы от строки.

Это было бы возможно еще лучше для не построения m_Data строка, используемая в моем примере. Вместо этого Вы могли сохранить ссылку на исходный массив и моделировать весь мой SubString доступы путем работы над этим массивом.

однако, легко видеть, что эта реализация по существу ожидала постоянное извлечение времени (если словарь хорошего поведения)! Это - настоящий успех, который не может возможно быть разбит деревом поиска/trie!

class QGramIndex {
    private readonly int m_Maxlen;
    private readonly string m_Data;
    private readonly int m_Q;
    private int[] m_SA;
    private Dictionary<string, int> m_Dir = new Dictionary<string, int>();

    private struct StrCmp : IComparer<int> {
        public readonly String Data;
        public StrCmp(string data) { Data = data; }
        public int Compare(int x, int y) {
            return string.CompareOrdinal(Data.Substring(x), Data.Substring(y));
        }
    }

    private readonly StrCmp cmp;

    public QGramIndex(IList<string> strings, int maxlen, int q) {
        m_Maxlen = maxlen;
        m_Q = q;

        var sb = new StringBuilder(strings.Count * maxlen);
        foreach (string str in strings)
            sb.AppendFormat(str.PadRight(maxlen, '\u0000'));
        m_Data = sb.ToString();
        cmp = new StrCmp(m_Data);
        MakeSuffixArray();
        MakeIndex();
    }

    public int this[string s] { get { return FindInIndex(s); } }

    private void MakeSuffixArray() {
        // Approx. runtime: n^3 * log n!!!
        // But I claim the shortest ever implementation of a suffix array!
        m_SA = Enumerable.Range(0, m_Data.Length).ToArray();
        Array.Sort(m_SA, cmp);
    }

    private int FindInArray(int ith) {
        return Array.BinarySearch(m_SA, ith, cmp);
    }

    private int FindInIndex(string s) {
        int idx;
        if (!m_Dir.TryGetValue(s, out idx))
            return -1;
        return m_SA[idx] / m_Maxlen;
    }

    private string QGram(int i) {
        return i > m_Data.Length - m_Q ?
            m_Data.Substring(i) :
            m_Data.Substring(i, m_Q);
    }

    private void MakeIndex() {
        for (int i = 0; i < m_Data.Length; ++i) {
            int pos = FindInArray(i);
            if (pos < 0) continue;
            m_Dir[QGram(i)] = pos;
        }
    }
}

Пример использования:

static void Main(string[] args) {
    var strings = new [] { "hello", "world", "this", "is", "a",
                           "funny", "test", "which", "i", "have",
                           "taken", "much", "too", "far", "already" };

    var index = new QGramIndex(strings, 10, 3);

    var tests = new [] { "xyz", "aki", "ake", "muc", "uch", "too", "fun", "est",
                         "hic", "ell", "llo", "his" };

    foreach (var str in tests) {
        int pos = index[str];
        if (pos > -1)
            Console.WriteLine("\"{0}\" found in \"{1}\".", str, strings[pos]);
        else
            Console.WriteLine("\"{0}\" not found.", str);
    }
}
13
ответ дан 2 December 2019 в 00:19
поделиться

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

худший случай этого является O (n), но Вы только получите это, если Ваши строковые записи будут почти все идентичны. Словарь поиска, вероятно, будет довольно большим, таким образом, это будет, вероятно, хорошая идея сохранить его на диске или использовать реляционную базу данных:-)

0
ответ дан 2 December 2019 в 00:19
поделиться

РЕДАКТИРОВАНИЕ/: мой друг просто указал на глупое предположение в моей конструкции таблицы q-грамма. Конструкция может быть сделана намного более простым †“и следовательно, намного быстрее. Я отредактировал исходный код и объяснение для отражения этого. Я думаю, что это могло бы быть конечное решение .

Вдохновленный RafaЕ‚ комментарием Dowgird к моему предыдущему ответу, я обновил свой код. Я думаю, что это заслуживает собственный ответ однако, так как это также довольно длинно. Вместо того, чтобы дополнить существующие строки, этот код создает индекс по исходному массиву строк. Вместо того, чтобы хранить единственное положение, суффиксный массив хранит пару: индекс целевой строки и положение суффикса в той строке. В результате только необходимо первое число. Однако второе число необходимо для конструкции q - таблица грамма.

новая версия алгоритма создает q - таблица грамма путем обхода по суффиксному массиву вместо исходных строк. Это сохраняет двоичный поиск суффиксного массива. Следовательно, время выполнения конструкции отбрасывает от O ( n * журнал n) вниз к O ( n) (где n является размером суффиксного массива).

Уведомление, что, как мое первое решение, использование SubString результаты в большом количестве ненужных копий. Очевидное решение состоит в том, чтобы записать дополнительный метод, который создает легкую обертку вместо того, чтобы копировать строку. Сравнение тогда должно быть немного адаптировано. Это оставляют как осуществление для читателя.;-)

using Position = System.Collections.Generic.KeyValuePair<int, int>;

class QGramIndex {
    private readonly int m_Q;
    private readonly IList<string> m_Data;
    private Position[] m_SA;
    private Dictionary<string, int> m_Dir;

    public QGramIndex(IList<string> strings, int q) {
        m_Q = q;
        m_Data = strings;
        MakeSuffixArray();
        MakeIndex();
    }

    public int this[string s] { get { return FindInIndex(s); } }

    private int FindInIndex(string s) {
        int idx;
        if (!m_Dir.TryGetValue(s, out idx))
            return -1;
        return m_SA[idx].Key;
    }

    private void MakeSuffixArray() {
        int size = m_Data.Sum(str => str.Length < m_Q ? 0 : str.Length - m_Q + 1);
        m_SA = new Position[size];
        int pos = 0;
        for (int i = 0; i < m_Data.Count; ++i)
            for (int j = 0; j <= m_Data[i].Length - m_Q; ++j)
                m_SA[pos++] = new Position(i, j);

        Array.Sort(
            m_SA,
            (x, y) => string.CompareOrdinal(
                m_Data[x.Key].Substring(x.Value),
                m_Data[y.Key].Substring(y.Value)
            )
        );
    }

    private void MakeIndex() {
        m_Dir = new Dictionary<string, int>(m_SA.Length);

        // Every q-gram is a prefix in the suffix table.
        for (int i = 0; i < m_SA.Length; ++i) {
            var pos = m_SA[i];
            m_Dir[m_Data[pos.Key].Substring(pos.Value, 5)] = i;
        }
    }
}

Использование совпадает с в другом примере минус необходимое maxlen аргумент в пользу конструктора.

0
ответ дан 2 December 2019 в 00:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: