简体   繁体   中英

How can I loop through n-dimensional sparse matrix (created by own class)?

I have two classes

class CSparseMatrix:
{
            public int NumberOfDimensions { get; set;}
            public int DefaultNumber { get; set; }
            List<int> dimensionsRanges = new List<int>(); // that's specify dimension of ranges, f.e. {100, 100, 100, 120..}

            List<CSparseCell> cells = new List<CSparseCell>(); // contains only values different from default for this matrix
}
class CSparseCell {
            public int Value { get; set; }
            public List<int> coordinates = new List<int>();
}

And the problem is: how to loop through this CSparseMatrix and output all ranges with values in format like: [0, 0, 0, 0] - *value*, [0, 0, 0, 1] - *value*, [0, 0, 0, 2] - *value*, ...[dimensionsRanges[0]-1, dimensionsRanges[1]-1, dimensionsRanges[2]-1, dimensionsRanges[3]-1] - *value* so through all ranges and output all values (we can have any number of this dimensions). That means that in program we had to to output all values of matrix, which can have any number of dimensions and ranges could be different. But we don't know what this number of dimensions will be so can't use n-nested loops, and actually I have no idea of algorithm or method how to iterate through all values from List<int> dimensionsRanges

The way, we get the specific value from this "matrix" is

            public int GetValueFromCell(List<int> coordinate)
            {
                foreach(CSparseCell cell in cells)
                {
                    if(cell.coordinates.All(coordinate.Contains)) {
                        return cell.Value;
                    }
                }
                return DefaultNumber;
            }

Since you say your matrix is large, avoid returning CSparseCell by pushing the Value lookup to the answer computation.

You create a method to return all the coordinates in the sparse matrix, using a helper method to increment coordinates. NOTE: I changed the coordinate increment method to use a for loop instead of do which might be easier to understand (?).

void IncCoord(ref List<int> aCoord) { // ref not needed, just for documentation
    for (var curDim = NumberOfDimensions - 1; curDim >= 0; --curDim) {
        if (aCoord[curDim] == dimensionsRanges[curDim] - 1) // handle carry
            aCoord[curDim] = 0;
        else {
             ++aCoord[curDim];
            break;
        }
   }
}

public IEnumerable<List<int>> AllCoords() {
    var curCellCoord = Enumerable.Repeat(0, NumberOfDimensions).ToList();
    var numCells = dimensionsRanges.Product();
    for (int j1 = 0; j1 < numCells; ++j1) {
        yield return curCellCoord.ToList();
        IncCoord(ref curCellCoord);
    }
}

Now you can use this method to get all Value s and you can format the output however you like. Assuming t is a CSparseMatrix :

var ans = t.AllCoords().Select(c => $"[{c.Join(",")}] - {t.GetValueFromCell(c)}");

A couple of helper extension methods are used:

public static class IEnumerableExt {
    public static int Product(this IEnumerable<int> src) => src.Aggregate(1, (a,n) => a * n);
}

public static class StringExt {
    public static string Join<T>(this IEnumerable<T> items, string sep) => String.Join(sep, items);
}

I'd make a couple suggestions. First tying the coordinate and the value together complicate things. It'd be much better to separate those two.

Secondly, having to search the entire array to find a single cell makes it very inefficient. You can create a simple sparse matrix out of a dictionary. This not at all the best way but with the unknown key requirements you have given it's at least better than the array. (is it really a matrix if the key is 1..n ?)

Here's an example. First we make the coordinates their own type.

readonly struct SparseCoord : IEquatable<SparseCoord>
{
    public static SparseCoord ToCoordinate(params int[] coords)
    {
        return new SparseCoord(coords);
    }

    public SparseCoord(int[] c)
    {
        this.Coordinate = new int[c?.Length ?? 0];

        if (null != c)
            c.CopyTo(this.Coordinate, 0);
    }

    public int[] Coordinate { get; }

    public bool Equals([AllowNull] SparseCoord other)
    {
        return Enumerable.SequenceEqual(this.Coordinate, other.Coordinate);
    }

    public override bool Equals(object obj)
    {
        if (obj is SparseCoord c)
            return this.Equals(c);

        return false;
    }

    public override int GetHashCode()
    {
        var hash = new HashCode();
        foreach (var i in this.Coordinate)
            hash.Add(i);

        return hash.ToHashCode();
    }

    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append('(');
        foreach (var i in this.Coordinate)
        {
            sb.Append(i);
            sb.Append(',');
        }
        sb.Length = sb.Length - 1;
        sb.Append(')');
        return sb.ToString();
    }
}

Then we create a sparse matrix using a dictionary as the storage. Note this is incomplete obviously as there's no way to clear it partially or completely but again it's just an example.

class SparseMatrix : IEnumerable<KeyValuePair<SparseCoord, int>>
{
    Dictionary<SparseCoord, int> cells = new Dictionary<SparseCoord, int>();

    public int this[SparseCoord coord]
    {
        get
        {
            if (this.cells.TryGetValue(coord, out var ret))
                return ret;
            return 0;
        }
        set
        {
            this.cells[coord] = value;
        }
    }

    public IEnumerator<KeyValuePair<SparseCoord, int>> GetEnumerator()
    {
        return this.cells.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

Then you can add values and iterate over the contents:

    static void Main(string[] _)
    {
        SparseMatrix matrix = new SparseMatrix();

        matrix[SparseCoord.ToCoordinate(1, 2, 3)] = 1;
        matrix[SparseCoord.ToCoordinate(5, 6, 7)] = 2;

        foreach (var value in matrix)
            Console.WriteLine(value);
    }

Finally I would reiterate that if your matrix really is going to get "big" as you said in a comment then you should invest some time in looking at some well known implementations of sparse matrix.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM