Wenn ich Ihr Problem richtig verstehe, wollen Sie Dinge berechnen wie log { P("x1 x2 x3 x4 x5") / P("x1") P("x2") ... P("x5") }, wobei P die Wahrscheinlichkeit misst, dass ein bestimmtes 5-Gramm oder 1-Gramm eine bestimmte Sache ist (und im Grunde ein Verhältnis der Anzahlen ist, vielleicht mit Laplace-ähnlichen Offsets). Machen Sie also einen einzigen Durchgang durch Ihren Korpus und speichern Sie die Zählungen von (1) jedem 1-Gramm, (2) jedem n-Gramm (verwenden Sie für letzteres ein Diktat), und dann führen Sie für jedes externe n-Gramm ein paar Diktatabfragen durch, ein bisschen Arithmetik, und schon sind Sie fertig. Ein Durchlauf durch den Korpus zu Beginn, dann ein fester Arbeitsaufwand pro externem n-Gramm.
(Anmerkung: Eigentlich bin ich mir nicht sicher, wie man PMI für mehr als zwei Zufallsvariablen definiert; vielleicht ist es so etwas wie log P(a)P(b)P(c)P(abc) / P(ab)P(bc)P(a_c). Aber wenn es irgendetwas in dieser Richtung ist, können Sie es auf dieselbe Weise machen: Sie iterieren durch Ihren Korpus und zählen viele Dinge, und dann sind alle Wahrscheinlichkeiten, die Sie brauchen, einfach Verhältnisse der Zählungen, vielleicht mit Laplace-artigen Korrekturen).
Wenn Ihr Korpus so groß ist, dass Sie das n-Gramm-Diktat nicht im Speicher unterbringen können, dann teilen Sie es in speichergroße Stücke auf, berechnen Sie n-Gramm-Diktate für jedes Stück und speichern Sie sie auf der Festplatte in einer Form, die es Ihnen ermöglicht, jeden beliebigen n-Gramm-Eintrag einigermaßen effizient zu erreichen; gehen Sie dann für jedes externe n-Gramm die Stücke durch und addieren Sie die Zählungen.
Welche Form? Das bleibt Ihnen überlassen. Eine einfache Möglichkeit: in lexikographischer Reihenfolge des n-Grams (Anmerkung: wenn Sie mit Wörtern statt mit Buchstaben arbeiten, sollten Sie damit beginnen, Wörter in Zahlen umzuwandeln; dazu benötigen Sie einen einzigen Vorlauf über Ihren Korpus); dann ist das Finden des gewünschten n-Grams eine binäre Suche oder etwas in der Art, was bei Chunks von 1 GB Größe etwa 15-20 Suchvorgänge pro Chunk bedeuten würde; Sie könnten etwas zusätzliche Indizierung hinzufügen, um dies zu reduzieren. Oder: Verwenden Sie eine Hash-Tabelle auf der Festplatte, mit Berkeley DB oder ähnlichem; in diesem Fall können Sie auf das Chunking verzichten. Oder, wenn das Alphabet klein ist (z.B. wenn es sich eher um Buchstaben-N-Gramme als um Wort-N-Gramme handelt und Sie einfachen englischen Text verarbeiten), speichern Sie sie einfach in einem großen Array, mit direktem Nachschlagen -- aber in diesem Fall können Sie das Ganze wahrscheinlich sowieso im Speicher unterbringen.