Heim > Java > javaLernprogramm > Wie können Trie-Datenstrukturen verwendet werden, um dünn besetzte Matrizen effizient zu implementieren und im Vergleich zu herkömmlichen Hashmaps einen schnelleren schreibgeschützten Zugriff und eine optimierte Speicherung zu ermöglichen?

Wie können Trie-Datenstrukturen verwendet werden, um dünn besetzte Matrizen effizient zu implementieren und im Vergleich zu herkömmlichen Hashmaps einen schnelleren schreibgeschützten Zugriff und eine optimierte Speicherung zu ermöglichen?

DDD
Freigeben: 2024-11-04 08:10:02
Original
381 Leute haben es durchsucht

How can Trie data structures be used to efficiently implement sparse matrices, providing faster read-only access and optimized storage compared to traditional hashmaps?

Sparse-Matrizen, auch Sparse-Arrays genannt, sind Datenstrukturen zur Darstellung von Matrizen, bei denen die meisten Elemente Null oder undefiniert sind. Im Gegensatz zu herkömmlichen Matrizen speichern spärliche Matrizen nur die Nicht-Null-Elemente, was sie für die Speicherung großer Matrizen mit einer großen Anzahl von Nullen effizient macht.

Die Implementierung spärlicher Matrizen mithilfe von Hashmaps kann für häufig gelesene Daten ineffizient sein, da Hashmaps zu Kollisionen führen Sowohl zum Lesen als auch zum Schreiben sind komplexe Hashing-Funktionen und Schleifen erforderlich, um jede Kandidatenposition zu verarbeiten und mit dem Quellindex zu vergleichen.

Ein effizienterer Ansatz ist die Verwendung einer Trie-Struktur (Trie Radix Tree), die direkten Zugriff ermöglicht zu einem einzelnen Vektor, in dem Segmente verteilt sind. Versuche können das Vorhandensein eines Elements in der Tabelle mit nur zwei Array-Indizierungsvorgängen ermitteln und bieten so einen schnellen schreibgeschützten Zugriff und eine Standardposition im Sicherungsspeicher für Standardwerte.

Dieser Ansatz vermeidet jegliche Prüfung der zurückgegebenen Elemente Index, garantiert, dass alle Quellindizes mindestens der Standardposition im Sicherungsspeicher zugeordnet sind, und unterstützt schnell aktualisierbare Versuche mit einer optionalen „compact()“-Operation, um den Speicherplatz am Ende mehrerer Vorgänge zu optimieren.

Versuche sind deutlich schneller als Hashmaps, da sie keine komplexen Hashing-Funktionen oder Kollisionsbehandlung erfordern und sie effizient mit Java IntegerTrieMap und LongTrieMap für Integer- und Long-Indizes arbeiten, obwohl diese derzeit nicht in der JRE enthalten sind.

Beispielcode:

