2009-02-17 8 views
6

La siguiente cadena de la mía intentó encontrar la diferencia entre dos cadenas. Pero es terriblemente lento, ya que iterar la longitud de la cadena:Operación de bit para encontrar la diferencia de cadena

#include <string> 
#include <vector> 
#include <iostream> 
using namespace std; 


int hd(string s1, string s2) { 
    // hd stands for "Hamming Distance" 
    int dif = 0; 

    for (unsigned i = 0; i < s1.size(); i++) { 
     string b1 = s1.substr(i,1); 
     string b2 = s2.substr(i,1); 

     if (b1 != b2) { 
      dif++; 
     } 
    } 

    return dif; 
} 

int main() { 

    string string1 = "AAAAA"; 
    string string2 = "ATATT"; 
    string string3 = "AAAAA"; 

    int theHD12 = hd(string1,string2); 
    cout << theHD12 << endl; 

    int theHD13 = hd(string1,string3); 
    cout << theHD13 << endl; 
} 

¿Hay una alternativa rápida para hacer eso? En Perl que puede tener el siguiente enfoque:

sub hd { 
    return ($_[0]^$_[1]) =~ tr/\001-\255//; 
} 

que es muCH2 más rápido que la iteración de la posición.

Me pregunto cuál es su equivalente en C++?

+0

Por Dios, no es de extrañar que es lenta, cuando se está asignando nuevas cadenas simplemente para mantener el único 'char's que podría obtener de' operator [] ', en cada índice. –

Respuesta

8

la diversión con el TEL:

#include <numeric> //inner_product 
#include <functional> //plus, equal_to, not2 
#include <string> 
#include <stdexcept> 

unsigned int 
hd(const std::string& s1, const std::string& s2) 
{ 
    // TODO: What should we do if s1.size() != s2.size()? 
    if (s1.size() != s2.size()){ 
     throw std::invalid_argument(
      "Strings passed to hd() must have the same lenght" 
    ); 
    } 

    return std::inner_product(
     s1.begin(), s1.end(), s2.begin(), 
     0, std::plus<unsigned int>(), 
     std::not2(std::equal_to<std::string::value_type>()) 
    ); 
} 
+0

7 años después, Samaras tiene una pregunta: ¿puedes explicarlo por favor? :) Debo ser muy tonto para ser el primero en preguntar! :) – gsamaras

+2

@gsamaras: en su versión básica, inner_product calcula la suma del producto de dos rangos, A y B: A [0] * B [0] + A [1] * B [1] + ... En la versión generalizada (utilizada aquí), las dos operaciones (adición y multiplicación) son proporcionadas por el llamante. Lo que queremos aquí es el recuento de pares de elementos que son diferentes, por lo que aún queremos que la primera operación sea adicional (std :: plus), pero queremos que la segunda operación sea "no es igual" (std :: not (std :: equal_to)) en lugar de la multiplicación. –

+0

