Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

"Устранение проблемы с установкой свойства boolean при сортировке динамического массива записей в Delphi"

Delphi , Базы данных , Сортировка и Фильтр

Оригинальный заголовок: passing dynamic array of record to mergesort in Delphi

Описание проблемы (вопрос): Мне нужно отсортировать динамический массив записей по полю типа boolean. Я передаю массив в функцию сортировки (merge-sort) и пытаюсь установить значение этого поля в TRUE, но, похоже, это не работает.

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

Код:

type
  TCustomRecord = record
    fLine: AnsiString; // полная строка
    fsubLine: AnsiString; // часть строки
    isDuplicate: boolean; // является ли эта подстрока дубликатом в другой строке
    isRefrence: boolean; // является ли эта строка из файла ссылки или той, которую нужно удалить дубликаты
    fIndex: Cardinal; // исходный порядковый номер строки
  end;
  TCustomRecordArray = array of TCustomRecord;
  PCustomRecord = ^TCustomRecord;

function Merge2(var Vals: array of TCustomRecord): Integer;
var
  AVals: array of TCustomRecord;
  // функция возвращает индекс последнего действительного элемента
  function Merge(I0, I1, J0, J1: Integer): Integer;
  var
    i, j, k, LC: Integer;
  begin
    LC := I1 - I0;
    for i := 0 to LC do
      AVals[i] := Vals[i + I0];
    // копирование нижней половины Vals в временный массив AVals

    k := I0;
    i := 0;
    j := J0;
    while ((i <= LC) and (j <= J1)) do
    begin
      if (AVals[i].fsubLine < Vals[j].fsubLine) then
      begin
        Vals[k] := AVals[i];
        if Vals[k].isRefrence = False then
          Vals[k].isDuplicate := False;
        inc(i);
        inc(k);
      end
      else if (AVals[i].fsubLine > Vals[j].fsubLine) then
      begin
        Vals[k] := Vals[j];
        if Vals[k].isRefrence = False then
          Vals[k].isDuplicate := False;
        inc(k);
        inc(j);
      end
      else
      begin // дубликат
        Vals[k] := AVals[i];
        if Vals[k].isRefrence = False then
          Vals[k].isDuplicate := True;
        inc(i);
        inc(j);
        inc(k);
      end;
    end;

    // копирование остатка
    while i <= LC do
    begin
      Vals[k] := AVals[i];
      inc(i);
      inc(k);
    end;

    if k <> j then
      while j <= J1 do
      begin
        Vals[k] := Vals[j];
        inc(k);
        inc(j);
      end;

    Result := k - 1;
  end;

  // функция возвращает индекс последнего действительного элемента
  function PerformMergeSort(ALo, AHi: Integer): Integer;
  var
    AMid, I1, J1: Integer;
  begin
    // лучше использовать Insertion Sort, когда (AHi - ALo) мало (примерно 32-100)
    if (ALo < AHi) then
    begin
      AMid := (ALo + AHi) shr 1;
      I1 := PerformMergeSort(ALo, AMid);
      J1 := PerformMergeSort(AMid + 1, AHi);
      Result := Merge(ALo, I1, AMid + 1, J1);
    end
    else
      Result := ALo;
  end;

begin
  //SetLength(AVals, Length(Vals) + 1 div 2);
  SetLength(AVals, Length(Vals) div 2 + 1);
  Result := 1 + PerformMergeSort(0, High(Vals));
end;

Вопрос: Как мне эффективно отсортировать этот массив записей, предпочтительно используя merge-sort, и установить значение поля isDuplicate в соответствии с этим?

Обновление: Я добавил тип указателя и выполнил модифицированный merge-sort для массива указателей. Это оказалось очень быстрым способом сортировки массива записей. Я также добавил функцию сравнения, которая добавляла флаги, которые мне были нужны. Единственная часть, которую я не смог сделать, - это добавить флаг для дубликатов на основе того, принадлежат они файлу A или файлу ссылки.

Код:

type
  PCustomRecord = ^TCustomRecord;
  TCustomRecord = record
    fLine: AnsiString; // полная строка
    fsubLine: AnsiString; // часть строки
    isDuplicate: boolean; // является ли эта подстрока дубликатом в другой строке
    isRefrence: boolean; // является ли эта строка из файла ссылки или той, которую нужно удалить дубликаты
    isUnique: boolean; // флаг для установки, если не ссылка и не дубликат
    fIndex: Cardinal; // исходный порядковый номер строки
  end;
  TCustomRecordArray = array of TCustomRecord;
  PCustomRecordList = ^TCustomRecordArray;

// установить фактический массив
// установить массив указателей, указывающих на фактический массив
// отсортировать с помощью merge-sort сперва
// затем вызвать функцию сравнения - это может быть процедура
function Compare(var PRecords: array of PCustomRecord; iLength: int64): Integer;
var
  i: Integer;
begin
  for i := 0 to High(PRecords) do
  begin
    Result := AnsiCompareStr(PRecords[i]^.fsubline, PRecords[i + 1]^.fsubline);
    if Result = 0 then
    begin
      if (PRecords[i].isRefrence = False) then
        PRecords[i].isDuplicate := True
      else if (PRecords[i + 1].isRefrence = False) then
        PRecords[i + 1].isDuplicate := True;
    end;
  end;
end;

procedure MergeSort(var Vals: array of PCustomRecord; ACount: Integer);
var
  AVals: array of PCustomRecord;

  procedure Merge(ALo, AMid, AHi: Integer);
  var
    i, j, k, m: Integer;
  begin
    i := 0;
    for j := ALo to AMid do
    begin
      AVals[i] := Vals[j];
      inc(i);
      // копирование нижней половины Vals в временный массив AVals
    end;

    i := 0; j := AMid + 1; k := ALo; // j может быть неопределенным после цикла for!
    while ((k < j) and (j <= AHi)) do
    if (AVals[i].fsubline <= Vals[j].fsubline) then
    begin
      Vals[k] := AVals[i];
      inc(i); inc(k);
    end
    else if (AVals[i].fsubline > Vals[j].fsubline) then
    begin
      Vals[k] := Vals[j];
      inc(k); inc(j);
    end;
    else
    begin // дубликат
      Vals[k] := AVals[i];
      inc(i); inc(j); inc(k);
    end;

    // найти следующее большее значение в Vals или AVals и скопировать его в правильное место.

    for m := k to j - 1 do
    begin
      Vals[m] := AVals[i];
      inc(i);
    end;
    // скопировать остаток неотсортированных элементов
  end;

  procedure PerformMergeSort(ALo, AHi: Integer);
  var
    AMid: Integer;
  begin
    if (ALo < AHi) then
    begin
      AMid := (ALo + AHi) shr 1;
      PerformMergeSort(ALo, AMid);
      PerformMergeSort(AMid + 1, AHi);
      Merge(ALo, AMid, AHi);
    end;
  end;

begin
  SetLength(AVals, ACount div 2 + 1);
  PerformMergeSort(0, ACount - 1);
end;

Этот подход очень быстрый на небольших файлах, занимает меньше секунды. Удаление дубликатов элементов в массиве, которые несут флаг дубликата и НЕ являются файлом ссылки, является сложной задачей. Как стабильная сортировка, я пытался отсортировать по флагу boolean, но не получил ожидаемых результатов. Я использовал TStringList для просмотра того, правильно ли устанавливаются предыдущие флаги, и это работает идеально. Время возросло с 1 секунды до 6 секунд. Я знаю, что должен быть простой способ установить флаг isUnique без использования TStringList.

Вопрос: Помогите мне создать функцию или процедуру для установки флагов дубликата для повторяющихся элементов с isRefrence = False и isDuplicate = True и isUnique.

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

Ответ:

Проблема заключается в том, что автор пытается изменить значение поля isDuplicate в процессе сортировки, что приводит к неожиданным результатам. Чтобы решить эту проблему, можно отделить процесс сортировки от процесса изменения полей записей. Вот как это можно сделать:

  1. Создайте функцию сравнения, которая будет сравнивать записи по полю fsubLine. Эта функция не будет ничего менять в записях.
  2. Создайте процедуру, которая будет менять значение поля isDuplicate в записях на основе результатов сравнения. Эта процедура не будет ничего сортировать.
  3. Используйте функцию сравнения и процедуру изменения в функции сортировки (например, merge-sort).

Вот пример кода, который демонстрирует этот подход:

type
  TCustomRecord = record
    fLine: AnsiString;
    fsubLine: AnsiString;
    isDuplicate: boolean;
    isRefrence: boolean;
    fIndex: Cardinal;
  end;
  TCustomRecordArray = array of TCustomRecord;
  PCustomRecord = ^TCustomRecord;

function CompareSubstrings(Item1, Item2: PCustomRecord): Integer;
begin
  Result := AnsiCompareStr(Item1^.fsubLine, Item2^.fsubLine);
end;

procedure SetDuplicateFlags(var PRecords: array of PCustomRecord; iLength: int64);
var
  i: Integer;
begin
  for i := 0 to High(PRecords) do
  begin
    if (PRecords[i].isRefrence = False) and (PRecords[i + 1].isRefrence = False) then
    begin
      if CompareSubstrings(PRecords[i], PRecords[i + 1]) = 0 then
        PRecords[i].isDuplicate := True;
    end;
  end;
end;

procedure MergeSort(var Vals: array of PCustomRecord; ACount: Integer);
var
  AVals: array of PCustomRecord;

  procedure Merge(ALo, AMid, AHi: Integer);
  var
    i, j, k, m: Integer;
  begin
    // ... (код функции Merge, как в примере из вопроса)
  end;

  procedure PerformMergeSort(ALo, AHi: Integer);
  var
    AMid: Integer;
  begin
    if (ALo < AHi) then
    begin
      AMid := (ALo + AHi) shr 1;
      PerformMergeSort(ALo, AMid);
      PerformMergeSort(AMid + 1, AHi);
      Merge(ALo, AMid, AHi);
      SetDuplicateFlags(Vals, AHi - ALo + 1);
    end;
  end;

begin
  SetLength(AVals, ACount div 2 + 1);
  PerformMergeSort(0, ACount - 1);
end;

В этой версии кода функция SetDuplicateFlags устанавливает флаг isDuplicate для записей, у которых поле fsubLine имеет одинаковое значение, и только для тех записей, которые не являются файлами ссылками. Функция MergeSort сортирует записи с помощью merge-sort, а затем вызывает SetDuplicateFlags для установки флагов дубликатов. Это разделение процесса сортировки и процесса изменения полей записей позволяет добиться правильного результата.

Создано по материалам из источника по ссылке.

Пользователь хочет отсортировать динамический массив записей по полю типа boolean и установить значение этого поля в TRUE в процессе сортировки с помощью merge-sort в Delphi. Однако, у пользователя возникают проблемы с правильной установкой значения поля


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




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


:: Главная :: Сортировка и Фильтр ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-19 13:29:56
2024-11-21 11:35:34/0.0062739849090576/1