Сравнение списков

Я использую этот вопрос в интервью и задаюсь вопросом, какое решение лучше всего.

Напишите подгруппу Perl, которая принимает списки п, а затем возвращает 2 списка ^ п-1, сообщающих вам, какие элементы в каких списках; то есть, какие элементы находятся только в первом списке, втором, списке, первом и втором списке и во всех других комбинациях списков. Предположим, что п достаточно мало (меньше 20).

Например:

list_compare([1, 3], [2, 3]);
  => ([1], [2], [3]);

Здесь первый список результатов дает все элементы, которые есть только в списке 1, второй список результатов дает все элементы, которые есть только в списке 2, а третий список результатов дает все элементы, которые находятся в обоих списках.

list_compare([1, 3, 5, 7], [2, 3, 6, 7], [4, 5, 6, 7])
  => ([1], [2], [3], [4], [5], [6], [7])

Здесь первый список содержит все элементы, которые есть только в списке 1, второй список содержит все элементы, которые есть только в списке 2, а третий список содержит все элементы, которые находятся в обоих списках 1 и 2, как в первом примере. Четвертый список содержит все элементы, которые есть только в списке 3, пятый список содержит все элементы, которые есть только в списках 1 и 3, шестой список содержит все элементы, которые есть только в списках 2 и 3, а седьмой список дает все элементы. которые есть во всех 3 списках.

Я обычно задаю эту проблему как продолжение подмножества этой проблемы для п = 2.

Каково решение?

Последующие действия: элементы в списках представляют собой строки. Могут быть дубликаты, но поскольку это просто строки, дубликаты должны быть сжаты на выходе. Порядок элементов в выходных списках не имеет значения, имеет значение порядок самих списков.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
600
3

Ответы 3

Вот мое решение:

Создайте хэш, ключи которого представляют собой объединение всех элементов во входных списках, а значения представляют собой битовые строки, где бит я устанавливается, если элемент присутствует в списке я. Битовые строки строятся с использованием побитового или. Затем создайте выходные списки, перебирая ключи хэша, добавляя ключи в связанный выходной список.

sub list_compare {
    my (@lists) = @_;
    my %compare;
    my $bit = 1;
    foreach my $list (@lists) {
        $compare{$_} |= $bit foreach @$list;
        $bit *= 2; # shift over one bit
    }


    my @output_lists;
    foreach my $item (keys %compare) {
        push @{ $output_lists[ $compare{$item} - 1 ] }, $item;
    }

    return \@output_lists;

}

Обновлено, чтобы включить создание перевернутого списка вывода, предложенное Аристотелем.

Прежде всего, хочу отметить, что ответ nohat просто не работает. Попробуйте запустить его и посмотрите на вывод в Data :: Dumper, чтобы убедиться в этом.

Тем не менее, ваш вопрос не совсем корректный. Похоже, вы используете наборы как массивы. Как вы хотите обрабатывать дубликаты? Как вы хотите обрабатывать сложные структуры данных? В каком порядке вы хотите расположить элементы? Для простоты я предполагаю, что ответы - это дубликаты сквоша, можно структурировать сложные структуры данных, и порядок не имеет значения. В этом случае вполне адекватным ответом будет следующий:

sub list_compare {
  my @lists = @_;

  my @answers;
  for my $list (@lists) {
    my %in_list = map {$_=>1} @$list;
    # We have this list.
    my @more_answers = [keys %in_list];
    for my $answer (@answers) {
      push @more_answers, [grep $in_list{$_}, @$answer];
    }
    push @answers, @more_answers;
  }

  return @answers;
}

Если вы хотите скорректировать эти предположения, вам нужно будет скорректировать код. Например, не раздавить сложные структуры данных и не раздавить дубликаты можно с помощью:

sub list_compare {
  my @lists = @_;

  my @answers;
  for my $list (@lists) {
    my %in_list = map {$_=>1} @$list;
    # We have this list.
    my @more_answers = [@$list];
    for my $answer (@answers) {
      push @more_answers, [grep $in_list{$_}, @$answer];
    }
    push @answers, @more_answers;
  }

  return @answers;
}

Тем не менее, это использование строковой модификации структуры данных для проверки того, существуют ли вещи, существующие в одной, в другой. Чтобы расслабиться в этом состоянии, потребуется немного больше усилий.

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

nohat 16.09.2008 21:34

Ваше данное решение можно еще немного упростить.

В первом цикле вы можете использовать простое сложение, поскольку вы всегда выполняете операцию ИЛИ только с отдельными битами, и вы можете сузить область действия $bit, перебирая индексы. Во втором цикле вы можете вычесть 1 из индекса вместо создания ненужного 0-го элемента выходного списка, который необходимо отключить shift, и где вы без необходимости повторяете m * n раз (где m - количество выходных списков, а n - количество уникальных элементов), итерация по уникальным элементам уменьшит количество итераций до n (что является значительным преимуществом в типичных случаях использования, когда m намного больше, чем n), и упростит код.

sub list_compare {
    my ( @list ) = @_;
    my %dest;

    for my $i ( 0 .. $#list ) {
        my $bit = 2**$i;
        $dest{$_} += $bit for @{ $list[ $i ] };
    }

    my @output_list;

    for my $val ( keys %dest ) {
        push @{ $output_list[ $dest{ $val } - 1 ] }, $val;
    }

    return \@output_list;
}

Также обратите внимание, что, если подумать таким образом, процесс сбора результатов может быть очень кратко записан с помощью модуля Список :: Часть:

use List::Part;

sub list_compare {
    my ( @list ) = @_;
    my %dest;

    for my $i ( 0 .. $#list ) {
        my $bit = 2**$i;
        $dest{$_} += $bit for @{ $list[ $i ] };
    }

    return [ part { $dest{ $_ } - 1 } keys %dest ];
}

Но учтите, что list_compare - ужасное имя. Что-то вроде part_elems_by_membership было бы намного лучше. Кроме того, необходимо исправить неточности в вашем вопросе, на которые указал Бен Тилли.

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

nohat 16.09.2008 21:37

Другие вопросы по теме