Что самый эффективный путь состоит в том, чтобы проверить на дубликаты в массиве данных с помощью Perl?

Я должен видеть, существуют ли дубликаты в массиве строк, каков самый эффективный временем способ сделать его?

19
задан Ether 4 October 2010 в 19:20
поделиться

5 ответов

Одна из вещей, которые мне нравятся в 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";
}

Output

'false' дублируется.

'no' дублируется.

27
ответ дан 30 November 2019 в 02:04
поделиться

Преобразование массива в хэш - самый быстрый способ [ 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) (когда все дублируется), но если ваш массив большой и дубликатов мало, вы выиграете.

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

23
ответ дан 30 November 2019 в 02:04
поделиться

Если вам все равно нужен уникальный массив, то быстрее всего использовать сильно оптимизированную библиотеку 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}++;
}
8
ответ дан 30 November 2019 в 02:04
поделиться

Создайте хэш или набор или используйте 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]

Эти счетчики построены на основе словарей (имя Pythons для коллекций хешированных отображений).

Это экономно по времени, потому что хеш-ключи индексируются. В большинстве случаев время поиска и вставки ключей происходит почти за постоянное время. (На самом деле «хеши» Perl называются так, потому что они реализованы с использованием алгоритмического трюка, называемого «хешированием» - своего рода контрольной суммы, выбранной из-за крайне низкой вероятности коллизии при подаче произвольных входных данных).

Если вы инициализируете значения целыми числами, начиная с 1, вы можете увеличивать каждое значение на единицу, если найдете его ключ уже в хэше. Это чуть ли не наиболее эффективный универсальный способ подсчета строк.

6
ответ дан 30 November 2019 в 02:04
поделиться

Это не прямой ответ, но это вернет массив без дубликатов:

#!/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";
2
ответ дан 30 November 2019 в 02:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: