#delphi #list #intersection
#delphi #Список #пересечение
Вопрос:
Я хочу вычислить список «пересечение». Проблема в том:
L1 = [1, 0, 2, 3, 1 , 3, 0, 5]
L2 = [3, 5]
Тогда результатом будет
L3 = [0, 0, 0, 1, 0, 1, 0, 1]
Затем я преобразую этот результат в байт. В этом случае будет 21 в десятичном формате.
Я хочу создать на delphi, и мне нужно, чтобы это делалось эффективно. Есть ли способ решить эту проблему лучше, чем O (m * n)?
Комментарии:
1. Если результат всегда будет умещаться в байт, то мы можем предположить
m = 8
, верно? Существуют ли верхние границы для значений, хранящихся в L2? (В частности, всегда ли они могут быть в диапазоне от 0 до 255 включительно?)2. Я также не понимаю, что вы подразумеваете под пересечением. С вашими данными я предполагаю, что L3 = [0,0,0,1,0,1,0,0]. Вы допустили ошибку?
3. Моя структура представляет собой массив байт. L1 с N элементами и L2 с M элементами. Пересечение будет равно O (M * N).
4. Если я найду элемент L2 в L1, выходной элемент будет равен 1, иначе 0. Я нахожу элемент ‘3’ или ‘5’ в позиции, представленной символом L3. Извините за мой плохой английский.
5. Вы можете сначала отсортировать второй (более короткий) массив, чтобы реализовать алгоритм «пересечения» быстрее, чем N * M
Ответ №1:
Вот функция, которая должна делать то, что вы хотите. Я определил L2
как набор вместо массива, поскольку вы сказали, что все ваши значения будут помещаться в Byte
. Его сложность равна O (n); проверка принадлежности к множеству выполняется в постоянное время. Но поскольку результат должен умещаться в байт, длина L1
должна быть ограничена 8, поэтому сложность этой функции на самом деле равна O (1).
function ArrayMembersInSet(const L1: array of Byte; const L2: set of Byte): Byte;
var
i: Integer;
b: Byte;
begin
Assert(Length(L1) <= 8,
'List is to long to fit in result');
Result := 0;
for i := 0 to High(L1) do begin
b := L1[i];
if b in L2 then
Result := Result or (1 shl (7 - i));
end;
end;
Комментарии:
1. Я попробую этот код! Спасибо! Вы знаете, является ли оператор В O (N)?
2.
in
является ли O (1), как указано в ответе, «проверка принадлежности к набору выполняется в постоянное время»3. вы также можете написать свой цикл for с 0, поскольку ваша операция сдвига предполагает это. Вы могли бы утверждать, что и что массив содержит не более 8 элементов.
4. Мне любопытно. Как работает оператор В работе? Использует ли он хэш для выполнения операции в O (1)?
5. Это работает путем выполнения побитовых операций. Набор — это внутренний массив размером до 32 байт. Программа использует величину левого операнда, чтобы решить, какой байт проверять, а затем выполняет простую операцию «и».
Ответ №2:
Ответ Роба будет работать для этого конкретного случая. Для более общего случая, когда необходимо сравнить два списка, вы можете сделать это за O (m n) времени, если оба списка отсортированы. (Или O (n log n) время, если вам нужно сначала отсортировать их, но это все равно намного быстрее, чем O (m * n).)
Базовый алгоритм сравнения списков выглядит следующим образом:
procedure ListCompare(list1, list2: TWhateverList; [Add extra params here]);
var
i, j: integer;
begin
i := 0;
j := 0;
while (i < list1.Count) and (j < list2.Count) do
begin
if list1[i] < list2[j] then
begin
//handle this appropriately
inc(i);
end
else if list1[i] > list2[j] then
begin
//handle this appropriately
inc(j);
end
else //both elements are equal
begin
//handle this appropriately
inc(i);
inc(j);
end;
end;
//optional cleanup, if needed:
while (i < list1.Count) do
begin
//handle this appropriately
inc(i);
end;
while (j < list2.Count) do
begin
//handle this appropriately
inc(j);
end;
end;
Это можно настроить для целого ряда задач, включая пересечение списков, изменив места «обрабатывать это соответствующим образом», и гарантированно не будет выполняться больше шагов, чем указано в обоих списках вместе взятых. Для пересечения списков, пусть регистр equals добавит значение к некоторым выводам, а два других ничего не сделают, кроме увеличения количества счетчиков, и вы можете отказаться от необязательной очистки.
Один из способов использования этого алгоритма — превратить дополнительные параметры вверху в указатели на функции и передать в подпрограммы, которые будут обрабатывать соответствующие случаи, или nil, чтобы ничего не делать. (Просто убедитесь, что вы проверяете наличие nil перед их вызовом, если вы идете по этому маршруту!) Таким образом, вам нужно будет написать базовый код только один раз.
Ответ №3:
Ну, несмотря ни на что, вам нужно будет посетить каждый элемент в каждом списке, сравнивая значения. Вложенный цикл выполнил бы это за O (n ^ 2), и преобразование должно быть просто локальной работой.
РЕДАКТИРОВАТЬ: я заметил, что вы хотите лучшую среду выполнения, чем O (n * m).
Комментарии:
1. Почему вам нужно делать это во вложенном цикле?
2. Как еще вы могли бы сравнить каждый элемент в каждом списке.
3. Ответ, который равен O (1), использует НАБОР и МАССИВ, а не СПИСКИ.