Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > Сортировка массива


Автор: Keeper89 4.2.2010, 17:07
Доброго времени суток.

Имеется динамический массив целых чисел достаточно большой размерности (например, 100 столбцов х 100 строк).
Как грамотнее реализовать сортировку по выбранному столбцу по убыванию или возрастанию?

Автор: ~FoX~ 4.2.2010, 17:17
Keeper89, Ну на таких величинах прекрасно работает быстрый сорт (быстродействие O(n log n)) ... http://algolist.ru/sort/quick_sort.php

Автор: Keeper89 4.2.2010, 21:02
~FoX~, а что если использовать встроенные классы типа TList или TObjectList? В них уже есть реализация быстрой сортировки. 

Автор: Keeper89 5.2.2010, 01:00
Да, и получается нужно во внутреннем цикле соответственно менять элементы столбцов, которые не сортируются?

Автор: ~FoX~ 5.2.2010, 08:11
Цитата(Keeper89 @  4.2.2010,  22:02 Найти цитируемый пост)
~FoX~, а что если использовать встроенные классы типа TList или TObjectList? В них уже есть реализация быстрой сортировки.

На счет Tlist и TObjectList не припомню что бы там сортировка хоть какая то была, А вот TStringList именно быстрым сортится... Только критерий там один - по первому символу.... Т.е. в твоем случае только по первому столбцу
Цитата(Keeper89 @  5.2.2010,  02:00 Найти цитируемый пост)
Да, и получается нужно во внутреннем цикле соответственно менять элементы столбцов, которые не сортируются? 

Придется, но это не накладно... Ну относительно алгоритма сортировки по крайней мере  smile 

Автор: Frees 5.2.2010, 08:44
Цитата(~FoX~ @  5.2.2010,  11:11 Найти цитируемый пост)
Только критерий там один - по первому символу.... Т.е. в твоем случае только по первому столбцу

Функцию сравнения можно свою написать и сортировать как захочешь..

Автор: artsb 5.2.2010, 09:05
Цитата(Keeper89 @  5.2.2010,  01:00 Найти цитируемый пост)
Да, и получается нужно во внутреннем цикле соответственно менять элементы столбцов, которые не сортируются? 

Можно сделать так:
Загнать первый столбец, например в TStringList.
Написать свою функцию сравнения, которая возвращает -1 если нужно поменять местами объекты и 1 или 0 в противном случае.
Помимо этого, сделать проверку: если возвращается -1, тогда "вручную" поменять местами элементы всех столбцов.
Конечно, нельзя сказать, что всё это будет работать очень быстро. Но зато удовлетворяет твоим требованиям smile
Код

// внутри функции сортировки
 if  lst->Strings[iFirst][1] > lst->Strings[iSecond][1] then // сравнение чисто условное
  Result := 1;
 else if lst->Strings[iFirst][1] < lst->Strings[iSecond][1] then
  Result := -1;
 else
  Result := 0;
 
 if Result = -1 then
  begin
   // меняем местами элементы массива
  end;

Автор: bems 5.2.2010, 12:19
Код

 uses Generics.Collections, Generics.Defaults;

 type TIntArr = array of Integer;
      TInt2DArr = array of TIntArr;

 procedure Sort(var Data: TInt2DArr; Index: Integer);
 begin
 TArray.Sort<TIntArr>(Data, TComparer<TIntArr>.Construct(
   function(const Left, Right: TIntArr): Integer
   begin
   Result := Left[Index] - Right[Index]
   end));
 end;

Автор: artsb 5.2.2010, 12:39
Keeper89, прикрепляю пример. Посмотри и скажи, правильно ли я тебя понял.

Автор: Keeper89 5.2.2010, 15:58
artsb, абсолютно правильно.

bems, спасибо, решение с дженериками - супер! smile
Единственное добавил еще одну вещь:
Код

type
  TSortType = (stAscending, stDescending);

procedure Sort(var Data: TMatrix; Index: Integer; SortType: TSortType);
begin
  TArray.Sort<TIntArr>(Data,
    TComparer<TIntArr>.Construct(
      function(const Left, Right: TIntArr): Integer
      begin
        Result := Left[Index] - Right[Index];
        if SortType = stDescending then
          Result := -Result;
      end)
    );
end;

Автор: artsb 5.2.2010, 16:08
Цитата(Keeper89 @  5.2.2010,  15:58 Найти цитируемый пост)
решение с дженериками - супер!

А этот вариант случаем не сортирует всё подряд? Вроде как нужно было по конкретному столбцу. Просто я в Делфи не силён...

Добавлено через 2 минуты и 51 секунду
Всё. Кажись понял smile

Автор: artsb 5.2.2010, 22:30
Выкладываю своё решение (правда на С++). Мало ли, кому-нибудь пригодится.
Выкладываю на С++, т.к. плохо знаком с Делфи. Хотя и могу перевести код и он даже будет работать, но скорее всего, будет не эффективен. И к тому же, могут быть скрытые ошибки.

Код

// некий массив 10х10
int mas[10][10] = {{9,5,6,2,8,1,0,5,4,1},
                   {8,3,1,6,5,7,2,8,0,2},
                   {7,8,1,0,4,7,2,9,4,6},
                   {6,5,6,2,2,4,8,6,0,4},
                   {5,1,6,3,7,8,2,5,3,0},
                   {4,7,7,5,2,7,1,0,8,5},
                   {3,4,7,2,8,1,3,5,3,8},
                   {2,1,3,7,2,9,4,7,3,0},
                   {1,4,4,7,2,8,4,6,6,9},
                   {0,5,2,6,1,7,3,8,3,7}};

// функция сортировки
// mas   - указатель на массив
// iCols - количество столбцов в массиве
// iCol  - индекс столбца, по которому нужно произвести сортировку
// L     - левый индекс сортируемого участка
// R     - правый индекс сортируемого участка
void _QuickSort(int *mas, const int iCols, const int iCol, int L, int R) {
  int I, J, P, T;

  do {
    I = L;
    J = R;
    P = mas[((L + R) >> 1)*iCols+iCol];
    do {
      while(mas[I*iCols+iCol] < P)
        ++I;
      while(mas[J*iCols+iCol] > P)
        --J;
      switch(I<=J?(I<J?0:1):-1) {
       case 0:
        for(register int i=0; i<iCols; ++i) {
          T = mas[I*iCols+i];
          mas[I*iCols+i] = mas[J*iCols+i];
          mas[J*iCols+i] = T;
        }
       case 1:
        ++I;
        --J;
      }
    } while(I < J);
    if(L < J)
      _QuickSort(mas, iCols, iCol, L, J);
    L = I;
  } while(I < R);
}

// использование:
_QuickSort(*mas, 10, 0, 0, 9);

Алгоритм стащил из реализации в classes.pas smile
Объясню почему я написал так:
Код

      switch(I<=J?(I<J?0:1):-1) {
       case 0:
        for(register int i=0; i<iCols; ++i) {
          T = mas[I*iCols+i];
          mas[I*iCols+i] = mas[J*iCols+i];
          mas[J*iCols+i] = T;
        }
       case 1:
        ++I;
        --J;
      }

, а не оставил так как это было реализовано в classes.pas:
Дело в том, что там было написано приблизительно так:
Код

      if(I<=J) {
        for(register int i=0; i<iCols; ++i) {
          T = mas[I*iCols+i];
          mas[I*iCols+i] = mas[J*iCols+i];
          mas[J*iCols+i] = T;
        }
        ++I;
        --J;
      }

Здесь получается, что когда индексы совпадают, т.е. I==J происходит перестановка элементов с этими индексами, но это бессмысленно как вы понимаете smile (пустая трата времени)

ЗЫ профи поправьте меня если что  smile 

Автор: bems 5.2.2010, 23:25
Keeper89, попросить пояснений ты мог бы прямо в этой теме. Итак:

