使用 Perl 检查数据数组中重复项的最有效方法是啥?
Posted
技术标签:
【中文标题】使用 Perl 检查数据数组中重复项的最有效方法是啥?【英文标题】:What's the most efficient way to check for duplicates in an array of data using Perl?使用 Perl 检查数据数组中重复项的最有效方法是什么? 【发布时间】:2011-03-02 00:17:17 【问题描述】:我需要查看字符串数组中是否存在重复项,最省时的方法是什么?
【问题讨论】:
【参考方案1】:我喜欢 Perl 的一个原因是它几乎可以像英语一样阅读。这有点道理。
use strict;
use warnings;
my @array = qw/yes no maybe true false false perhaps no/;
my %seen;
foreach my $string (@array)
next unless $seen$string++;
print "'$string' is duplicated.\n";
输出
'false' is duplicated.
'no' is duplicated.
【讨论】:
【参考方案2】:将数组转换为哈希是最快的方法 [O(n)
],尽管它的内存效率很低。使用 for 循环比 grep 快一点,但我不知道为什么。
#!/usr/bin/perl
use strict;
use warnings;
my %count;
my %dups;
for(@array)
$dups$_++ if $count$_++;
一种节省内存的方法是对数组进行适当的排序并遍历它以寻找相等且相邻的条目。
# not exactly sort in place, but Perl does a decent job optimizing it
@array = sort @array;
my $last;
my %dups;
for my $entry (@array)
$dups$entry++ if defined $last and $entry eq $last;
$last = $entry;
这是nlogn
的速度,因为排序,但只需要在%count
中存储重复数据而不是第二个数据副本。最坏情况下的内存使用仍然是O(n)
(当所有内容都重复时),但如果您的数组很大并且没有很多重复项,您将获胜。
抛开理论不谈,基准测试表明后者开始在具有高重复百分比的大型阵列(如超过一百万)上失败。
【讨论】:
感谢您的详尽回答!我现在是用后一种方式做的,但内存不是问题,只是时间,所以我肯定会把它重新实现为哈希。【参考方案3】:如果你无论如何都需要uniquified数组,最快还是用重度优化的库List::MoreUtils,然后和原来的结果对比一下:
use strict;
use warnings;
use List::MoreUtils 'uniq';
my @array = qw(1 1 2 3 fibonacci!);
my @array_uniq = uniq @array;
print ((scalar(@array) == scalar(@array_uniq)) ? "no dupes" : "dupes") . " found!\n";
或者,如果列表很大,并且您想在发现重复条目后立即放弃,请使用哈希:
my %uniq_elements;
foreach my $element (@array)
die "dupe found!" if $uniq_elements$element++;
【讨论】:
【参考方案4】:创建一个散列或一个集合或使用 collections.Counter()。
当您遇到每个字符串/输入时,请检查哈希中是否存在该实例。如果是这样,那就是重复的(对这些做任何你想做的事情)。否则,使用字符串作为键向哈希添加一个值(例如,哦,比如说数字一)。
示例(使用 Python 的 collections.Counter):
#!python
import collections
counts = collections.Counter(mylist)
uniq = [i for i,c in counts.iteritems() if c==1]
dupes = [i for i, c in counts.iteritems() if c>1]
这些计数器是围绕字典(散列映射集合的 Python 名称)构建的。
这是高效的,因为哈希键是索引的。在大多数情况下,键的查找和插入时间是在几乎恒定的时间内完成的。 (实际上 Perl 的“哈希”之所以被称为是因为它们是使用一种称为“哈希”的算法技巧来实现的——一种校验和,因为当输入任意输入时,它的碰撞概率极低。
如果您将值初始化为整数,从 1 开始,那么您可以增加每个值,因为您已经在哈希中找到了它的键。这几乎是最有效的通用字符串计数方法。
【讨论】:
【参考方案5】:不是直接的答案,但这将返回一个没有重复的数组:
#!/usr/bin/perl
use strict;
use warnings;
my @arr = ('a','a','a','b','b','c');
my %count;
my @arr_no_dups = grep !$count$_++ @arr;
print @arr_no_dups, "\n";
【讨论】:
my $dupes_found = !! grep $_ > 1 values %count;
更有效的测试是使用first
而不是grep
。实际上,即使my $dupes_found = @arr == @arr_no_dupes;
也应该可以工作。【参考方案6】:
请不要询问最省时的方法,除非您有一些特定要求,例如“我必须在一秒钟内对包含 100,000 个整数的列表进行重复数据删除”。否则,您会无缘无故地担心需要多长时间。
【讨论】:
也许 OP 只是好奇(尽管我同意除非它被证明是一个瓶颈,否则不值得担心)。无论如何,这应该是对问题的评论而不是答案。【参考方案7】:类似于@Schwern 的第二个解决方案,但在sort
的比较函数中更早地检查重复项:
use strict;
use warnings;
@_ = sort print "dup = $a$/" if $a eq $b; $a cmp $b @ARGV;
它不会像散列解决方案那样快,但它需要更少的内存并且非常可爱
【讨论】:
以上是关于使用 Perl 检查数据数组中重复项的最有效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
通过聚合在pandas组中查找频繁项的最有效方法是啥[重复]
在 perl 中检查 $string 是不是以 $needle 开头的最有效方法