2012-04-10 27 views
5

Tôi tương đối mới với python và bắt đầu làm việc với các cây hậu tố. Tôi có thể xây dựng chúng, nhưng tôi đang chạy vào một vấn đề bộ nhớ khi chuỗi được lớn. Tôi biết rằng họ có thể được sử dụng để làm việc với các chuỗi DNA có kích thước 4^10 hoặc 4^12, nhưng bất cứ khi nào tôi cố gắng thực hiện một phương pháp, tôi kết thúc với một vấn đề bộ nhớ.Làm việc với các cây hậu tố ở trăn

Đây là mã của tôi để tạo chuỗi và cây hậu tố.

import random 

def get_string(length): 
    string="" 
    for i in range(length): 
     string += random.choice("ATGC") 
    return string 

word=get_string(4**4)+"$" 

def suffixtree(string): 
    for i in xrange(len(string)): 
     if tree.has_key(string[i]): 
      tree[string[i]].append([string[i+1:]][0]) 
     else: 
      tree[string[i]]=[string[i+1:]] 
    return tree 

tree={} 
suffixtree(word) 

Khi tôi nhận được khoảng 4 ** 8, tôi gặp phải sự cố bộ nhớ nghiêm trọng. Tôi khá mới với điều này vì vậy tôi chắc chắn tôi đang thiếu một cái gì đó với lưu trữ những điều này. Mọi lời khuyên sẽ được đánh giá cao.

Lưu ý: Tôi muốn thực hiện tìm kiếm chuỗi để tìm các chuỗi phù hợp trong một chuỗi rất lớn. Chuỗi tìm kiếm phù hợp với kích thước là 16. Vì vậy, điều này sẽ tìm một chuỗi kích thước 16 trong một chuỗi lớn, và sau đó chuyển sang chuỗi tiếp theo và thực hiện tìm kiếm khác. Vì tôi sẽ thực hiện một số lượng lớn các tìm kiếm, nên một cây hậu tố đã được đề xuất.

Rất cám ơn

+1

