2017-12-13 13 views
1

2つの単語を含む行の数が必要です。この目的のために、私は次のコードを書いています: 入力ファイルは1000 linesと約4,000 wordsを含み、約4時間かかります。 Javaにライブラリがありますが、それはより速く行うことができますか? 実行時間を短縮するために、Appache LuceneまたはStanford Core NLPを使用してこのコードを実装できますか?明確な言葉を取得Java 8を使用するファイル内の2つの単語の確率分布

ArrayList<String> reviews = new ArrayList<String>(); 
ArrayList<String> terms = new ArrayList<String>(); 
Map<String,Double> pij = new HashMap<String,Double>(); 

BufferedReader br = null; 
FileReader fr = null; 
try 
    { 
     fr = new FileReader("src/reviews-preprocessing.txt"); 
      br = new BufferedReader(fr); 
      String line; 
      while ((line= br.readLine()) != null) 
      { 
      for(String term : line.split(" ")) 
       { 
        if(!terms.contains(term)) 
         terms.add(term); 
       } 
       reviews.add(line); 
      } 
     } 
     catch (IOException e) { e.printStackTrace();} 
     finally 
     { 
      try 
      { 
       if (br != null) 
        br.close(); 
       if (fr != null) 
        fr.close(); 
      } 
      catch (IOException ex) { ex.printStackTrace();}  
    } 
long Count = reviews.size(); 
for(String term_i : terms) 
    { 
     for(String term_j : terms) 
      { 
       if(!term_i.equals(term_j)) 
       { 
        double p = (double) reviews.parallelStream().filter(s -> s.contains(term_i) && s.contains(term_j)).count(); 
        String key = String.format("%s_%s", term_i,term_j); 
        pij.put(key, p/Count); 
       } 
      } 
    } 
+2

ライブラリは魔法ではありません。ライブラリを使用していないためにコードが遅くなることはありません。別のストリーム操作を含む2つのネストされたループを使用しているため、速度が遅いです。つまり、 'term.size()'× 'term.size()'× 'reviews.size()'の操作です。 – Holger

+1

そうですが、これは避けられません。だから私は、ParllelStreamを使うのではなく、より高速なメソッドを使うことができると考えました。 @Holger –

+3

それは避けられないことです。それはアルゴリズムを開発する技術です。それは私たちが非常に多くの異なるソートアルゴリズムを知っている理由です。同じタスクを解決するにはさまざまな方法がありますが、より良い方法がないとは決して想像できません。 – Holger

答えて

6

あなたの最初のループではなくSetを使用しての、線形時間複雑性を有するArrayList.contains、に依存しています。したがって、ndの異なる単語を仮定すると、それは既に "行数ndの時間複雑さを有する。

その後、あなたはND×ND単語の組み合わせを作成し、これらの組み合わせの存在のために、すべての千行を探査しています。言い換えれば、100個の別個の単語だけを仮定すると、1,000×100 + 100×100×1,000 = 10,100,000回の操作を実行しています。

代わりに、実際に行内に存在する組み合わせを作成し、マップに収集するだけで済みます。これは実際に存在する組み合わせのみを処理し、両方の確率が同じであるため、それぞれの "a_b"/"b_a"のどちらかの組み合わせをチェックすることでこれを改善することができます。次に、あなただけの×「行あたり単語」×「行あたり単語」あなたの場合の動作、つまり、およそ16,000の操作「行の番号」を行っています。

以下のメソッドは、 "a_b"/"b_a"の組み合わせの1つのみを保持する行のすべての単語を結合し、各組み合わせを1行として数えることができるように重複を排除します。

static Stream<String> allCombinations(String line) { 
    String[] words = line.split(" "); 
    return Arrays.stream(words) 
     .flatMap(word1 -> 
      Arrays.stream(words) 
        .filter(words2 -> word1.compareTo(words2)<0) 
        .map(word2 -> word1+'_'+word2)) 
     .distinct(); 
} 

この方法では、それは、並列処理を行うためのあらゆる試みを必要とせずに、数秒以内に「戦争と平和」の私のコピーを駆け抜けた

List<String> lines = Files.readAllLines(Paths.get("src/reviews-preprocessing.txt")); 
double ratio = 1.0/lines.size(); 
Map<String, Double> pij = lines.stream() 
     .flatMap(line -> allCombinations(line)) 
     .collect(Collectors.groupingBy(Function.identity(), 
             Collectors.summingDouble(x->ratio))); 

のような使用することができます。それほど驚くべきことではないが、「and_the」は最も高い確率を有する組み合わせであった。

あなたは、異なる入力で動作するようにコードを一般化する

String[] words = line.toLowerCase().split("\\W+"); 

にライン

String[] words = line.split(" "); 

を変更する複数の空白や句読点の文字を処理し、ケースを無視して考えることができます。

+2

実際の本の名前は '!= war && == peace'を定義していない単語のように' '戦争と人道(惑星、光、地球)'などであるべきだという意見があります。もともとは 'мiръ'('!= peace')と書かれていました。これは、最初の印刷された本の誤字、または!=平和な単語のいずれかと見なされますが、それでも名前はおそらく "戦争と平和"として残されます。 – Eugene

+2

@Eugene:私は、革命前の世界と平和の言葉。私は「мир」という意味だけを知っていました。しかし、とにかく、あなたの前提は正しかった、私たちはよく知られている名前を使って、読者が私たちが何を話しているかを知っていることを確かめる... – Holger

関連する問題