Класс Generics.Collections.TArray умеет искать двоичным поиском и сортировать методом быстрой сортировки. Это объеденино в класс, потому что невозможно создать обобщенную функцию, не член класса/объекта. Это ограничение языка. Все методы это методы класса, поэтому не нужно создавать экземпляр TArray.

Метод Generics.Collections.TArray.Sort<T> выполняет быструю сортировку массива элементов типа, указанного в Т. 
Код

    class procedure Sort<T>(var Values: array of T); overload;
    class procedure Sort<T>(var Values: array of T; 
      const Comparer: IComparer<T>); overload;
    class procedure Sort<T>(var Values: array of T; 
      const Comparer: IComparer<T>; Index, Count: Integer); overload;

Values - собственно массив. В моем примере я объявляю типы
Код

type TIntArr = array of Integer;
     TInt2DArr = array of TIntArr;

Это нужно только для того чтобы объявление параметра Data не воспринималось как открытый массив, это приводит к несоответствию типов. Это ограничение данного конкретного случая, а не класса TArray или дженериков в целом. Я имею в виду, что в T можно прямо указать array of Integer не объявляя промежуточного типа.
Если Index и Count не указаны, то сортируется весь массив.
Comparer, это объект, реализующий интерфейс IComparer. Если не указан, то используется компаратор по умолчанию. Я еще не вник, что же это за сравнение по умолчанию для разных типов, но случаи с целыми числами и строками очевидны. 

Класс Generics.Defaults.TComparer<T> это абстактный компаратор двух величин типа, указанного в T. Его метод Compare абстрактен, поэтому не стоит напрямую создавать экземпляры этого класса.

Метод Generics.Defaults.TComparer<T>.Construct создает объект класса, унаследованного от TComparer<T>, а значит также поддерживающего интерфейс IComparer<T> Реально создается экземпляр TDelegatedComparer<T>, у которого  метод Compare уже реализован. Эта реализация просто вызывает функцию по ссылке, переданной при вызове Construct.
Анонимные методы это отдельная тема, погугли это сам.
Благодаря тому, что Construct возвращает не сам свежесозданный объект, а его интерфейс, и тому что TComparer<T> унаследован от TInterfacedObject, ты можешь сразу передать интерфейс в TArray.Sort<T>, не заботясь о сохранении ссылки с целью освобождения. Объект будет разрушен автоматически при обнулении счетчика ссылок (в моем примере это происходит сразу после вызова Sort<T>).

Автор: Keeper89 6.2.2010, 03:14
bems, все прояснилось, спасибо!

З.Ы. Я просто думал не засорять тему нужными только мне пояснениями, поэтому отправил ЛС.

Автор: Keeper89 21.2.2010, 16:08
bems, 
Возник вопрос. Возможно ли заменить 
Код

 function(const Left, Right: TIntArr): Integer
      begin
...

на другой тип, например
Код

 function(const Left, Right: TDoubleArr): Real
      begin
...

Или даже не Real, а свой собственный?

Автор: bems 21.2.2010, 17:51
Keeper89, можно, но тогда замени тип и в
TArray.Sort<TDoubleArr> и в
TComparer<TDoubleArr>.Construct

Автор: Keeper89 21.2.2010, 20:06
Сделал так:
Код


  TCoefficient = Double;

  TCoefficients = array of TCoefficient;
  TDoubleCoefficients = array of TCoefficients;  // FTable

   ...

  TArray.Sort<TCoefficients>(FTable,
    TComparer<TCoefficients>.Construct(
      function(const Left, Right: TCoefficients): TCoefficient
      begin
        Result := Left[AColumn - 1] - Right[AColumn - 1];
        if ASortType = stDescending then
          Result := -Result;
      end)
    );

И выскакивает ошибка (в 15 строке, где end):
Цитата

Incompatible types: 'TComparison<uKCommonTypes.TCoefficients>' and 'Procedure'

Автор: bems 21.2.2010, 20:44
Не нужно возвращать TCoefficient.
Независимо от типа сравниваемых величин, возвращается целое. <0 если меньше, 0 если равно, >0 если больше

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)