Bueno, he encontrado una manera que es más de 30 veces más rápida, aunque, podría decirse que es una trampa. He incluido el código de Benchmark.pm para compararlo, ya que aparentemente no estás familiarizado con él.
El punto de referencia es:
Rate Join Cheat
Join 83294/s -- -97%
Cheat 2580687/s 2998% --
Y el código. Después de la tercera línea, creo que vas a entender por qué su engaño podría decirse:
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));
}
Por supuesto, su engaño porque no es escrito en Perl-its en C. Además, tiene limitaciones no tiene la versión de Perl (solo funciona con caracteres ASCII en minúscula siendo el más significativo; simplemente ignora todo lo demás). Pero si realmente necesitas velocidad, puedes usar trampas como esta.
edición:
La extensión a todos Latin1 (así, los caracteres de 8 bits sin formato, en realidad). Además, encontré que el compilador logró optimizar un bucle más simple (sin aritmética de puntos) mejor, y es más fácil de leer también, entonces ... Benchmark me dice que la versión solo en minúscula ASCII es aproximadamente 10% más rápida:
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));
}
Tenga en cuenta que la ventaja de la versión C aumenta a medida que la cadena se alarga, lo que se espera, ya que su Θ (n) en comparación con las versiones de Perl O (n · logn). También disminuye la penalización para Latin1 completo, lo que significa que la pena es probablemente el memcmp.
Lectura interesante para la comparación de matrices: http://stackoverflow.com/questions/1609467/in-perl-is-there-a-built-in-way-to-compare-two-arrays-for-equality – Mat
También puede comparar la longitud de la cadena y devolver false si las longitudes de la cadena son diferentes, para evitar tener que ordenar cadenas que definitivamente no son anagramas. Para cadenas que tienen la misma longitud, la comparación de longitud es muy rápida, mucho más rápida que split-sort-join, por lo que cualquier impacto en el rendimiento sería insignificante. –
Bueno, puede mejorar el rendimiento al deshacerse de la función de comparación; no lo necesita: 'join '', sort split (//, $ s1)'. – derobert