Вычисление объединения, пересечения и разности уникальных списков

Проблема

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

Решение

В приведенных ниже решениях списки инициализируются следующим образом:
@a = (1, 3, 5, 6, 7, 8);
@b = (2, 3, 5, 7, 9);
@unuon = @isect = @diff = ();
%unuon = %isect = ();
%count = ();
Простое решение для объединения и пересчения
foreach $e (@a) {$inion{$e} = 1;}
foreach $e (@b) {
  if ( $union {$e} ) { $isect{$e} = 1; }
  $union {$e} = 1;
}
@unuon = keys %union;
@isect = keys %isect;
Идиоматическое решение
foreach $e (@a, @b) {$inion{$e}++ && $isect{$e}++;}
@unuon = keys %union;
@isect = keys %isect;
Объединение, пересечение и симметричная разность
foreach $e (@а, @b) { $count{$e}++; }
foreach $e (keys %count) {
  push(@union, $e);
  if ($count{$e} == 2) {
    push @isect, $e;
  } else {
      push @diff, $e;
    }
}
Косвенное решение
@isect = @diff = @union = ();
foreach $e (@a, @b) { $count{$e}++; }
foreach $e (keys %count) {
  push(@union, $e);
  push @{ $count{$e) == 2 ? \@isect : \@diff }, $e;
}

Комментарий

В первом решении происходит непосредственное вычисление объединения и пересечения двух списков, ни один из которых не содержит дубликатов. Для записи элементов, принадлежащих к объединению и пересечению, используются два разных хэша. Сначала мы заносим каждый элемент первого массива в хэш объединения и ассоциируем с ним истинное значение. Затем при последовательной обработке элементов второго массива мы проверяем, присутствует ли элемент в объединении. Если присутствует, он также включается и в хэш пересечения. В любом случае элемент заносится в хэш объединения. После завершения перебора мы извлекаем ключи обоих хэшей. Ассоциированные с ними значения не нужны.
Второе решение («Идиоматическое») в сущности делает то же самое, однако для него потребуется хорошее знание операторов Perl (а также awk, С, C++ и Java) ++ и &&. Если ++ находится после переменной, то ее старое значение используется до приращения. Когда элемент встречается впервые, он еще отсутствует в объединении, поэтому первая часть && будет ложной, а вторая часть попросту игнорируется. Когда тот же элемент встретится во второй раз, он уже присутствует в объединении, поэтому мы заносим его и в пересечение.
В третьем решении использован всего один хэш для хранения информации о том, сколько раз встретился тот или иной элемент. Записав элементы обоих массивов в хэш, мы последовательно перебираем его ключи. Каждый ключ автоматически попадает в объединение. Ключи, с которыми ассоциировано значение 2, присутствуют в обоих массивах и потому заносятся в массив пересечения. Ключи с ассоциированным значением 1 встречаются лишь в одном из двух массивов и заносятся в массив разности. В отличие от исходного решения, порядок элементов в выходных массивах не совпадает с порядком элементов входных массивов.
В последнем решении, как и в предыдущем, используется всего один хэш с количеством экземпляров каждого элемента. Однако на этот раз реализация построена на массиве в блоке @{...}.
Мы вычисляем не простую, а симметричную разность. Эти термины происходят из теории множеств. Симметричная разность представляет собой набор всех элементов, являющихся членами либо @А, либо @В, но не обоих сразу. Простая разность — набор всех элементов @А, отсутствующих в @В.

См. также




2013-09-10 17:05:19

Proverte kod v komentariyah gde pro list tam oshibki detskie




Оставить комментарий:
Ваше Имя:
Email:
Антибот: *  
Ваш комментарий: