List 列表比较
我在采访中使用了这个问题,我想知道最好的解决方案是什么 编写一个获取n个列表的Perl sub,然后返回2^n-1个列表,告诉您哪些项目在哪些列表中;也就是说,哪些项目仅在第一个列表、第二个列表、第一个和第二个列表以及所有其他列表组合中。假设n相当小(小于20) 例如:List 列表比较,list,perl,List,Perl,我在采访中使用了这个问题,我想知道最好的解决方案是什么 编写一个获取n个列表的Perl sub,然后返回2^n-1个列表,告诉您哪些项目在哪些列表中;也就是说,哪些项目仅在第一个列表、第二个列表、第一个和第二个列表以及所有其他列表组合中。假设n相当小(小于20) 例如: list_compare([1, 3], [2, 3]); => ([1], [2], [3]); 这里,第一个结果列表给出了仅在列表1中的所有项目,第二个结果列表给出了仅在列表2中的所有项目,第三个结果列表给出了两
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个列表中的所有项目
我通常把这个问题作为n=2的问题子集的后续问题
解决办法是什么
跟进:列表中的项目是字符串。可能存在重复项,但因为它们只是字符串,所以应该在输出中压缩重复项。输出列表中项目的顺序无关紧要,列表本身的顺序无关紧要。以下是我的解决方案:
构造一个散列,其键是输入列表中所有元素的并集,值是位字符串,如果元素存在于列表i中,则设置位i。位字符串是使用按位or构造的。然后,通过迭代散列的键来构造输出列表,将键添加到关联的输出列表中
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;
}
已更新以包含亚里士多德建议的反向输出列表生成。以下是我的解决方案:
构造一个散列,其键是输入列表中所有元素的并集,值是位字符串,如果元素存在于列表i中,则设置位i。位字符串是使用按位or构造的。然后,通过迭代散列的键来构造输出列表,将键添加到关联的输出列表中
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的答案根本不起作用。尝试运行它,并查看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;
}
然而,这是使用数据结构的字符串化来检查存在于一个数据结构中的事物是否存在于另一个数据结构中。放松这种状况需要做更多的工作。您给出的解决方案可以简化很多 在第一个循环中,您可以使用普通加法,因为您只使用单个位进行ORing,并且您可以通过迭代索引来缩小
$bit
的范围。在第二个循环中,您可以从索引中减去1,而不是生成不必要的第0个输出列表元素,该元素需要shift
ed关闭,并且不必要地重复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;
}
还请注意,一旦以这种方式考虑,结果收集过程可以在模块的帮助下非常简洁地编写:
但是请注意,
list\u compare
是一个可怕的名字。像part\u elems\u by\u membership
这样的东西会更好。此外,Ben Tilly指出的问题中的不精确之处需要纠正。您给出的解决方案仍然可以简化很多
在第一个循环中,您可以使用普通加法,因为您只使用单个位进行ORing,并且您可以通过迭代索引来缩小$bit
的范围。在第二个循环中,您可以从索引中减去1,而不是生成不必要的第0个输出列表元素,该元素需要shift
ed关闭,并且不必要地重复m*n次(其中m是输出列表的数量,n是唯一元素的数量),对唯一元素进行迭代将使迭代次数减少到n次(这在m要大得多的典型用例中是一个显著的胜利)