Veo a Eric, gracias, en esta [pregunta] (http://stackoverflow.com/questions/40773463/how-to-store-binary-data-when-you-only-care-about-speed), una comparación de su función y for-loop y si! el acercamiento está hecho, usando diferentes estructuras de datos. – gsamaras

2

Algunos puntos obvios que podrían hacer que sea más rápido:

  1. Pase las cadenas como referencias const, no por el valor
  2. Utilice el operador de indexación [] para obtener caracteres, no es una llamada a un método
  3. Compilar con la optimización de
+0

¿Cómo se "compila con optimización en"? – neversaint

+0

Depende mucho del compilador en uso, me temo. Si está utilizando GCC, por ejemplo, use la opción -On, donde n es un dígito que controla el nivel de optimización. – unwind

10

tratar de sustituir el bucle por:

for (unsigned i = 0; i < s1.size(); i++) { 
    if (b1[i] != b2[i]) { 
      dif++; 
    } 
} 

Esto debería ser mucho más rápido porque no se crean nuevas cadenas.

+0

lmao, no me di cuenta de que estaban asignando 2 x nuevas cadenas en cada índice, para guardar copias de 'char's ... –

3

Use iteradores:

int GetHammingDistance(const std::string &a, const std::string &b) 
{ 
    // Hamming distance is not defined for strings of different lengths. 
    ASSERT(a.length() == b.length()); 

    std::string::const_iterator a_it = a.begin(); 
    std::string::const_iterator b_it = b.begin(); 

    std::string::const_iterator a_end = a.end(); 
    std::string::const_iterator b_end = b.end(); 

    int distance = 0; 
    while (a_it != a_end && b_it != b_end) 
    { 
     if (*a_it != *b_it) ++distance; 
     ++a_it; ++b_it; 
    } 

    return distance; 
} 
3

Choice 1: Modificar el código original para ser tan eficiente como possable.

int hd(string const& s1, string const& s2) 
{ 
    // hd stands for "Hamming Distance" 
    int dif = 0; 

    for (std::string::size_type i = 0; i < s1.size(); i++) 
    { 
     char b1 = s1[i]; 
     char b2 = s2[i]; 

     dif += (b1 != b2)?1:0; 
    } 

    return dif; 
} 

Segunda opción: utilice algunos de los algoritmos de STL para realizar el trabajo pesado.

struct HammingFunc 
{ 
    inline int operator()(char s1,char s2) 
    { 
     return s1 == s2?0:1; 
    } 
}; 

int hd(string const& s1, string const& s2) 
{ 
    int diff = std::inner_product(s1.begin(),s1.end(), 
            s2.begin(), 
            0, 
            std::plus<int>(),HammingFunc() 
           ); 
    return diff; 
} 
1

Utiliza cadenas.

como se explica aquí The hunt for the fastest Hamming Distance C implementation si se puede usar char * mis experiements concluyen que para gcc 4.7.2 en una Intel Xeon X5650 la función más rápida de uso general para el cálculo de la distancia de Hamming pequeñas cadenas (arrays de char) es:

// na = length of both strings 
unsigned int HammingDistance(const char* a, unsigned int na, const char* b) { 

    unsigned int num_mismatches = 0; 
    while (na) { 
     if (*a != *b) 
      ++num_mismatches; 

     --na; 
     ++a; 
     ++b; 
    } 

    return num_mismatches; 
} 

Si su problema le permite establecer un límite superior a distancia, de modo que no se preocupan de mayores distancias y este límite es siempre menor que la longitud de las cuerdas, el ejemplo anterior puede ser furhterly optimizado para:

// na = length of both strings, dist must always be < na 
unsigned int HammingDistance(const char* const a, const unsigned int na, const char* const b, const unsigned int dist) { 

    unsigned int i = 0, num_mismatches = 0; 

    while(i <= dist) 
    { 
     if (a[i] != b[i]) 
      ++num_mismatches; 

     ++i; 
    } 

    while(num_mismatches <= dist && i < na) 
    { 
     if (a[i] != b[i]) 
      ++num_mismatches; 

     ++i; 
    } 

    return num_mismatches; 
} 

No estoy seguro de si const hace nada con respecto a la velocidad, pero lo uso todos modos ...

+0

(1) El rendimiento depende del compilador * y * CPU, entre otras cosas. "Este es el más rápido" es engañoso en el mejor de los casos, y depende del código que se compila exactamente como lo hizo su compilador, lo que no es requerido por ningún estándar. (2) Me encanta cómo ignoras el hecho de que la persona que llama tiene que buscar longitudes. Si este código se molestó, su velocidad se reduciría a la mitad. (3) C no es C++. Sus "cadenas" no son cadenas de C++. Esto podría haberse hecho con cadenas de C++ sin sacrificar el rendimiento. (4) ¿En serio? ¿Resucitaste una pregunta de 4 años para esto? – cHao

+0

(1) Gcc 4.7.2 para Intel Xeon X5650. (2-3-4 etc ...) "Resurected" esto, como dices, porque ya he comenzado un nuevo hilo que se considera un duplicado de esto. Esta respuesta sirve como una buena respuesta a mi hilo original que no puedo responder, así que respondo mi hilo aquí. Si esta respuesta no cabe aquí significa que mi hilo no es duplicado de esto. ¿Puedo arrojar esta respuesta a mi publicación "duplicada" de otra manera? –

+0

y algo más. El autor dijo que su código era "irremediablemente lento". Una razón por la que escribo esto es para ofrecerle una alternativa que es "deshacerse de las cuerdas" (si es posible) y usar char *. En la configuración anterior, la diferencia fue enorme cuando transformamos todas las cadenas en char *. Podría ser una solución para él hacer lo mismo. (No me di cuenta de la antigüedad de la publicación) –

Cuestiones relacionadas