提高效率和速度自动提供算法

时间:2015-01-22 12:27:25

标签: java algorithm autosuggest

在我的项目中,为了插入自动提示功能(也存在像Google这样的小插入错误),我创建了这个类:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Searcher {

private ArrayList<String> titoli;
private boolean multiThread;
private int numeroCore;
private Thread [] threads;
private int indice;
private String [] testoSplittato;
private SortedSet<String> risultati;
private final Runnable runnable;

public Searcher (ArrayList<String> titoli , boolean multiThread) {
    this.titoli = titoli;
    this.multiThread = multiThread;
    indice = 0;
    risultati = new TreeSet<String> ();
    numeroCore = multiThread ? Runtime.getRuntime().availableProcessors() : 1;
    threads = new Thread [numeroCore];
    runnable = new Runnable () {
        @Override
        public void run () {                    
            String prossimo;
            while ((prossimo = getProssimo ()) != null) {
                if (suggerimentoDaAggiungere (prossimo.toLowerCase()))
                    aggiungiRisultato (prossimo);
            }
        }
    };
    for (int i=0;i<threads.length;i++) {
        threads [i] = new Thread (runnable);
    }
}

public Searcher (ArrayList<String> titoli) {
    this (titoli, false);
}

private synchronized void setIndice (int n) {
    indice = n;
}

private synchronized String getProssimo () {
    try {
        return titoli.get(indice++);
    }catch (IndexOutOfBoundsException ioobe) {
        return null;
    }
}

public synchronized void aggiungiRisultato (String s) {
    risultati.add(s);
}

private boolean mancaUnaLettera (String a , String b) {
    int al = a.length() , bl = b.length();
    if (al < 2 || al >= bl)
        return false;

    //a più corta di b
    int primaLetteraSbagliata = -1;
    for (int i=0;i<al;i++) {
        if (a.charAt (i) != b.charAt(i)) {
            primaLetteraSbagliata = i;
            break;
        }
    }

    return primaLetteraSbagliata >= 0 && (b.substring(0, primaLetteraSbagliata) + b.substring(primaLetteraSbagliata + 1)).indexOf(a) >= 0;
}

private boolean unaLetteraDiTroppo (String a , String b) {
    int al = a.length() , bl = b.length();
    if (al < 3 || al - 1 > bl)
        return false;

    //a più corta o stessa lunghezza di b
    int primaLetteraSbagliata = -1;
    for (int i=0;i<bl;i++) {
        if (a.charAt(i) != b.charAt(i)) {
            primaLetteraSbagliata = i;
            break;
        }
    }

    //non sono sicurissimo di qui
    if (primaLetteraSbagliata == -1 && al + 1 == bl)
        return true;

    return primaLetteraSbagliata >= 0 && b.indexOf ((primaLetteraSbagliata > 0 ? a.substring(0 , primaLetteraSbagliata) : "") + (primaLetteraSbagliata + 1 < al ? a.substring(primaLetteraSbagliata + 1) : "") ) >= 0;
}

private boolean unaLetteraSbagliata (String a , String b) {
    int al = a.length() , bl = b.length();
    if (al < 3 || al > bl)
        return false;

    //a ha lunghezza >= di b
    int primaLetteraSbagliata = -1;
    for (int i=0;i<al;i++) {
        if (a.charAt(i) != b.charAt(i)) {
            primaLetteraSbagliata = i;
            break;
        }
    }

    boolean ok = primaLetteraSbagliata + 1 < al;

    return primaLetteraSbagliata >= 0 && (b.substring(0, primaLetteraSbagliata) + (ok ? b.substring(primaLetteraSbagliata + 1) : "")).indexOf(a.substring(0, primaLetteraSbagliata) + (ok ? a.substring(primaLetteraSbagliata + 1) : "")) >= 0;
}

private boolean presenteConErrore (String chiave , String titolo) {
    String [] titoloSplittato = titolo.split(" ");
    if (titolo.indexOf (chiave) >= 0)
        return true;
    for (String s : titoloSplittato)
        if (mancaUnaLettera (chiave , s) || unaLetteraDiTroppo (chiave, s) || unaLetteraSbagliata (chiave, s))
            return true;

    return false;
}

public boolean suggerimentoDaAggiungere (String titolo) {        
    for (String s : testoSplittato) {
        if (!presenteConErrore (s,titolo))
            return false;
    }

    return true;
}

public SortedSet<String> getSuggerimenti (String testo) {
    for (Thread t : threads) {
        if (/*t != null && */t.isAlive()) {
            t.interrupt();
        }
    }
    testoSplittato = testo.toLowerCase ().split (" ");
    risultati.clear();
    if (testoSplittato.length > 0) {
        setIndice (0);
        for (int i=0;i<threads.length;i++) {
            threads [i] = new Thread (runnable);
            threads [i].setPriority(Thread.MAX_PRIORITY);
            threads [i].start();
        }
    }
    for (Thread t : threads)
        try {
            t.join();
        } catch (InterruptedException ex) {
            Logger.getLogger(Searcher.class.getName()).log(Level.SEVERE, null, ex);
        }
    //System.out.println ("FINITO");

    return risultati;                              
}

public static ArrayList<String> getTitoli (String titleFilePath) {
    ArrayList<String> restituire = new ArrayList<String> ();
    try {
        BufferedReader br = new BufferedReader (new InputStreamReader (new FileInputStream (titleFilePath) {}));
        String s;
        while ((s = br.readLine ()) != null) {
            restituire.add (s);
        }
    } catch (Exception ex) {
        Logger.getLogger(Searcher.class.getName()).log(Level.SEVERE, null, ex);
    }

    return restituire;
}

}

对于您来说,可以提高此算法的效率和速度吗?如果是,那怎么可能? PS。我知道存在专用于这些函数的库(如Lucene)但我不能用它们实现带有插入错误的自动提供功能。

1 个答案:

答案 0 :(得分:0)

在不了解细节的情况下,您的代码有两个明显的问题:

  1. 线性时间
  2. 您的代码会循环每个建议。如果可能的建议列表很长,那么迭代每一个项目将会变得无法接受。您可以按某种顺序对建议进行排序和/或切断最大数量的建议。

    1. 争用
    2. 您的并行线程没有缩放。他们都在争夺getProssimo所持的锁定,然后做很少的工作(我想象),所以大多数时候,你不会从多个线程中获得任何好处。通过让线程一次获得批量建议来减少争用。