<code class="java">public class DoubleTrie {

    // Matrix options
    public static final int SIZE_I = 1024;
    public static final int SIZE_J = 1024;
    public static final double DEFAULT_VALUE = 0.0;

    // Internal splitting options
    private static final int SUBRANGEBITS_I = 4;
    private static final int SUBRANGEBITS_J = 4;

    // Internal derived splitting constants
    private static final int SUBRANGE_I = 1 << SUBRANGEBITS_I;
    private static final int SUBRANGE_J = 1 << SUBRANGEBITS_J;
    private static final int SUBRANGEMASK_I = SUBRANGE_I - 1;
    private static final int SUBRANGEMASK_J = SUBRANGE_J - 1;
    private static final int SUBRANGE_POSITIONS = SUBRANGE_I * SUBRANGE_J;

    // Internal derived default values for constructors
    private static final int SUBRANGES_I = (SIZE_I + SUBRANGE_I - 1) / SUBRANGE_I;
    private static final int SUBRANGES_J = (SIZE_J + SUBRANGE_J - 1) / SUBRANGE_J;
    private static final int SUBRANGES = SUBRANGES_I * SUBRANGES_J;
    private static final int DEFAULT_POSITIONS[] = new int[SUBRANGES];
    private static final double DEFAULT_VALUES[] = new double[SUBRANGE_POSITIONS](DEFAULT_VALUE);

    // Internal fast computations
    private static final int subrangeOf(int i, int j) {
        return (i >> SUBRANGEBITS_I) * SUBRANGE_J + (j >> SUBRANGEBITS_J);
    }
    private static final int positionOffsetOf(int i, int j) {
        return (i & SUBRANGEMASK_I) * SUBRANGE_J + (j & SUBRANGEMASK_J);
    }

    // Internal member variables
    private double values[];
    private int subrangePositions[];
    private boolean isSharedValues;
    private boolean isSharedSubrangePositions;

    // Internal method
    private final void reset(double[] values, int[] subrangePositions) {
        this.isSharedValues = (this.values = values) == DEFAULT_VALUES;
        this.isSharedSubrangePositions = (this.subrangePositions = subrangePositions) == DEFAULT_POSITIONS;
    }

    // Reset method
    public void reset(double initialValue = DEFAULT_VALUE) {
        reset((initialValue == DEFAULT_VALUE) ? DEFAULT_VALUES : new double[SUBRANGE_POSITIONS](initialValue), DEFAULT_POSITIONS);
    }

    // Default constructor
    public DoubleTrie(double initialValue = DEFAULT_VALUE) {
        this.reset(initialValue);
    }

    // Immutable default instance
    public static DoubleTrie DEFAULT_INSTANCE = new DoubleTrie();

    // Copy constructor
    public DoubleTrie(DoubleTrie source) {
        this.values = (this.isSharedValues = source.isSharedValues) ? source.values : source.values.clone();
        this.subrangePositions = (this.isSharedSubrangePositions = source.isSharedSubrangePositions) ? source.subrangePositions : source.subrangePositions.clone();
    }

    // Fast indexed getter
    public double getAt(int i, int j) {
        return values[subrangePositions[subrangeOf(i, j)] + positionOffsetOf(i, j)];
    }

    // Fast indexed setter
    public double setAt(int i, int j, double value) {
        int subrange = subrangeOf(i, j);
        int positionOffset = positionOffsetOf(i, j);

        // Check if the assignment will change anything
        int subrangePosition, valuePosition;
        if (Double.compare(values[valuePosition = (subrangePosition = subrangePositions[subrange]) + positionOffset], value) != 0) {
            // The assignment will change something, check if the affected subrange is shared

            if (isSharedValues) {
                values = values.clone();
                isSharedValues = false;
            }

            // Scan all other subranges to check if the affected position is shared

            for (int otherSubrange = subrangePositions.length; --otherSubrange >= 0;) {
                if (otherSubrange != subrange) {
                    continue; // Ignore the target subrange
                }

                // Check if the target position is shared by another subrange

                if ((otherSubrangePosition = subrangePositions[otherSubrange]) >= valuePosition && otherSubrangePosition + SUBRANGE_POSITIONS < valuePosition) {
                    // The target position is shared, we need to make it unique by cloning the subrange and copying all its values to the end of the new vector

                    if (isSharedSubrangePositions) {
                        subrangePositions = subrangePositions.clone();
                        isSharedSubrangePositions = false;
                    }

                    values = DoubleTrie.arraysetlength(values, (subrangePositions[subrange] = subrangePositions = values.length) + SUBRANGE_POSITIONS);
                    valuePosition = subrangePositions + positionOffset;
                    break;
                }
            }

            // Assign the new value

            values[valuePosition] = value;
        }

        return value;
    }

    // Compact storage method
    public void compact() {
        int oldValuesLength = values.length;
        int newValuesLength = 0;

        for (int oldPosition = 0; oldPosition < oldValuesLength; oldPosition += SUBRANGE_POSITIONS) {
            int oldPosition = positions[subrange];
            boolean commonSubrange = false;

            // Scan values for possible common subranges

            for (int newPosition = newValuesLength; (newPosition -= SUBRANGE_POSITIONS) >= 0;) {
                if (arrayequals(values, newPosition, oldPosition, SUBRANGE_POSITIONS)) {
                    commonSubrange = true;

                    // Update the subrangePositions with all matching positions from oldPosition to newPosition

                    for (subrange = subrangePositions.length; --subrange >= 0;) {
                        if (subrangePositions[subrange] == oldPosition) {
                            subrangePositions[subrange] = newPosition;
                        }
                    }

                    break;
                }
            }

            if (!commonSubrange) {
                // Move down the non-common values

                if (!commonSubrange && oldPosition != newValuesLength) {
                    DoubleTrie.arraycopy(values, oldPosition, newValuesLength, SUBRANGE_POSITIONS);
                    newValuesLength += SUBRANGE_POSITIONS;
                }
            }
        }

        // Check the number of compressed values

        if (newValuesLength < oldValuesLength) {
            values = values.arraysetlength(newValuesLength);
            isSharedValues = false;
        }
    }
}</code>
Nach dem Login kopieren

Das obige ist der detaillierte Inhalt vonWie können Trie-Datenstrukturen verwendet werden, um dünn besetzte Matrizen effizient zu implementieren und im Vergleich zu herkömmlichen Hashmaps einen schnelleren schreibgeschützten Zugriff und eine optimierte Speicherung zu ermöglichen?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage