2009-02-17 36 views
6

Chuỗi sau đây của tôi đã cố gắng tìm sự khác biệt giữa hai chuỗi. Nhưng nó chậm khủng khiếp vì nó lặp lại độ dài của chuỗi:Hoạt động bit để tìm sự khác biệt về chuỗi

#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; 
} 

Có cách nào thay thế nhanh không? Trong Perl, chúng tôi có thể có cách tiếp cận sau:

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

nhanh hơn nhiều so với lặp lại vị trí.

Tôi tự hỏi điều gì tương đương với nó trong C++?

+0

Jeez, không có thắc mắc đó là chậm, khi bạn đang phân bổ chuỗi mới chỉ để giữ duy nhất 'char' bạn có thể nhận được từ 'toán tử []', tại mỗi và mọi chỉ mục. –

Trả lời

8

Fun với STL:

#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 năm sau, Samaras có một câu hỏi: bạn có thể giải thích được không? :) Tôi phải rất câm để là người đầu tiên hỏi! :) – gsamaras

+2

@gsamaras: Trong phiên bản cơ bản của nó, inner_product tính tổng của sản phẩm của hai phạm vi, A và B: A [0] * B [0] + A [1] * B [1] + ... Trong phiên bản tổng quát (được sử dụng ở đây), hai thao tác (bổ sung và phép nhân) được cung cấp bởi người gọi. Những gì chúng tôi muốn ở đây là số cặp phần tử khác nhau, vì vậy chúng tôi vẫn muốn hoạt động đầu tiên được thêm vào (std :: plus), nhưng chúng tôi muốn hoạt động thứ hai là "không bằng" (std :: không (std :: equal_to)) thay cho phép nhân. –

+0

Tôi thấy Eric, cảm ơn, trong [câu hỏi] này (http://stackoverflow.com/questions/40773463/how-to-store-binary-data-when-you-only-care-about-speed), một so sánh chức năng của bạn và cho vòng lặp và nếu! cách tiếp cận được thực hiện, sử dụng các cấu trúc dữ liệu khác nhau. – gsamaras

2

Một số điểm rõ ràng mà có thể làm cho nó nhanh hơn:

  1. Vượt qua chuỗi như tài liệu tham khảo const, không phải bởi giá trị
  2. Sử dụng toán tử chỉ mục [] để có được nhân vật, không phải là một phương pháp gọi
  3. Compile với tối ưu hóa trên
+0

Làm thế nào để bạn "biên dịch với tối ưu hóa trên"? – neversaint

+0

Phụ thuộc rất nhiều vào trình biên dịch đang sử dụng, tôi sợ. Ví dụ: nếu bạn đang sử dụng GCC, hãy sử dụng tùy chọn -On, trong đó n là một chữ số kiểm soát mức tối ưu hóa. – unwind

10

Cố gắng thay thế cho vòng lặp theo:

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

Điều này sẽ nhanh hơn rất nhiều vì không có chuỗi mới nào được tạo.

+0

lmao, tôi không nhận thấy họ đang phân bổ 2 x chuỗi mới ở mọi chỉ mục, để giữ bản sao của 'char' ... –

3

Sử dụng vòng lặp:

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: Sửa đổi mã ban đầu của bạn là như effecient như 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; 
} 

Tùy chọn thứ hai sử dụng một số thuật toán STL để thực hiện việc nâng hạng nặng.

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

Bạn sử dụng chuỗi.

Như đã giải thích ở đây The hunt for the fastest Hamming Distance C implementation nếu bạn có thể sử dụng char * experiements tôi kết luận rằng cho Gcc 4.7.2 trên một Intel Xeon X5650 chức năng mục đích chung Hamming khoảng cách tính toán nhanh nhất cho nhỏ chuỗi (mảng char) là:

// 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; 
} 

Nếu vấn đề của bạn cho phép bạn thiết lập một giới hạn khoảng cách trên, do đó bạn không chăm sóc cho khoảng cách lớn hơn và giới hạn này luôn nhỏ hơn so với chuỗi chiều dài, ví dụ trên có thể furhterly tối ưu hóa để:

// 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; 
} 

Tôi không chắc chắn nếu const không bất cứ điều gì liên quan đến tốc độ, nhưng tôi sử dụng nó anyways ...

+0

(1) Hiệu suất phụ thuộc vào trình biên dịch * và * CPU, trong số những thứ khác. "Đây là nhanh nhất" là gây hiểu lầm tốt nhất, và dựa vào mã được biên dịch chính xác như trình biên dịch của bạn đã làm - mà không được yêu cầu bởi bất kỳ tiêu chuẩn nào. (2) Yêu cách bạn bỏ qua thực tế là người gọi phải tìm độ dài. Nếu mã này làm phiền, tốc độ của nó sẽ bị cắt giảm một nửa. (3) C không phải là C++. "Chuỗi" của bạn không phải là chuỗi C++. Điều này có thể đã được thực hiện với các chuỗi C++ mà không bị mất hiệu suất. (4) Nghiêm túc? Bạn đã hồi sinh một câu hỏi 4 năm cho điều này? – cHao

+0

(1) Gcc 4.7.2 dành cho Intel Xeon X5650. (2-3-4 vv ...) Tôi "resurected" này, như bạn nói, bởi vì tôi đã bắt đầu một sợi mới được coi là một bản sao của điều này. Câu trả lời này phục vụ như là một câu trả lời tốt cho chủ đề ban đầu của tôi mà tôi không thể trả lời, vì vậy tôi trả lời thread của tôi ở đây. Nếu câu trả lời này không phù hợp ở đây có nghĩa là chủ đề của tôi không trùng lặp với điều này. Tôi có thể ném câu trả lời này vào bài đăng "trùng lặp" của tôi theo cách khác không? –

+0

Và một điều nữa. Tác giả nói rằng mã của anh ta "vô cùng chậm chạp". Một lý do tôi viết này là để cung cấp cho anh ta một thay thế đó là "thoát khỏi các chuỗi" (nếu có thể) và sử dụng char *. Trong sự khác biệt thiết lập ở trên là rất lớn khi chúng ta chuyển đổi tất cả các chuỗi thành char *. Nó có thể là một giải pháp cho anh ta để làm như vậy. (tôi không nhận thấy bài đăng cũ bao nhiêu tuổi) –

Các vấn đề liên quan