我有一个带有两个值(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++;
}
}
}
答案 0 :(得分:1)
如果您自己实现矩阵类,则实际上有多种选择:
public class BoleanMatrix
{
public bool this[int i, int j] {get;set;}
}
将其缓存。这简单。进行任何修改后,只需更新高位的缓存值即可。实施是无关紧要的。
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
}
}
}
将实现更改为任何稀疏变体,例如,您可以将矩阵值存储为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;
}
}
);