К сожалению, вы еще не обновили свой вопрос, поэтому до сих пор не совсем ясно, о чем вы спрашиваете. Например. вы говорите о пересечении (которое должно искать значения, которые существуют в каждом отдельном массиве), но из (не работающего) кода кажется, что вы просто ищете дубликаты в любом из массивов.
Хотя ответ Мэйсона указывает на очевидное общее решение для такого рода алгоритмов, я полагаю, что оно несколько отличается для такого многомерного массива. Я разработал две процедуры для определения (1) пересечения, а также (2) дубликатов. Оба предполагают неупорядоченное содержание в массивах неравной длины.
Сначала я решил ввести несколько новых типов:
type
PChain = ^TChain;
TChain = array of Integer;
TChains = array of TChain;
Во-вторых, обеим подпрограммам нужен какой-то механизм сортировки. Очень быстрый, но грязный - это использование / 1010 *:
function CompareInteger(Item1, Item2: Pointer): Integer;
begin
Result := Integer(Item1) - Integer(Item2);
end;
procedure SortChain(var Chain: TChain);
var
List: TList;
begin
List := TList.Create;
try
List.Count := Length(Chain);
Move(Chain[0], List.List[0], List.Count * SizeOf(Integer));
List.Sort(CompareInteger);
Move(List.List[0], Chain[0], List.Count * SizeOf(Integer));
finally
List.Free;
end;
end;
Но гораздо лучшая реализация достигается путем настройки RTL-кода с Classes.QuickSort
, что в точности совпадает с приведенным выше, без копирования массива (дважды):
procedure SortChain(Chain: PChain; L, R: Integer);
var
I: Integer;
J: Integer;
Value: Integer;
Temp: Integer;
begin
repeat
I := L;
J := R;
Value := Chain^[(L + R) shr 1];
repeat
while Chain^[I] < Value do
Inc(I);
while Chain^[J] > Value do
Dec(J);
if I <= J then
begin
Temp := Chain^[I];
Chain^[I] := Chain^[J];
Chain^[J] := Temp;
Inc(I);
Dec(J);
end;
until I > J;
if L < J then
SortChain(Chain, L, J);
L := I;
until I >= R;
end;
Пересечения:
Чтобы получить пересечение всех массивов, достаточно сравнить все значения в самом коротком массиве со значениями во всех других массивах. Поскольку самый короткий массив может содержать повторяющиеся значения, этот небольшой массив сортируется, чтобы можно было игнорировать дубликаты. С этого момента это просто вопрос нахождения (или, вернее, не нахождения) того же значения в одном из других массивов. Сортировка всех других массивов не требуется, поскольку вероятность найти значение в более ранней позиции, чем в отсортированном массиве, составляет 50%.
function GetChainsIntersection(const Chains: TChains): TChain;
var
IShortest: Integer;
I: Integer;
J: Integer;
K: Integer;
Value: Integer;
Found: Boolean;
FindCount: Integer;
begin
// Determine which of the chains is the shortest
IShortest := 0;
for I := 1 to Length(Chains) - 1 do
if Length(Chains[I]) < Length(Chains[IShortest]) then
IShortest := I;
// The length of result will at maximum be the length of the shortest chain
SetLength(Result, Length(Chains[IShortest]));
Value := 0;
FindCount := 0;
// Find for every value in the shortest chain...
SortChain(@Chains[IShortest], 0, Length(Chains[IShortest]) - 1);
for K := 0 to Length(Chains[IShortest]) - 1 do
begin
if (K > 0) and (Chains[IShortest, K] = Value) then
Continue;
Value := Chains[IShortest, K];
Found := False;
for I := 0 to Length(Chains) - 1 do
if I <> IShortest then
begin
Found := False;
for J := 0 to Length(Chains[I]) - 1 do
// ... the same value in other chains
if Chains[I, J] = Value then
begin
Found := True;
Break;
end;
if not Found then
Break;
end;
// Add a found value to the result
if Found then
begin
Result[FindCount] := Value;
Inc(FindCount);
end;
end;
// Truncate the length of result to the actual number of found values
SetLength(Result, FindCount);
end;
Дубликаты:
Это также не требует сортировки всех массивов по отдельности. Все значения копируются в одномерный временный массив. После сортировки массива легко найти дубликаты.
function GetDuplicateShackles(const Chains: TChains): TChain;
var
Count: Integer;
I: Integer;
Temp: TChain;
PrevValue: Integer;
begin
// Foresee no result
SetLength(Result, 0);
// Count the total number of values
Count := 0;
for I := 0 to Length(Chains) - 1 do
Inc(Count, Length(Chains[I]));
if Count > 0 then
begin
// Copy all values to a temporary chain...
SetLength(Temp, Count);
Count := 0;
for I := 0 to Length(Chains) - 1 do
begin
Move(Chains[I][0], Temp[Count], Length(Chains[I]) * SizeOf(Integer));
Inc(Count, Length(Chains[I]));
end;
// Sort the temporary chain
SortChain(@Temp, 0, Count - 1);
// Find all duplicate values in the temporary chain
SetLength(Result, Count);
Count := 0;
PrevValue := Temp[0];
for I := 1 to Length(Temp) - 1 do
begin
if (Temp[I] = PrevValue) and
((Count = 0) or (Temp[I] <> Result[Count - 1])) then
begin
Result[Count] := PrevValue;
Inc(Count);
end;
PrevValue := Temp[I];
end;
SetLength(Result, Count);
end;
end;
Пример приложения:
И поскольку мне нравится тестировать весь мой код, для его представления потребовалось очень мало работы.
unit Unit1;
interface
uses
SysUtils, Classes, Controls, Forms, StdCtrls, Grids;
type
PChain = ^TChain;
TChain = array of Integer;
TChains = array of TChain;
TForm1 = class(TForm)
Grid: TStringGrid;
IntersectionFullButton: TButton;
IntersectionPartialButton: TButton;
DuplicatesFullButton: TButton;
DuplicatesPartialButton: TButton;
Memo: TMemo;
procedure FormCreate(Sender: TObject);
procedure IntersectionButtonClick(Sender: TObject);
procedure DuplicatesButtonClick(Sender: TObject);
private
procedure ClearGrid;
procedure ShowChains(const Chains: TChains);
procedure ShowChain(const Chain: TChain; const Title: String);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
MaxDepth = 20;
procedure FillChains(var Chains: TChains; FillUp: Boolean; MaxValue: Integer);
var
X: Integer;
Y: Integer;
Depth: Integer;
begin
SetLength(Chains, MaxDepth);
for X := 0 to MaxDepth - 1 do
begin
if FillUp then
Depth := MaxDepth
else
Depth := Random(MaxDepth - 2) + 3; // Minimum depth = 3
SetLength(Chains[X], Depth);
for Y := 0 to Depth - 1 do
Chains[X, Y] := Random(MaxValue);
end;
end;
procedure SortChain(Chain: PChain; L, R: Integer);
var
I: Integer;
J: Integer;
Value: Integer;
Temp: Integer;
begin
repeat
I := L;
J := R;
Value := Chain^[(L + R) shr 1];
repeat
while Chain^[I] < Value do
Inc(I);
while Chain^[J] > Value do
Dec(J);
if I <= J then
begin
Temp := Chain^[I];
Chain^[I] := Chain^[J];
Chain^[J] := Temp;
Inc(I);
Dec(J);
end;
until I > J;
if L < J then
SortChain(Chain, L, J);
L := I;
until I >= R;
end;
function GetChainsIntersection(const Chains: TChains): TChain;
var
IShortest: Integer;
I: Integer;
J: Integer;
K: Integer;
Value: Integer;
Found: Boolean;
FindCount: Integer;
begin
IShortest := 0;
for I := 1 to Length(Chains) - 1 do
if Length(Chains[I]) < Length(Chains[IShortest]) then
IShortest := I;
SetLength(Result, Length(Chains[IShortest]));
Value := 0;
FindCount := 0;
SortChain(@Chains[IShortest], 0, Length(Chains[IShortest]) - 1);
for K := 0 to Length(Chains[IShortest]) - 1 do
begin
if (K > 0) and (Chains[IShortest, K] = Value) then
Continue;
Value := Chains[IShortest, K];
Found := False;
for I := 0 to Length(Chains) - 1 do
if I <> IShortest then
begin
Found := False;
for J := 0 to Length(Chains[I]) - 1 do
if Chains[I, J] = Value then
begin
Found := True;
Break;
end;
if not Found then
Break;
end;
if Found then
begin
Result[FindCount] := Value;
Inc(FindCount);
end;
end;
SetLength(Result, FindCount);
end;
function GetDuplicateShackles(const Chains: TChains): TChain;
var
Count: Integer;
I: Integer;
Temp: TChain;
PrevValue: Integer;
begin
SetLength(Result, 0);
Count := 0;
for I := 0 to Length(Chains) - 1 do
Inc(Count, Length(Chains[I]));
if Count > 0 then
begin
SetLength(Temp, Count);
Count := 0;
for I := 0 to Length(Chains) - 1 do
begin
Move(Chains[I][0], Temp[Count], Length(Chains[I]) * SizeOf(Integer));
Inc(Count, Length(Chains[I]));
end;
SortChain(@Temp, 0, Count - 1);
SetLength(Result, Count);
Count := 0;
PrevValue := Temp[0];
for I := 1 to Length(Temp) - 1 do
begin
if (Temp[I] = PrevValue) and
((Count = 0) or (Temp[I] <> Result[Count - 1])) then
begin
Result[Count] := PrevValue;
Inc(Count);
end;
PrevValue := Temp[I];
end;
SetLength(Result, Count);
end;
end;
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
Grid.ColCount := MaxDepth;
Grid.RowCount := MaxDepth;
end;
procedure TForm1.ClearGrid;
var
I: Integer;
begin
for I := 0 to Grid.ColCount - 1 do
Grid.Cols[I].Text := '';
end;
procedure TForm1.ShowChains(const Chains: TChains);
var
I: Integer;
J: Integer;
begin
for I := 0 to Length(Chains) - 1 do
for J := 0 to Length(Chains[I]) - 1 do
Grid.Cells[I, J] := IntToStr(Chains[I, J]);
end;
procedure TForm1.ShowChain(const Chain: TChain; const Title: String);
var
I: Integer;
begin
if Length(Chain) = 0 then
Memo.Lines.Add('No ' + Title)
else
begin
Memo.Lines.Add(Title + ':');
for I := 0 to Length(Chain) - 1 do
Memo.Lines.Add(IntToStr(Chain[I]));
end;
end;
procedure TForm1.IntersectionButtonClick(Sender: TObject);
var
FillUp: Boolean;
Chains: TChains;
Chain: TChain;
begin
ClearGrid;
Memo.Clear;
FillUp := Sender = IntersectionFullButton;
if FillUp then
FillChains(Chains, True, 8)
else
FillChains(Chains, False, 4);
ShowChains(Chains);
Chain := GetChainsIntersection(Chains);
ShowChain(Chain, 'Intersection');
end;
procedure TForm1.DuplicatesButtonClick(Sender: TObject);
var
Chains: TChains;
Chain: TChain;
begin
ClearGrid;
Memo.Clear;
FillChains(Chains, Sender = DuplicatesFullButton, 900);
ShowChains(Chains);
Chain := GetDuplicateShackles(Chains);
ShowChain(Chain, 'Duplicates');
end;
initialization
Randomize;
end.
Unit1.DFM:
object Form1: TForm1
Left = 343
Top = 429
Width = 822
Height = 459
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
DesignSize = (
806
423)
PixelsPerInch = 96
TextHeight = 13
object Memo: TMemo
Left = 511
Top = 63
Width = 295
Height = 360
Anchors = [akLeft, akTop, akRight, akBottom]
ScrollBars = ssVertical
TabOrder = 5
end
object IntersectionFullButton: TButton
Left = 511
Top = 7
Width = 141
Height = 25
Caption = 'Intersection (full chains)'
TabOrder = 1
OnClick = IntersectionButtonClick
end
object Grid: TStringGrid
Left = 0
Top = 0
Width = 503
Height = 423
Align = alLeft
ColCount = 20
DefaultColWidth = 24
DefaultRowHeight = 20
FixedCols = 0
RowCount = 20
FixedRows = 0
TabOrder = 0
end
object DuplicatesFullButton: TButton
Left = 658
Top = 7
Width = 141
Height = 25
Caption = 'Duplicates (full chains)'
TabOrder = 3
OnClick = DuplicatesButtonClick
end
object IntersectionPartialButton: TButton
Left = 511
Top = 35
Width = 141
Height = 25
Caption = 'Intersection (partial chains)'
TabOrder = 2
OnClick = IntersectionButtonClick
end
object DuplicatesPartialButton: TButton
Left = 658
Top = 35
Width = 141
Height = 25
Caption = 'Duplicates (partial chains)'
TabOrder = 4
OnClick = DuplicatesButtonClick
end
end