Ну, я нашел способ, который более чем в 30 раз быстрее, хотя, возможно, его обман. Я включил код Benchmark.pm, чтобы сравнить его, поскольку вы, по-видимому, не знакомы с ним.
Эталон является:
Rate Join Cheat
Join 83294/s -- -97%
Cheat 2580687/s 2998% --
И код. После третьей линии, я думаю, вы поймете, почему его возможно жульничество:
use v5.14;
use Benchmark qw(cmpthese);
use Inline 'C';
sub an_join {
my ($s1, $s2) = @_;
return (join '', sort split(//, $s1)) eq
(join '', sort split(//, $s2));
}
use constant {
STR1 => 'abcdefghijklm',
STR2 => 'abcdefghijkmm',
STR3 => 'abcdefghijkml',
};
cmpthese(
0,
{
'Join' => 'an_join(STR1, STR2); an_join(STR1, STR3)',
'Cheat' => 'an_cheat(STR1, STR2); an_cheat(STR1, STR3)',
});
__END__
__C__
int an_cheat(const char *a, const char *b) {
unsigned char vec_a[26], vec_b[26];
const char *p, *end;
memset(vec_a, 0, sizeof(vec_a));
memset(vec_b, 0, sizeof(vec_b));
end = a+strlen(a);
for (p = a; p < end; ++p)
if (*p >= 'a' && *p <= 'z')
++vec_a[(*p)-'a'];
end = b+strlen(b);
for (p = b; p < end; ++p)
if (*p >= 'a' && *p <= 'z')
++vec_b[(*p)-'a'];
return 0 == memcmp(vec_a, vec_b, sizeof(vec_a));
}
Конечно, его обман, потому что его не написано в Perl-его в C. Кроме того, она имеет ограничения, версия Perl не (работает только с строчными ASCII-символами, что является самым значительным - он просто игнорирует все остальное). Но если вам действительно нужна скорость, вы можете использовать обман вроде этого.
редактировать:
Продление всем Latin1 (ну, сырые 8-битные символы, на самом деле). Кроме того, я обнаружил, что компилятору удалось оптимизировать более простой цикл (без точечной арифметики), и его также легче читать, поэтому ... Benchmark говорит мне, что версия с нижним регистром ASCII примерно на 10% быстрее:
int an_cheat_l1b(const char *a, const char *b) {
unsigned char vec_a[UCHAR_MAX], vec_b[UCHAR_MAX];
size_t len, i;
memset(vec_a, 0, sizeof(vec_a));
memset(vec_b, 0, sizeof(vec_b));
len = strlen(a);
for (i = 0; i < len; ++i)
++vec_a[((const unsigned char *)(a))[i]];
len = strlen(b);
for (i = 0; i < len; ++i)
++vec_b[((const unsigned char *)(b))[i]];
return 0 == memcmp(vec_a, vec_b, sizeof(vec_a));
}
Обратите внимание, что преимущество версии C возрастает по мере того, как строка становится длиннее, что ожидается, поскольку ее Θ (n) в отличие от версий Perl O (n · logn). Кроме того, штраф за полный латиница 1 уменьшается, а это значит, что штраф, вероятно, является memcmp.
Интересное чтение для сравнения массива: http://stackoverflow.com/questions/1609467/in-perl-is-there-a-built-in-way-to-compare-two-arrays-for-equality – Mat
Вы также можете сравнить длину строки и вернуть false, если длина строк различна, чтобы избежать сортировки строк, которые определенно не являются анаграммами. Для строк с одинаковой длиной сравнение по длине очень быстрое, намного быстрее, чем split-sort-join, поэтому любое поражение производительности будет незначительным. –
Ну, вы можете улучшить производительность, избавившись от функции сравнения - вам это не нужно: 'join '', sort split (//, $ s1)'. – derobert