Tôi không quen thuộc với cây hậu tố và triển khai của bạn không cho tôi manh mối về cách thức hoạt động của nó. Tôi khuyên bạn nên sử dụng thư viện, ví dụ: [pytst] (http://nicolas.lehuen.com/category/pytst/). – MattH

+1

Gợi ý: một cấu trúc cây sẽ liên quan đến dicts lồng nhau. –

Trả lời

2

Như những người khác đã nói, cấu trúc dữ liệu bạn đang xây dựng không phải là cây hậu tố. Tuy nhiên, các vấn đề về bộ nhớ xuất phát từ thực tế là cấu trúc dữ liệu của bạn liên quan đến rất nhiều bản sao chuỗi rõ ràng . Một cuộc gọi như thế này

string[i+1:] 

tạo ra một (sâu) bản sao của chuỗi con bắt đầu từ i+1 thực tế.

Nếu bạn vẫn quan tâm đến việc xây dựng cấu trúc dữ liệu ban đầu của bạn (bất kể việc sử dụng nó có thể là gì), giải pháp tốt là sử dụng bộ đệm thay vì bản sao chuỗi. Thuật toán của bạn sau đó sẽ trông như thế này:

def suffixtree(string): 
    N = len(string) 
    for i in xrange(N): 
     if tree.has_key(string[i]): 
      tree[string[i]].append(buffer(string,i+1,N)) 
     else: 
      tree[string[i]]=[buffer(string,i+1,N)] 
    return tree 

tôi đã cố gắng này được nhúng trong phần còn lại của mã của bạn, và khẳng định rằng nó đòi hỏi ít hơn đáng kể sau đó 1 GB bộ nhớ chính thậm chí với tổng chiều dài 8^11 ký tự.

Lưu ý rằng điều này có thể sẽ có liên quan ngay cả khi bạn chuyển sang cây hậu tố thực tế. Việc thực hiện cây hậu tố chính xác sẽ không lưu trữ các bản sao (thậm chí không có bộ đệm) trong các cạnh cây; tuy nhiên, trong quá trình xây dựng cây, bạn có thể cần nhiều bản sao tạm thời của các chuỗi. Sử dụng loại buffer cho những điều này là một ý tưởng rất tốt để tránh đặt một gánh nặng lên bộ thu gom rác cho tất cả các bản sao chuỗi không cần thiết rõ ràng.

+0

Cảm ơn bạn đã cung cấp thông tin. Tôi sẽ cần phải nhìn vào hàm đệm chi tiết hơn. – doggysaywhat

4

Điều này không giống như cây với tôi. Có vẻ như bạn đang tạo ra tất cả các hậu tố có thể và lưu trữ chúng trong một hashtable.

Bạn có thể sẽ nhận được hiệu suất bộ nhớ nhỏ hơn nhiều nếu bạn sử dụng một cây thực tế. Tôi khuyên bạn nên sử dụng triển khai thư viện.

2

Nếu các vấn đề về bộ nhớ của bạn nằm trong việc tạo cây hậu tố, bạn có chắc là bạn cần nó không? Bạn có thể tìm thấy tất cả các trận đấu trong một chuỗi đơn như thế này:

word=get_string(4**12)+"$" 

def matcher(word, match_string): 
    positions = [-1] 
    while 1: 
     positions.append(word.find(match_string, positions[-1] + 1)) 
     if positions[-1] == -1: 
      return positions[1:-1] 

print matcher(word,'AAAAAAAAAAAA') 
[13331731, 13331732, 13331733] 
print matcher('AACTATAAATTTACCA','AT') 
[4, 8] 

máy của tôi là khá cũ, và điều này mất 30 giây để chạy, với 4^12 chuỗi. Tôi đã sử dụng một mục tiêu 12 chữ số để có một số trận đấu. Ngoài ra giải pháp này sẽ tìm thấy kết quả chồng chéo - nên có bất kỳ.

Here là một mô-đun cây hậu tố bạn có thể thử, như thế này:

import suffixtree 
stree = suffixtree.SuffixTree(word) 
print stree.find_substring("AAAAAAAAAAAA") 

Thật không may, máy tính của tôi là quá chậm chạp trong việc kiểm tra này ra đúng với chuỗi dài. Nhưng có lẽ một khi hậu tố được xây dựng, các tìm kiếm sẽ rất nhanh, vì vậy đối với số lượng lớn các tìm kiếm, nó sẽ là một cuộc gọi tốt. Hơn nữa find_substring chỉ trả lại kết quả trùng khớp đầu tiên (không biết đây có phải là vấn đề không, tôi chắc chắn bạn có thể điều chỉnh nó dễ dàng).

Cập nhật: Chia chuỗi thành cây hậu tố nhỏ hơn, như vậy tránh được các vấn đề bộ nhớ

Vì vậy, nếu bạn cần phải làm 10 triệu tìm kiếm trên 4^12 chuỗi dài, chúng ta rõ ràng không muốn chờ đợi cho 9,5 năm (tìm kiếm đơn giản tiêu chuẩn, lần đầu tiên tôi đề xuất, trên máy chậm của tôi ...). Tuy nhiên, chúng tôi vẫn có thể sử dụng cây hậu tố (do đó nhanh hơn rất nhiều), VÀ tránh các vấn đề về bộ nhớ. Chia chuỗi lớn thành các khối có thể quản lý được (mà chúng ta biết bộ nhớ máy có thể đối phó với) và biến một đoạn thành cây hậu tố, tìm kiếm nó 10 triệu lần, sau đó loại bỏ đoạn đó và di chuyển sang đoạn tiếp theo. Chúng ta cũng cần nhớ tìm kiếm chồng chéo giữa mỗi đoạn.Tôi đã viết một số mã để làm điều này (Nó giả định chuỗi lớn để được tìm kiếm, word là một bội số của chuỗi có thể quản lý tối đa, max_length, bạn sẽ phải điều chỉnh mã để kiểm tra phần còn lại ở cuối, nếu đây là không phải như vậy):

def split_find(word,search_words,max_length): 
    number_sub_trees = len(word)/max_length 
    matches = {} 
    for i in xrange(0,number_sub_trees): 
     stree = suffixtree.SuffixTree(word[max_length*i:max_length*(i+1)]) 
     for search in search_words: 
      if search not in matches: 
       match = stree.find_substring(search) 
       if match > -1: 
        matches[search] = match + max_length*i,i 
      if i < number_sub_trees: 
       match = word[max_length*(i+1) - len(search):max_length*(i+1) + len(search)].find(search) 
       if match > -1: 
        matches[search] = match + max_length*i,i 
    return matches 

word=get_string(4**12) 
search_words = ['AAAAAAAAAAAAAAAA'] #list of all words to find matches for 
max_length = 4**10 #as large as your machine can cope with (multiple of word) 
print split_find(word,search_words,max_length) 

Trong ví dụ này tôi giới hạn chiều dài cây hậu tố tối đa dài 4^10, cần khoảng 700MB. Sử dụng mã này, cho một chuỗi dài 4^12, 10 triệu lượt tìm kiếm sẽ mất khoảng 13 giờ (tìm kiếm đầy đủ, với 0 kết quả phù hợp, vì vậy nếu có kết quả khớp thì sẽ nhanh hơn). Tuy nhiên, như một phần của điều này chúng ta cần phải xây dựng 100 cây hậu tố, mà sẽ mất khoảng.100 * 41 giây = 1 giờ.

Vì vậy, tổng thời gian chạy là khoảng 14 giờ, không có vấn đề về bộ nhớ ... Cải thiện lớn trên 9,5 năm. Lưu ý rằng tôi đang chạy trên CPU 1.6GHz với RAM 1 GB, vì vậy bạn nên có khả năng làm tốt hơn thế này!

+0

Cảm ơn sự giúp đỡ, tôi đang thử nghiệm nó. Tuy nhiên, tôi thấy rằng với chuỗi khoảng 4^11 trong kích thước tôi vẫn kết thúc với các vấn đề bộ nhớ. – doggysaywhat

+0

@doggysaywhat - bạn sẽ cần khoảng 3 GB để xây dựng cây hậu tố từ chuỗi 4^11. Và nó sẽ là khoảng 12GB cho 4^12 ... Bạn cần bao nhiêu chuỗi để tìm kiếm? và có bao nhiêu tìm kiếm?Bạn có thể tốt hơn bằng cách sử dụng cách tiếp cận tôi mô tả đầu tiên và chỉ cần chờ đợi! – fraxel

+0

Xin chào Fraxel, xin lỗi vì sự chậm trễ. Tôi có vấn đề gia đình phát sinh. Phương thức chậm hơn gặp phải sự cố khi tôi đạt đến 1-10 triệu lượt tìm kiếm. Ý tưởng đằng sau điều này là tìm tất cả các phần tử lặp lại có kích thước 16 trong một chuỗi ban đầu có kích thước M. Vì vậy, lấy chuỗi M [0:16] và sau đó là M [1:17], v.v. đến cuối bản gốc chuỗi và thực hiện tìm kiếm các bản sao của chúng trong chuỗi. Về cơ bản nó cung cấp cho bạn số lần lặp lại. Tôi đã chơi xung quanh với điều này và với việc sử dụng thuật toán burrows-wheeler để làm khớp chính xác cho các kích thước lớn. – doggysaywhat

2

Lý do bạn gặp phải sự cố về bộ nhớ là do nhập 'banana' bạn đang tạo {'b': ['anana$'], 'a': ['nana$', 'na$', '$'], 'n': ['ana$', 'a$']}. Đó không phải là cấu trúc cây. Bạn có mọi hậu tố có thể của đầu vào được tạo và lưu trữ trong một trong các danh sách. Điều đó chiếm không gian lưu trữ O (n^2). Ngoài ra, đối với một cây hậu tố để hoạt động đúng, bạn muốn các nút lá để cung cấp cho bạn vị trí chỉ mục.

result you want to get{'banana$': 0, 'a': {'$': 5, 'na': {'$': 3, 'na$': 1}}, 'na': {'$': 4, 'na$': 2}}. (Đây là một đại diện được tối ưu hóa; một cách tiếp cận đơn giản hơn giới hạn chúng ta thành các nhãn đơn.)

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