2016-10-31 25 views
5

Tôi muốn giảm độ phức tạp của thuật toán sau. Về cơ bản, nó có một từ như là một đầu vào và tính toán số lượng các chữ cái duy nhất bên trong nó ("entropy" của từ). Giải pháp hiện tại của tôi sử dụng 3 nhúng cho vòng, mà đi ra đến một phức tạp của o (n^3). Vì mã này là một phần của dự án lớn hơn (chúng tôi đã xây dựng bộ giải cho trò chơi được gọi là boggle), tôi đã hy vọng giảm độ phức tạp của thuật toán để giảm thời gian thực thi của nó. Cảm ơn trước!Giảm độ phức tạp của mã o (n^3) C++

int wordEntropy(string word) 
{ 

int length = word.length(); 
int uniquewords = length; 
string compare = word; 
char save[17]; 
int cond=0; 

for (int ii=0; ii < length; ii++) 
{ 

    for (int jj=ii+1; jj < length; jj++) 
    { 
     for (int kk=0; kk<= ii; kk++) 
     { 
      if (save[kk] == word[ii]) {cond++;} 
     } 
     if (word[ii] == word[jj]) 
     { 
      if (cond>0) {break;} 
      uniquewords--; 
     } 
    } 

    save[ii] = word[ii]; 
    cond = 0; 

} 
return uniquewords; 
} 
+0

Giữ đơn giản? Lặp lại từ đó, ghi lại những chữ cái bạn đã thấy trong một bitet. Cuối cùng, tổng hợp các bitet. Độ phức tạp thời gian O (n + m) trong đó n là độ dài của từ và m là kích thước của bảng chữ cái (ví dụ 26). –

Trả lời

9

Nếu đây thực sự là về hiệu suất, tùy thuộc vào phạm vi của các nhân vật có giá trị một cái gì đó như thế này có thể nhanh hơn:

std::size_t wordEntropy(const std::string & word) 
{ 
    unsigned char seen[256] = { 0 }; 
    for(unsigned char c : word) 
    { 
     ++seen[ c ]; 
    } 
    return std::count_if(& seen[0], & seen[ 0 ] + 256, 
          [](unsigned char c) { return c != 0; }); 
} 

Nhưng rõ ràng, điều này khó khăn hơn một chút để duy trì. Giải pháp này có đảm bảo độ phức tạp của O (n) và nó không thực hiện bất kỳ phân bổ bộ nhớ động nào.

phiên bản thay thế mà không có vấn đề nếu một nhân vật xuất hiện nhiều hơn 255 lần:

std::size_t wordEntropy(const std::string & word) 
{ 
    bool seen[256] = { false }; 
    for(unsigned char c : word) 
    { 
     seen[ c ] = true; 
    } 
    return std::count_if(& seen[0], & seen[ 0 ] + 256, 
          [](bool t) { return t; }); 
} 
+1

Bạn có thể cần phải viết đó là 'for (unsigned char c: word)', vì nhiều triển khai C++ xử lý phạm vi 'char' là' [-128, 127] '. – Xirema

+2

Bạn cũng cần phải thay thế '256' đó bằng' std :: numeric :: limits :: max() 'trong trường hợp bạn nhấn char 16 bit. – NathanOliver

+0

Có, tất cả những điều trên đều đúng. Ngoài ra, nếu một ký tự xảy ra thường xuyên hơn sau đó 255 lần trong một từ, thuật toán gốc không thành công, tôi cung cấp một phiên bản thay thế để khắc phục vấn đề này. –

13

Một giải pháp rẻ tiền chỉ là để dính vào các nhân vật trong một unordered_set, mà là một HashSet (khấu hao O (1) chèn và tra cứu):

#include <unordered_set> 

int wordEntropy(const std::string &word) { 
    std::unordered_set<char> uniquechars(word.begin(), word.end()); 
    return uniquechars.size(); 
} 

Điều này mang lại một phức tạp của O (n), đó là tốt như nó được.

+0

Trung bình là O (N) nhưng nó có thể đạt trường hợp xấu nhất là O (N^2). Không chắc chắn chính xác những gì bạn sẽ cần phải làm cho trường hợp xấu nhất này mặc dù. – NathanOliver

+0

@NathanOliver Bạn sẽ cần một tập tin 'unordered_set' được thực hiện tồi tệ để đạt được trường hợp xấu nhất, hoặc triển khai thực hiện sai' băm '. Đó là nguyên nhân gây ra sự xuống cấp hiệu năng trong Bộ băm. – Xirema

+0

@Xirema Vì vậy, nó có liên quan đến va chạm sau đó? – NathanOliver

10

Do việc tính toán tại chỗ, mà không cần bất kỳ cấp phát bộ nhớ thêm (và tốn thời gian):

std::sort(word.begin(), word.end()); 
auto last = std::unique(word.begin(), word.end()); 
return last - word.begin(); 
+0

Cần lưu ý rằng đối với chuỗi dài sẽ là O (n log n). (Đối với những từ Boggle điển hình, sự khác biệt có thể không quan trọng). – nneonneo

+3

@nneonneo - đối với các từ Boggle điển hình, sự khác biệt (so với sử dụng một số dạng) là quan trọng: tất cả chi phí bộ nhớ và độ phức tạp thời gian chạy của một bộ vượt xa công việc "thêm" cần thiết để sắp xếp một từ ngắn. Có nhiều hơn nữa để đánh giá hiệu suất hơn so với sự phức tạp tiệm cận. –

0

Nếu chuỗi ngắn, sau đó bạn sẽ lo lắng thêm về allocs bộ nhớ hơn lớn-O. Dù bằng cách nào, đây là một giải pháp nhanh hơn.

Vì bạn đã đề cập rằng trò chơi này là một trò chơi boggle, và đầu vào cho hàm này là một chuỗi có tên "word", tôi giả định rằng bạn đã xác minh rằng tất cả các ký tự trong "word" là ký tự bảng chữ cái ascii. Nếu có, đây có lẽ là số entropy bất biến nhanh nhất của trường hợp:

int word_entropy (std::string const& word) 
{ 
    uint32_t bit_map = 0; 
    for (char const ch : word) 
     bit_map |= static_cast <uint32_t> (1) << (ch & 31); 
    return __builtin_popcount (bit_map); 
} 
Các vấn đề liên quan