Иерархическое кодирование длин серий без потерь - PullRequest
11 голосов
/ 29 июля 2011

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

Например, я хочу, чтобы ABCBCABCBCDEEF стал: (2A (2BC)) D (2E) F

Меня не беспокоит, что вариант выбирается между двумя одинаковыми возможными вложениями, например,

ABBABBABBABA может быть (3ABB) ABA или A (3BBA) BA, которые имеют одинаковую сжатую длину, несмотря на наличие разных структур.

Однако я хочу, чтобы выбор был НАИБОЛЕЕ жадным. Например:

ABCDABCDCDCDCD выберет (2ABCD) (3CD) - длиной шесть в исходных символах, которая меньше, чем ABCDAB (4CD), которая имеет длину 8 в исходных символах.

Что касается фона, у меня есть несколько повторяющихся шаблонов, которые я хочу обобщить. Так что данные более удобочитаемы. Я не хочу нарушать логический порядок данных, так как это важно. но я хочу подвести итог, сказав, что символ A раз 3, затем символы XYZ для 20 случаев и т. д., и это можно визуально отобразить во вложенном смысле.

Приветственные идеи.

Ответы [ 2 ]

3 голосов
/ 29 июля 2011

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

Вы можете вставить следующий код в LINQPad и запустить его, и он должен выдать следующий результат:

ABCBCABCBCDEEF = (2A(2BC))D(2E)F
ABBABBABBABA = (3A(2B))ABA
ABCDABCDCDCDCD = (2ABCD)(3CD)

Как видите, средний пример закодировал ABB как A(2B) вместо ABB вы должны были бы сделать это самостоятельно, если подобные односимвольные последовательности должны быть закодированы как повторяющийся символ или нет, или если следует использовать определенный порог (например, 3 или более).

По сути, код работает так:

  1. Для каждой позиции в последовательности, попробуйте найти самое длинное совпадение (на самом деле, это не так, требуется первое 2+ совпадение с нимобнаруживает, я оставил остальное как упражнение для вас, так как я должен оставить свой компьютер на несколько часов)
  2. Затем он пытается закодировать эту последовательность, ту, которая повторяется, рекурсивно и выплевывает X* seq тип объектаect
  3. Если он не может найти повторяющуюся последовательность, он выплевывает один символ в этом месте
  4. Затем он пропускает то, что закодировал, и продолжает с # 1

В любом случае, вот код:

void Main()
{
    string[] examples = new[]
    {
        "ABCBCABCBCDEEF",
        "ABBABBABBABA",
        "ABCDABCDCDCDCD",
    };

    foreach (string example in examples)
    {
        StringBuilder sb = new StringBuilder();
        foreach (var r in Encode(example))
            sb.Append(r.ToString());
        Debug.WriteLine(example + " = " + sb.ToString());
    }
}

public static IEnumerable<Repeat<T>> Encode<T>(IEnumerable<T> values)
{
    return Encode<T>(values, EqualityComparer<T>.Default);
}

public static IEnumerable<Repeat<T>> Encode<T>(IEnumerable<T> values, IEqualityComparer<T> comparer)
{
    List<T> sequence = new List<T>(values);

    int index = 0;
    while (index < sequence.Count)
    {
        var bestSequence = FindBestSequence<T>(sequence, index, comparer);
        if (bestSequence == null || bestSequence.Length < 1)
            throw new InvalidOperationException("Unable to find sequence at position " + index);

        yield return bestSequence;
        index += bestSequence.Length;
    }
}

private static Repeat<T> FindBestSequence<T>(IList<T> sequence, int startIndex, IEqualityComparer<T> comparer)
{
    int sequenceLength = 1;
    while (startIndex + sequenceLength * 2 <= sequence.Count)
    {
        if (comparer.Equals(sequence[startIndex], sequence[startIndex + sequenceLength]))
        {
            bool atLeast2Repeats = true;
            for (int index = 0; index < sequenceLength; index++)
            {
                if (!comparer.Equals(sequence[startIndex + index], sequence[startIndex + sequenceLength + index]))
                {
                    atLeast2Repeats = false;
                    break;
                }
            }
            if (atLeast2Repeats)
            {
                int count = 2;
                while (startIndex + sequenceLength * (count + 1) <= sequence.Count)
                {
                    bool anotherRepeat = true;
                    for (int index = 0; index < sequenceLength; index++)
                    {
                        if (!comparer.Equals(sequence[startIndex + index], sequence[startIndex + sequenceLength * count + index]))
                        {
                            anotherRepeat = false;
                            break;
                        }
                    }
                    if (anotherRepeat)
                        count++;
                    else
                        break;
                }

                List<T> oneSequence = Enumerable.Range(0, sequenceLength).Select(i => sequence[startIndex + i]).ToList();
                var repeatedSequence = Encode<T>(oneSequence, comparer).ToArray();
                return new SequenceRepeat<T>(count, repeatedSequence);
            }
        }

        sequenceLength++;
    }

    // fall back, we could not find anything that repeated at all
    return new SingleSymbol<T>(sequence[startIndex]);
}

public abstract class Repeat<T>
{
    public int Count { get; private set; }

    protected Repeat(int count)
    {
        Count = count;
    }

    public abstract int Length
    {
        get;
    }
}

public class SingleSymbol<T> : Repeat<T>
{
    public T Value { get; private set; }

    public SingleSymbol(T value)
        : base(1)
    {
        Value = value;
    }

    public override string ToString()
    {
        return string.Format("{0}", Value);
    }

    public override int Length
    {
        get
        {
            return Count;
        }
    }
}

public class SequenceRepeat<T> : Repeat<T>
{
    public Repeat<T>[] Values { get; private set; }

    public SequenceRepeat(int count, Repeat<T>[] values)
        : base(count)
    {
        Values = values;
    }

    public override string ToString()
    {
        return string.Format("({0}{1})", Count, string.Join("", Values.Select(v => v.ToString())));
    }

    public override int Length
    {
        get
        {
            int oneLength = 0;
            foreach (var value in Values)
                oneLength += value.Length;
            return Count * oneLength;
        }
    }
}

public class GroupRepeat<T> : Repeat<T>
{
    public Repeat<T> Group { get; private set; }

    public GroupRepeat(int count, Repeat<T> group)
        : base(count)
    {
        Group = group;
    }

    public override string ToString()
    {
        return string.Format("({0}{1})", Count, Group);
    }

    public override int Length
    {
        get
        {
            return Count * Group.Length;
        }
    }
}
1 голос
/ 31 июля 2011

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


ABCBCABCBCDEEF
s->ttDuuF
t->Avv
v->BC
u->E

ABABCDABABCD
s->ABtt
t->ABCD

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

Известно, что проблема наименьшей грамматики является сложной и хорошо изученной. Я не знаю, сколько часть «прямой последовательности» добавляет или вычитает из сложности.

...