Оптимизация преобразования Берроуза Уилера

#java #text #transform #burrows-wheeler-transform

#java #текст #преобразование #берроуз-уилер-преобразование

Вопрос:

Здравствуйте, у меня возникли некоторые трудности с оптимизацией преобразования Берроуза Уилера. Я пытаюсь преобразовать текстовые файлы, однако преобразование больших текстовых файлов, таких как Библия, занимает слишком много времени.

Есть идеи о том, как действовать дальше?

 public BurrowsWheelerTransformEncoder()
{

}

private String originalSuffix(int index, String string)
{
    String temp = (string.substring(index,string.length())   string.substring(0,index));

    //this bit just 'compresses' each transformation of text by producing
    //a prefix, so 'abracadabra' just becomes 'abrac'
    //this is so minimal amount of memory is used when it is stored in an array

    return temp.substring(0,5) 
    //the last character of the transformation is kept
           temp.charAt(temp.length()-1);
}

private String compressedSuffix(String string)
{
    //this method just 'compresses' original piece of text by producing
    //a prefix, so 'abracadabra' just becomes 'abrac'
    //this is so comprisons won't take so long
    return string.substring(0,5) string.charAt(string.length()-1);
}

public static void main(String args[]) throws Exception
{
    BurrowsWheelerTransformEncoder encoder = new BurrowsWheelerTransformEncoder();
    BufferedReader input = new BufferedReader(new FileReader("src/compressionalgorithm/texts/manifesto.txt"));

    String text = "";
    //the row in the sorted array where the original text can be found
    int originalRow = 0;
    //system time when program began
    long startTime = System.nanoTime();

    //get text from file
    while(input.ready())
    {
        text  = input.readLine();
    }
    //create a new array to hold all transformations
    String[] textArray = new String[text.length()];
    int length = text.length();

    //get individual transformations and put in array
    for(int i = 0; i < text.length(); i  )
    {
        textArray[i] = encoder.originalSuffix(i,text);
        //for debugging large text files, prints progress after every 10k'th 
        //transformation
        if(i%10000==0)
        System.out.println(i "/" length);
    }
    //uses java's internal methods to sort the array, presumably 
    //the most efficient way to do the sort (for now)
    Arrays.sort(textArray);

    String compressedOriginalText = encoder.compressedSuffix(text);

    //print the results
    for(int i = 0; i < textArray.length; i  )
    {
        if(textArray[i].equals(compressedOriginalText))
        {
            originalRow = i;
        }
        if(i%100==0)
        {
            System.out.println();
        }
        System.out.print(textArray[i].charAt(textArray[i].length()-1));
    }
    System.out.println("nThe original transformation of the text was found at row "   originalRow   " of the sorted array.");
    System.out.println("Time elapsed: "   (System.nanoTime() - startTime));
 }
  

Ответ №1:

В случае кодирования вам не нужно фактически создавать массив строк — вместо этого используйте массив int (или long в зависимости от размера вашего файла) для хранения индекса, с которого начинается ваша вращающаяся строка.

  • Создайте массив, инициализированный в [0 1 2 3 … n]
  • отсортируйте массив с помощью следующего сравнения (предположим, compareTo() имеет доступ к исходной строке, original ):

     int compareTo(int a, int b){
        int compare, len = original.length();
        do{
            char _a = original.charAt(a), _b = original.charAt(b);
            compare = _a-_b;
            a  ; b  ;
            if(a>=len)a-=len;
            if(b>=len)b-=len;
        }while(compare==0);
        return compare;
    }
      
  • обратите внимание на индекс «0» в массиве и добавьте его в свой вывод в качестве начального значения

В обратном порядке, опять же, мы хотели бы избежать построения всей таблицы для текста размером с Библию. Мы можем сделать это, используя тот факт, что идентичные токены в первой и последней строке всегда находятся в одном и том же порядке. Это верно, потому что первая строка отсортирована, а токены расположены циклически: для трех последовательных b в последней строке сортируются токены после них, поэтому b сортируются. Итак, чтобы обратить:

  • отсортируйте выходные токены. Наряду с сохранением отсортированных токенов, сохраните индекс, с которого начинался каждый токен. Таким образом, для несортированных токенов «nbnaa» необходимо сохранить [3 4 5 2 0 1] и «aaabnn». Важно: для этого шага вы ДОЛЖНЫ использовать стабильную сортировку.
  • используйте значение «start», упомянутое ранее, чтобы перестроить строку:

     string decode(string sorted, int[]index, int start){
        string answer = "" sorted.charAt(start);
        int next = index[start];
        while(next!=start){
            answer = sorted.charAt(next)   answer;
            next = index[next];
        }
        return answer;
    }
      

Ответ №2:

Эта строка:

     String temp = (string.substring(index,string.length())   string.substring(0,index));
  

собирается создавать копию всего входного текста каждый раз, когда вы его вызываете. Поскольку вы вызываете его N раз для входного текста из N символов, ваш алгоритм будет O(N^2) .

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

Комментарии:

1. копирование необходимо для создания отсортированного массива преобразований

2. Нет, это не так. Или, если это так, ваша реализация этого метода нарушена. Метод создает и возвращает строку длиной в 6 символов, но при этом копирует всю входную строку целиком.