Tôi không nghĩ rằng bạn có thể làm tốt hơn so với O (n) thời gian phức tạp - trong trường hợp xấu nhất bạn sẽ phải chạm vào mọi phần tử của tập dữ liệu để tìm ra trùng lặp
Một cách để cải thiện tiêu thụ không gian (với chi phí yêu cầu một số bit twiddling cũng như hai đi qua các tập dữ liệu) là sử dụng một Bloom Filter. Ý tưởng là thực hiện lần đầu tiên vượt qua tập dữ liệu: nếu bạn tìm thấy một bản sao có thể thì bạn xóa nó khỏi tập dữ liệu và thêm nó vào bảng băm (nếu chức năng lọc hoa nở chính xác thì chỉ khoảng 1% các phần tử sẽ được gắn cờ khi có thể trùng lặp). Sau đó, thực hiện lần thứ hai vượt qua tập dữ liệu được lọc, kiểm tra các phần tử dựa vào bảng băm nhỏ của các bản sao có thể có.
Mã của tôi sẽ bằng Java vì đó là ngôn ngữ tôi quen thuộc nhất.
Class DupFinder {
BloomFilter filter = new BloomFilter();
HashTable hashTable = new HashTable();
int start = 0;
int run(int[] dataset) {
// first pass
for(int i = 0; i < dataset.length; i++) {
if(filter.contains(dataset[i]) {
// check if element is in hashTable, else add it
if(hashTable.contains(dataset[i]) {
return dataset[i]; // duplicate found
} else {
hashTable.add(dataset[i]);
}
// remove element from dataset
int temp = dataset[start];
dataset[start] = dataset[i];
dataset[i] = temp;
start++;
} else filter.add(dataset[i]);
}
// second pass
for(int i = start; i < dataset.length; i++) {
if(hashTable.contains(dataset[i]) {
return dataset[i]; // duplicate found
}
}
return NULL; // no duplicate found
}
}
Một thay thế cho bảng băm của bạn là sử dụng một Radix Sort, một thời gian tuyến tính thuật toán sắp xếp. Phân loại Radix sẽ có hiệu suất trường hợp xấu nhất tốt hơn (O (n) cho sắp xếp radix, so với O (n^2) cho bảng băm trong trường hợp không chắc bạn chạy vào một số va chạm vô lý) nhưng hiệu suất trung bình tệ hơn (Việc thực hiện bảng băm thường sẽ tìm thấy bản sao sau khi quét chỉ một nửa số liệu, trong khi phân loại radix sẽ luôn yêu cầu nhiều lần vượt qua tập dữ liệu). Phân loại Radix cũng sẽ hiệu quả hơn về mức tiêu thụ không gian nếu bạn sử dụng cấu trúc dữ liệu không gian hiệu quả cho các nhóm, ví dụ: một danh sách chunked.
Bạn có thể song song việc triển khai bảng băm mà không phải chịu quá nhiều chi phí đồng bộ hóa. Sử dụng các chủ đề t, mỗi luồng sẽ xử lý n/t phần tử của tập dữ liệu (ví dụ:nếu bạn có 32 phần tử trong tập dữ liệu và 2 luồng, thì thread0 xử lý các phần tử 0-15 và thread1 xử lý các phần tử 16-31), đặt mỗi phần tử vào một thùng với chỉ mục absoluteValue (x modulo t). Theo đó, mỗi luồng sẽ chịu trách nhiệm xử lý tất cả các phần tử với chỉ mục nhóm đã cho, ví dụ: thread0 sẽ xử lý tất cả các nhóm với chỉ số 0. Tôi đang sử dụng BlockingQueue để đồng bộ hóa - điều này cho phép một chuỗi gọi mất() trên hàng đợi, khiến cho chuỗi xóa phần tử đầu tiên của hàng đợi hoặc chặn khác cho đến khi phần tử trở nên có sẵn; tất cả các cấu trúc dữ liệu khác là thread-local. Bạn sẽ cần khởi tạo biến dupFinders để một cá thể DupFinder xuất hiện trong cùng một chỉ mục của biến dupFinders của DupFinder (ví dụ thread0 luôn xuất hiện trong chỉ mục 0, do đó đảm bảo rằng tất cả các phần tử trong BlockingQueue của nó có absoluteValue (x modulo t) == 0).
Class DupFinder implements Callable<Integer> {
private Class Chunk {
int size = 0;
int chunk = new int[64];
boolean add(int x) {
if(size < 64) {
chunk[size] = x;
size++;
return true;
} else return false;
}
}
int t = ??? // number of threads
private BlockingQueue<Stack<Chunk>> queue = new LinkedBlockingQueue()
private DupFinder[] dupFinders = new DupFinder[t];
private Stack<Chunk>[] stacks = new Stack<Chunk>[t];
void add(Stack<Chunk> stack) {
queue.add(stack);
}
// the thread only receives n/t elements of the dataset
int call(int[] partialDataset) {
// partition dataset elements by their modulus(t)
for(int i = 0; i < partialDataset.length; i++) {
tempStack = stacks[Math.absoluteValue(partialDataset[i] modulo t)];
if(!tempStack.peek().add(partialDataset[i])) {
Chunk chunk = new Chunk();
chunk.add(partialDataset[i]);
tempStack.push(chunk);
}
}
// distribute chunk stacks to the appropriate threads
for(int i = 0; i < t; i++) {
dupFinders[i].add(stacks[i]);
}
HashTable hashTable = new HashTable();
for(int i = 0; i < t; i++) {
// wait for a chunk stack to become available
Stack<Chunk> tempStack = queue.take();
while(!tempStack.isEmpty) {
tempChunk = tempStack.pop();
for(int i = 0; i < tempChunk.size; i++) {
if(hashTable.contains(tempChunk.chunk[i]) {
return tempChunk.chunk[i]; // duplicate found
} else {
hashTable.add(tempChunk.chunk[i]);
}
}
}
}
return NULL; // no duplicate found
}
}
Nguồn
2014-09-14 06:34:55
Những số này đủ nhỏ (và tất cả> = 0) để vừa với các bit trong một số nguyên ngắn. Không phải là một băm, sau đó, nhưng một * bộ * - và, là một bộ bit, bạn có hiệu quả có thể kiểm tra mỗi mục mới. – usr2564301
Mức độ lớn của mảng và phạm vi của các mục trong mảng là bao nhiêu? Có một mảng số nguyên lớn tùy ý không? Hay nó là một mảng nhỏ các giá trị nhỏ? –
@JimMischel Nó là mảng lớn tùy ý và chúng tôi không thể đủ khả năng để sắp xếp mảng. – CRM