如何计算矩阵中的非零元素?

时间:2019-04-17 09:36:15

标签: c# .net matrix

我有一个带有两个值(0,1)的矩阵,我必须计算该矩阵中“ 1”的数量,因此我尝试检查所有元素,但对于[1000,1000]矩阵,它花费的时间太长,另一个问题是,我应该针对不同的矩阵多次执行此操作,所以我希望任何人都可以通过更快的模式来帮助我。

这是我的代码:

for (int i = 0; i < matrix.height; i++)
{
    for (int j = 0; j < matrix.width; j++)
    {
        if (matrix[j, i] == 1)
        {
            count++;
        }
    }
}

3 个答案:

答案 0 :(得分:1)

如果您自己实现矩阵类,则实际上有多种选择:

public class BoleanMatrix
{
     public bool this[int i, int j] {get;set;}
}
  1. 将其缓存。这简单。进行任何修改后,只需更新高位的缓存值即可。实施是无关紧要的。

    public class BoleanMatrix
    {
        private int _highBitCount = 0;
        public bool this[int i, int j] 
        {
            get;
            set
            {
                if(prev != value)
                { 
                    if(value)
                        _highBitCount++;
                    else
                        _highBitCount--;
                }
                //set here
             }
        }
    }
    
  2. 将实现更改为任何稀疏变体,例如,您可以将矩阵值存储为byte []数组中的位。如果仍然太多,请使用运行长度编码对其进行压缩。它具有诸如这些矩阵的更新和分发问题之类的缺点,通常它们比内存范围的矩阵要慢得多。高效的算法在很大程度上取决于矩阵的性质(值的分布)以及如何使用它们(乘法,除法,减法等)。

答案 1 :(得分:0)

鉴于您仅存储位,可以通过存储打包到uint值中的位来提高存储使用率,与使用{{1}相比,这将使所需的空间量减少32倍}。

如果这样做,则还可以通过使用许多不同的"Hamming Weight"算法之一来更有效地计算置位位数。

此方法的缺点是使用数组int索引器访问单个位可能较慢,但是设置位数量的计算要快得多(对于RELEASE模式,则快90倍以上)在我的PC上构建。)

这是示例代码;重要的类是BitMatrix

BitMatrix

如果您运行的是64位代码,那么使用using System; using System.Diagnostics; namespace Demo { class Program { static void Main() { int[,] matrix = new int[1000, 1000]; BitMatrix bitMatrix = new BitMatrix(1000, 1000); // Randomly populate matrices and calculate expected count. var rng = new Random(985912); int expected = 0; for (int r = 0; r < 1000; ++r) { for (int c = 0; c < 1000; ++c) { if ((rng.Next() & 1) == 0) continue; ++expected; matrix[r, c] = 1; bitMatrix[r, c] = true; } } Console.WriteLine("Expected = " + expected); // Time the explicit matrix loop. var sw = Stopwatch.StartNew(); for (int i = 0; i < 1000; ++i) if (count1(matrix) != expected) Console.WriteLine("count1() failed"); var elapsed1 = sw.ElapsedTicks; Console.WriteLine(sw.Elapsed); // Time the hamming weight approach. sw.Restart(); for (int i = 0; i < 1000; ++i) if (bitMatrix.NumSetBits() != expected) Console.WriteLine("NumSetBits() failed"); var elapsed2 = sw.ElapsedTicks; Console.WriteLine(sw.Elapsed); Console.WriteLine("BitMatrix matrix is " + elapsed1 / elapsed2 + " times faster"); } static int count1(int[,] matrix) { int h = 1 + matrix.GetUpperBound(0); int w = 1 + matrix.GetUpperBound(1); int c = 0; for (int i = 0; i < h; ++i) for (int j = 0; j < w; ++j) if (matrix[i, j] == 1) ++c; return c; } } public sealed class BitMatrix { public BitMatrix(int rows, int cols) { Rows = rows; Cols = cols; bits = new uint[(rows*cols+31)/32]; } public int Rows { get; } public int Cols { get; } public int NumSetBits() { int count = 0; foreach (uint i in bits) count += hammingWeight(i); return count; } public bool this[int row, int col] { get { int n = row * Cols + col; int i = n / 32; int j = n % 32; uint m = 1u << j; return (bits[i] & m) != 0; } set { int n = row * Cols + col; int i = n / 32; int j = n % 32; uint m = 1u << j; if (value) bits[i] |= m; else bits[i] &= ~m; } } static int hammingWeight(uint i) { i = i - ((i >> 1) & 0x55555555); i = (i & 0x33333333) + ((i >> 2) & 0x33333333); return (int)((((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24); } readonly uint[] bits; } } 数组并计算64位汉明权重实际上会更有效。

当我在PC上尝试该操作时,速度快了120倍以上。

这是ulong的64位版本:

BitMatrix

观察:事实证明,在public sealed class BitMatrix { public BitMatrix(int rows, int cols) { Rows = rows; Cols = cols; bits = new ulong[(rows*cols+63)/64]; } public int Rows { get; } public int Cols { get; } public int NumSetBits() { int count = 0; foreach (ulong i in bits) count += hammingWeight(i); return count; } public bool this[int row, int col] { get { int n = row * Cols + col; int i = n / 64; int j = n % 64; ulong m = 1ul << j; return (bits[i] & m) != 0; } set { int n = row * Cols + col; int i = n / 64; int j = n % 64; ulong m = 1ul << j; if (value) bits[i] |= m; else bits[i] &= ~m; } } static int hammingWeight(ulong i) { i = i - ((i >> 1) & 0x5555555555555555UL); i = (i & 0x3333333333333333UL) + ((i >> 2) & 0x3333333333333333UL); return (int)(unchecked(((i + (i >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56); } readonly ulong[] bits; } 的循环中使用for()foreach快一些,例如:

NumSetBits()

在我的PC上,这将性能从120倍提高到130倍。

最后:如果您想利用多线程,可以这样做(请注意使用public int NumSetBits() { int count = 0; for (var index = 0; index < bits.Length; index++) count += hammingWeight(bits[index]); return count; } -这是为了增加每个线程计算出的数据的块大小,以使其更多更有效率):

Partitioner

有了这一更改,汉明方法几乎快了200倍(对于2000x2000矩阵快了300倍),但请注意,更快的数量取决于设置的1位的比例。

答案 2 :(得分:0)

尝试并行for循环?如果无法缓存。

        object mylock = new object();
        int count = 0;

        Parallel.For(0, matrix.height, i =>
        {
            int forcount = 0;

            for (int j = 0; j < matrix.width; j++)
            {
                if (matrix[j, i] == 1)
                {
                    forcount++;
                }
            }

            lock (mylock)
            {
                count += forcount;
            }
        }
        );