![]() |
Rechnen mit Sparse Vectoren
Ich habe eine Klasse zum arbeiten mit Sparse Vectoren geschrieben.
Gespeichert wird ein Vector, bei dem jedem Index ein Double zugeordnet ist. Die Null-Werte werden nicht gespeichert.... Es gibt ein Bitarray in dem durch eine 1 angezeigt wird, daß ein Wert vorhanden ist, eine 0 zeigt an das kein Wert, also eine 0 vorhanden ist.
Delphi-Quellcode:
Ich suche nach einer performanten Methode zwei Vectoren zu addieren. Bis jetzt sieht die Methode so aus:
TSparseVector = class
private // Actual Capacity of the BitMatrix: fBitCapacity: Integer; // An Array holding the bits for the sparse Vector fBitMatrix: PBitArray; // Maximum Count fCount: Integer; // The Values (not containing Zero's) fValues: PDoubleArray; fValuesCapacity: Integer;
Delphi-Quellcode:
Eine Idee war, den Vektor auf den addiert werden soll in einen "Dense" Vektor zu verwandeln, also einen kompletten Vektor inklusive der Nullen zu haben. Das war mir jedoch zu langsam.
procedure TSparseVector.AddVector(const aSparseVector: TSparseVector);
var a_Item: Double; a_LowestIndex: Integer; a_Index: Integer; a_OwnIndex: Integer; a_NewCount: integer; a_MaxNonZero: Integer; i: Integer; a_DoubleArray: PDoubleArray; a_SparseVector: TSparseVector; begin // Als erstes feststellen, ob der eigene Vektor leer ist, wenn ja wird einfach eine Speicher-kopie des anderen Vektors gemacht if IsNullVector then begin if not (aSparseVector.IsNullVector) then InnerCopyWholeVector(aSparseVector) end else if not (aSparseVector.IsNullVector) then begin Assert(Count = aSparseVector.Count); GetMem(a_DoubleArray, fCount * 8); //:= getDenseVector; // own Vector as Dense Vector a_SparseVector := GetImplementingObject(aSparseVector) as TSparseVector; // a_LowestIndex := a_SparseVector.getLowestIndex; a_Index := 0; a_OwnIndex := 0; a_NewCount := 0; a_MaxNonZero := 0; for i := 0 to fCount - 1 do begin if GetBitValue(i) then begin a_Item := fValues^[a_OwnIndex]; inc(a_OwnIndex); if a_SparseVector.GetBitValue(i) then begin a_DoubleArray^[i] := a_Item + a_SparseVector.fValues^[a_Index]; inc(a_Index); inc(a_NewCount); a_MaxNonZero := i; end else begin a_DoubleArray^[i] := a_Item; inc(a_NewCount); a_MaxNonZero := i; end; end else // First Vector not set: begin if a_SparseVector.GetBitValue(i) then begin a_DoubleArray^[i] := a_SparseVector.fValues^[a_Index]; inc(a_Index); inc(a_NewCount); a_MaxNonZero := i; end else a_DoubleArray^[i] := 0; end; end; // for System.ReallocMem(fValues, a_NewCount * 8); fValuesCapacity := a_NewCount; SetCapacityBitMatrix((fCount and $FFFFFFC0) + 64); FillChar(fBitMatrix^, fBitCapacity div 8, 0); try a_LowestIndex := GetLowestNonZeroIndexForDoubleArray(a_DoubleArray); a_Index := 0; for i := a_LowestIndex to a_MaxNonZero do begin if a_DoubleArray^[i] <> 0 then begin fValues^[a_Index] := a_DoubleArray^[i]; inc(a_Index); SetBit(i); end; end; // for fValuesCount := a_Index; finally FreeMem(a_DoubleArray); end; end; end; Die jetztige Lösung ist immer noch 'suboptimal', ich versuche mal die Idee zu beschreiben:
Delphi-Quellcode:
Nach der For Schleife wird ein neuer Sparsevector aus dem DoubleArray gebaut, der neue Vektor enthält anschließend keine Nullen mehr.
for i := 0 to fCount - 1 do
begin // Wenn ein Wert gesetzt ist if GetBitValue(i) then begin a_Item := fValues^[a_OwnIndex]; inc(a_OwnIndex); // Schaue nach ob anderer Wert gesetzt ist if a_SparseVector.GetBitValue(i) then begin // Addiere beide Werte und speicher im DoubleArray a_DoubleArray^[i] := a_Item + a_SparseVector.fValues^[a_Index]; inc(a_Index); inc(a_NewCount); a_MaxNonZero := i; end else begin // ansonsten: nimm ursprünglichen Wert a_DoubleArray^[i] := a_Item; inc(a_NewCount); a_MaxNonZero := i; end; end else // First Vector not set: begin /7 Erster Wert nicht gesetzt, also evtl zweiten Wert nehmen if a_SparseVector.GetBitValue(i) then begin a_DoubleArray^[i] := a_SparseVector.fValues^[a_Index]; inc(a_Index); inc(a_NewCount); a_MaxNonZero := i; end else // kein Wert gesetzt - Neuer Wert ist 0 a_DoubleArray^[i] := 0; end; Ich hatte ursprünglich überlegt, die Bitmatrixen driekt zu verknüpfen und anschließend die Double's zu addieren, mir erschien das nur zu kompliziert. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:51 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz