Как лучше всего посчитать и отсортировать строковый массив? - PullRequest
9 голосов
/ 13 февраля 2012

Я пытаюсь найти, если есть хороший способ поиска (подсчитать количество вхождений), а затем эффективно отсортировать массив String ... это способ, который будет хорошо работать во встроенных системах (32 МБ)

Пример: мне нужно посчитать, сколько раз символ A, B, C и т. Д ... используется, за исключением результата для последующей сортировки ...

Я могу считать, используя общедоступныйМетод int count (String searchDomain, char searchValue), но каждая строка должна иметь все буквы алфавита, например:

"This is a test string"
A:1,B:0,C:0,D:0,E:1,I:3,F:0,...
"ACAAGATGCCATTGTCCCCCGGCCTCCTGCTGCTGCTGCTCTCCGGGGCCACGGCCACCGCTGCCCTGCC"
A:7,B:0,C:22,G:18

Мой метод сортировки должен иметь возможность отвечать на такие вещи, как: Сортировать по количеству As,Bs сортирует сначала по As, а затем сортирует этот поддомен по Bs

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

Ответы [ 8 ]

11 голосов
/ 16 февраля 2012

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

class Item
{
    // Your string. It's public, so you can get it if you want,
    // but also final, so you can't accidentally change it.
    public final String string;

    // An array of counts, where the offset is the alphabetical position
    // of the letter it's counting. (A = 0, B = 1, C=2...)
    private final short[] instanceCounts = new short[32];

    public Item(String string)
    {
        this.string = string;
        for(char c : string.toCharArray())
        {
            // Increment the count for this character
            instanceCounts[(byte)c - 65] ++;
        }
    }

    public int getCount(char c)
    {
        return instanceCounts[(byte)c - 65];
    }
}

Это будет содержать вашу строку (для поиска и отображения), и установить массив шорт с количеством соответствующих символов. (Если у вас действительно недостаточно памяти и вы знаете, что в ваших строках содержится более 255 символов, вы можете даже изменить это на массив байтов.) Сокращение составляет всего 16 байтов, поэтому Сам массив займет всего 64 байта, независимо от того, насколько сложна ваша строка. Если вы предпочитаете платить за производительность каждый раз, когда вычисляете число, вы можете избавиться от массива и заменить метод getCount (), но вам, вероятно, придется сэкономить разовую память, потребляя часто собираемый мусор память, которая является большой удар по производительности. :)

Теперь определите правило, по которому вы хотите искать, используя Comparator. Например, чтобы отсортировать по количеству A в вашей строке:

class CompareByNumberOfA implements Comparator<Item>
{
    public int compare(Item arg0, Item arg1) 
    {
        return arg1.getCount('A') - arg0.getCount('A');
    }
}

Наконец, поместите все свои элементы в массив и используйте для сортировки встроенные (и очень эффективные по памяти) методы Arrays. Например:

public static void main(String args[])
{
    Item[] items = new Item[5];
    items[0]= new Item("ABC");
    items[1]= new Item("ABCAA");
    items[2]= new Item("ABCAAC");
    items[3]= new Item("ABCAAA");
    items[4]= new Item("ABBABZ");

    // THIS IS THE IMPORTANT PART!
    Arrays.sort(items, new CompareByNumberOfA());

    System.out.println(items[0].string);
    System.out.println(items[1].string);
    System.out.println(items[2].string);
    System.out.println(items[3].string);
    System.out.println(items[4].string);
}

Вы можете определить целую группу компараторов и использовать их по своему усмотрению.

Одна из вещей, о которых нужно помнить при написании кода с использованием Java, это не слишком умный подход. Компиляторы прекрасно справляются с оптимизацией под свою платформу, если вы пользуетесь преимуществами, которые они могут оптимизировать (например, встроенными API, включая Arrays.sort).

Часто, если вы пытаетесь стать слишком умным, вы просто оптимизируете себя прямо из эффективного решения. :)

1 голос
/ 22 февраля 2012

Извините, у меня нет времени, чтобы написать это лучше.Чтобы минимизировать пространство, я бы сделал два mxn (плотных) массива, один байт и один короткий, где:

  • m - количество строк ввода
  • n - количество символовв каждой строке;это измерение варьируется от строки к строке
  • массив байтов содержит символ
  • короткий массив содержит счетчик для этого символа

Если счет гарантирован <256,Вы могли бы просто использовать один 2-байтовый массив mxnx. </p>

Если набор символов, который вы используете, плотный, то есть набор ВСЕХ символов, используемых в ЛЮБОЙ строке, не намного больше, чем набор символов, используемых в КАЖДОМСтрока, вы можете избавиться от байтового массива и просто использовать фиксированное «n» (выше) с функцией, которая отображается от символа к индексу.Это было бы намного быстрее.

Это потребовало бы обходов этого массива за 2Q для любого запроса с предложениями Q.Надеюсь, это будет достаточно быстро.

1 голос
/ 16 февраля 2012

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

Я не уверен, является ли это решением или повторением вопроса. Вы хотите структуру данных, которая является деревом, где корень имеет, например, 26 поддеревьев, одно для строк, начинающихся с «A», следующий дочерний для «B» и т. Д .; тогда ребенок «А» имеет, например, 20 детей, представляющих «AB», «AC», «AT» и т.д .; и так далее, вплоть до детей, представляющих, например, «ABALXYZQ», где каждый дочерний элемент содержит целочисленное поле, представляющее счет, то есть, сколько раз встречается эта подстрока?

class AdamTree {
    char ch;
    List<AdamTree> children;
    int count;
}

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

0 голосов
/ 22 февраля 2012

Кажется, есть некоторая путаница в отношении ваших требований и целей.

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

Если вы на самом деле храните объекты String и ваши строкина самом деле текст читается человеком, вы можете попробовать дефлировать их с помощью java.util.zip после того, как вы закончите поиск, индексирование и все такое.Если вы действительно хотите сохранить их крошечными, и вы не получите реальных String объектов, и вы сказали, что у вас есть только 26 различных букв, вы можете сжать их в группы по 5 битов и сохранить их таким образом,Для этого используйте интерфейс CharSequence.

0 голосов
/ 22 февраля 2012

Вы можете попробовать код на Java ниже

int[] data = new int[254];//we have 254 different characters 
void processData(String mString){

    for (int i=0 ; i< mString.length;i++){
       char c = mString.charAt(i); 
        data[c]++;
    }
}
int getCountOfChar(char c){
     return data[c];
}
0 голосов
/ 22 февраля 2012

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

Таким образом, это даст что-то вроде этого:

A:     0                  1                   3           ...
       |               /     \              /    \
B:     0             0        1           1        3
      / \          heaven   /   \     barracuda    ababab
C:   0   1                 0     1
   foo   cow             bar     bac

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

Возможно, вы могли бы оптимизировать его, обрезая длинные ветви без братьев и сестер

0 голосов
/ 22 февраля 2012

http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm Посмотрите на алгоритм KMP.Это довольно распространенная проблема программирования.Выше вы найдете одно из самых быстрых возможных решений.Легко понять и реализовать.

Подсчитайте вхождения с помощью KMP, затем либо выполните сортировку слиянием после вставки, либо, если вы знаете, что массив / etc отсортирован, выполните бинарный поиск / вставку направления.

0 голосов
/ 13 февраля 2012

Я могу помочь с php / псевдокодом и хэш-картами или ассоциативными массивами.

$hash="";

$string = "ACAAGATGCCATTGTCCCCCGGCCTCCTGCTGCTGCTGCTCTCCGGGGCCACGGCCACCGCTGCCCTGCC"
while ( read each $char from $string ) {

  if ( isset($hash[$char]) ) { 
      $hash[$char] = $hash[$char]+1 
  } else {
      $hash[$char]=1
  }
}

в конце у вас будет ассоциативный массив с 1 найденным ключом / символом и в хеш-значении вы будетеиметь количество случаев

Это не PHP (или любой другой язык в этом отношении), но принцип должен помочь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...