RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 562575
Accepted
Xambey
Xambey
Asked:2020-09-04 05:06:31 +0000 UTC2020-09-04 05:06:31 +0000 UTC 2020-09-04 05:06:31 +0000 UTC

C#矩阵实现[重复]

  • 772
这个问题已经在这里得到回答:
如何编写一个适用于所有数字类型的方法/类? (3 个答案)
3 年前关闭。

所以我决定从C++转向C#,遇到了这样的问题。据我了解,Sharpe 中没有模板本身,这是由接口和通用类补偿的。问题:有没有办法让编译器清楚Type是数字?使用标准类型运算符,否则无法实现加法等。矩阵。或者还有其他解决方案吗?这是他所做的:

public class Matrix<Type> /*where Type ...*/ 
{
       private List<List<Type>> MatrixValue;

       public Matrix(int rows, int cols)
       {
           MatrixValue = new List<List<Type>>(rows);
           foreach (var item in MatrixValue)
           {
               item.Capacity = cols;
           }
       }
       public Matrix()
       {
           MatrixValue = new List<List<Type>>();
       }
       public static Matrix<Type> operator +(Matrix<Type> left, Matrix<Type> right)
       {
           Matrix<Type> result = new Matrix<Type>(left.countRows(),   left.countColums());
           for (int i = 0; i < left.countRows(); i++)
           {
               for (int j = 0; j < left.countColums(); j++)
               {
                   result.set(left.get(i, j) + right.get(i, j); // ОШИБКА!
               }
           }
           return ;
       }
  ///...
}
c#
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Pavel Mayorov
    2020-09-04T19:22:50Z2020-09-04T19:22:50Z

    这是你可以尝试的方法:

    class Matrix<T> {
      static readonly Func<T, T, T> add, sub, mul;
    
      static Matrix() {
        var a = Expression.Parameter(typeof(T));
        var b = Expression.Parameter(typeof(T));
    
        add = Expression.Lambda<Func<T, T, T>>(Expression.Add(a, b), a, b).Compile();
        sub = Expression.Lambda<Func<T, T, T>>(Expression.Subtract(a, b), a, b).Compile();
        mul = Expression.Lambda<Func<T, T, T>>(Expression.Multiply(a, b), a, b).Compile();
      }
    
      // использование:
      // var r = add(left.get(i, j), right.get(i, j));
    }
    

    应该比动态快 - 但比正常添加慢。


    您可以通过将整个算法填充到表达式中来获得更多时间:

    class Matrix2<T>
    {
        delegate void MatrixOperation (int lb0, int ub0, int lb1, int ub1, T[,] a, T[,] b, T[,] c);
        static readonly MatrixOperation add;
    
        static Expression MakeRangeFor(Expression min, Expression max, Func<Expression, Expression> body) {
            var i = Expression.Variable(min.Type);
            var exit = Expression.Label();
            return Expression.Block(new[]{ i }, 
              Expression.Assign(i, min),
              Expression.Loop(
                Expression.Block(
                    body(i),
                    Expression.Condition(
                        Expression.LessThanOrEqual(Expression.PreIncrementAssign(i), max),
                        Expression.Empty(),
                        Expression.Break(exit)
                    )
                ),
                exit
              )
            );
        }
    
        static Matrix2()
        {
            var lb0 = Expression.Parameter(typeof(int));
            var ub0 = Expression.Parameter(typeof(int));
            var lb1 = Expression.Parameter(typeof(int));
            var ub1 = Expression.Parameter(typeof(int));
            var a = Expression.Parameter(typeof(T[,]));
            var b = Expression.Parameter(typeof(T[,]));
            var c = Expression.Parameter(typeof(T[,]));
    
            add = Expression.Lambda<MatrixOperation>(
                MakeRangeFor(lb0, ub0, i =>
                    MakeRangeFor(lb1, ub1, j =>
                        Expression.Assign(
                            Expression.ArrayAccess(a, i, j),
                            Expression.Add(
                                Expression.ArrayAccess(b, i, j),
                                Expression.ArrayAccess(c, i, j)
                            )
                        )
                    )
                ),
                lb0, ub0, lb1, ub1, a, b, c).Compile();
        }
    
       private readonly T[,] items;
    
       public static Matrix<T> operator +(Matrix<T> a, Matrix<T> b)
       {
           int lb0 = a.items.GetLowerBound(0),
               ub0 = a.items.GetUpperBound(0),
               lb1 = a.items.GetLowerBound(1),
               ub1 = a.items.GetUpperBound(1);
    
           var result = new Matrix<T>(lb0, ub0, lb1, ub1);
           add(lb0, ub0, lb1, ub1, result.items, a.items, b.items);
       }
    }
    

    从基准测试中可以看出,这样的转换进一步减少了运行时间,接近不使用泛化的算法的时间。

    • 8
  2. Best Answer
    VladD
    2020-09-04T06:08:09Z2020-09-04T06:08:09Z

    目前最有效的方法是从 nuget 连接System.Numerics.Vectors,并对您的操作进行矢量化。同时,您将以通用形式收到它们。加法运算是为定义的System.Numerics.Vectors.Vector<T>,即使结构本身T是未知的!

    这是一个简化的类,它表示具有加法运算的任意长度向量:

    class VectorNum<T> where T: struct
    {
        List<System.Numerics.Vector<T>> payload;
        int size;
    
        public VectorNum(int size)
        {
            var vectorSize = System.Numerics.Vector<T>.Count;
            var vectorCount = (size + vectorSize - 1) / vectorSize;
            payload = new List<System.Numerics.Vector<T>>(vectorCount);
            this.size = size;
        }
    
        public void AddTo(VectorNum<T> r)
        {
            List<System.Numerics.Vector<T>> ldata = payload, rdata = r.payload;
            if (size != r.size)
                throw new ArgumentException();
            for (int i = 0; i < ldata.Count; i++)
                ldata[i] = ldata[i] + rdata[i];
        }
    }
    

    基准如下。


    一个更简单(但效率较低)的方法可能是这样的:

    static T add(T l, T r)
    {
        return (T)((dynamic)l + r);
    }
    

    目前,无法向编译器解释泛型参数的类型是数字。更糟的是,这是有问题的,因为相同的IL 适用于泛型类型的每个实例。并且int's 和' 的添加double在 IL 级别由不同的指令表示。


    效率的测量。

    这是一个测试程序:

    static class Program
    {
        static void Main()
        {
            VectorExpr<double> ve1 = new VectorExpr<double>(2048),
                               ve2 = new VectorExpr<double>(2048);
            VectorDyn<double>  vd1 = new VectorDyn<double>(2048),
                               vd2 = new VectorDyn<double>(2048);
            VectorNatDouble    vn1 = new VectorNatDouble(2048),
                               vn2 = new VectorNatDouble(2048);
            VectorNum<double>  vv1 = new VectorNum<double>(2048),
                               vv2 = new VectorNum<double>(2048);
    
            // прогрев
            ve1.Init(1.1, 2.2); ve2.Init(1.1, 2.2);
            vd1.Init(1.1, 2.2); vd2.Init(1.1, 2.2);
            vn1.Init(1.1, 2.2); vn2.Init(1.1, 2.2);
            vv1.Init(1.1, 2.2); vv2.Init(1.1, 2.2);
    
            ve1.AddTo(ve2); vd1.AddTo(vd2); vn1.AddTo(vn2); vv1.AddTo(vv2);
    
            Stopwatch swE = new Stopwatch(), swD = new Stopwatch(),
                      swN = new Stopwatch(), swV = new Stopwatch();
    
            swE.Start();
            for (int i = 0; i < 1000 * 1000; i++)
                ve1.AddTo(ve2);
            swE.Stop();
            var resultE = ve1.Sum();
    
            swD.Start();
            for (int i = 0; i < 1000 * 1000; i++)
                vd1.AddTo(vd2);
            swD.Stop();
            var resultD = vd1.Sum();
    
            swN.Start();
            for (int i = 0; i < 1000 * 1000; i++)
                vn1.AddTo(vn2);
            swN.Stop();
            var resultN = vn1.Sum();
    
            swV.Start();
            for (int i = 0; i < 1000 * 1000; i++)
                vv1.AddTo(vv2);
            swV.Stop();
            var resultV = vv1.Sum();
    
            Console.WriteLine($"Expression: elapsed time {swE.Elapsed}, result = {resultE}");
            Console.WriteLine($"Dynamic   : elapsed time {swD.Elapsed}, result = {resultD}");
            Console.WriteLine($"Native    : elapsed time {swN.Elapsed}, result = {resultN}");
            Console.WriteLine($"Vector    : elapsed time {swV.Elapsed}, result = {resultV}");
        }
    }
    
    class VectorExpr<T>
    {
        static readonly Func<T, T, T> add;
    
        static VectorExpr()
        {
            var a = Expression.Parameter(typeof(T));
            var b = Expression.Parameter(typeof(T));
            add = Expression.Lambda<Func<T, T, T>>(Expression.Add(a, b), a, b).Compile();
        }
    
        T[] payload;
    
        public VectorExpr(int size)
        {
            payload = new T[size];
        }
    
        public void Init(T seed1, T seed2)
        {
            T curr = seed1;
            for (int i = 0; i < payload.Length; i++)
            {
                payload[i] = curr;
                curr = add(curr, seed2);
            }
        }
    
        public void AddTo(VectorExpr<T> r)
        {
            T[] ldata = payload, rdata = r.payload;
            if (ldata.Length != rdata.Length)
                throw new ArgumentException();
            for (int i = 0; i < ldata.Length; i++)
                ldata[i] = add(ldata[i], rdata[i]);
        }
    
        public T Sum() => Enumerable.Aggregate(payload, add);
    }
    
    class VectorDyn<T>
    {
        static T add(T l, T r) => (T)((dynamic)l + r);
    
        T[] payload;
    
        public VectorDyn(int size)
        {
            payload = new T[size];
        }
    
        public void Init(T seed1, T seed2)
        {
            T curr = seed1;
            for (int i = 0; i < payload.Length; i++)
            {
                payload[i] = curr;
                curr = add(curr, seed2);
            }
        }
    
        public void AddTo(VectorDyn<T> r)
        {
            T[] ldata = payload, rdata = r.payload;
            if (ldata.Length != rdata.Length)
                throw new ArgumentException();
            for (int i = 0; i < ldata.Length; i++)
                ldata[i] = add(ldata[i], rdata[i]);
        }
    
        public T Sum() => Enumerable.Aggregate(payload, add);
    }
    
    class VectorNum<T> where T: struct
    {
        List<System.Numerics.Vector<T>> payload;
        int size;
    
        public VectorNum(int size)
        {
            var vectorSize = System.Numerics.Vector<T>.Count;
            var vectorCount = (size + vectorSize - 1) / vectorSize;
            payload = new List<System.Numerics.Vector<T>>(vectorCount);
            this.size = size;
        }
    
        public void Init(T seed1, T seed2)
        {
            var values = new T[System.Numerics.Vector<T>.Count];
            T curr = seed1;
            for (int i = 0; i < size; i++)
            {
                int j = i % values.Length;
                values[j] = curr;
                curr = (T)((dynamic)curr + seed2);
                if (j == values.Length - 1 || i == size - 1)
                {
                    payload.Add(new System.Numerics.Vector<T>(values));
                    Array.Clear(values, 0, values.Length);
                }
            }
        }
    
        public void AddTo(VectorNum<T> r)
        {
            List<System.Numerics.Vector<T>> ldata = payload, rdata = r.payload;
            if (size != r.size)
                throw new ArgumentException();
            for (int i = 0; i < ldata.Count; i++)
                ldata[i] = ldata[i] + rdata[i];
        }
    
        public T Sum()
        {
            var vectorSum = Enumerable.Aggregate(payload, (v1, v2) => v1 + v2);
            return Enumerable.Range(0, System.Numerics.Vector<T>.Count)
                             .Select(idx => vectorSum[idx])
                             .Aggregate((l, r) => (T)((dynamic)l + r));
        }
    }
    
    class VectorNatDouble
    {
        double[] payload;
    
        public VectorNatDouble(int size)
        {
            payload = new double[size];
        }
    
        public void Init(double seed1, double seed2)
        {
            double curr = seed1;
            for (int i = 0; i < payload.Length; i++)
            {
                payload[i] = curr;
                curr = curr + seed2;
            }
        }
    
        public void AddTo(VectorNatDouble r)
        {
            double[] ldata = payload, rdata = r.payload;
            if (ldata.Length != rdata.Length)
                throw new ArgumentException();
            for (int i = 0; i < ldata.Length; i++)
                ldata[i] = ldata[i] + rdata[i];
        }
    
        public double Sum() => payload.Sum();
    }
    

    比较动态方法、表达式方法、向量化方法和本地非通用代码。

    五次运行的结果(发布模式,Visual Studio 之外,64 位 Windows 上的 x64):

    ...\bin\x64\Release>Test.exe
    Expression: elapsed time 00:00:03.2824695, result = 4613743627468,84
    Dynamic   : elapsed time 00:00:22.1788664, result = 4613743627468,84
    Native    : elapsed time 00:00:01.3229095, result = 4613743627468,84
    Vector    : elapsed time 00:00:01.0145604, result = 4613743627468,84
    
    ...\bin\x64\Release>Test.exe
    Expression: elapsed time 00:00:03.3579191, result = 4613743627468,84
    Dynamic   : elapsed time 00:00:22.6582909, result = 4613743627468,84
    Native    : elapsed time 00:00:01.3391487, result = 4613743627468,84
    Vector    : elapsed time 00:00:01.0315590, result = 4613743627468,84
    
    ...\bin\x64\Release>Test.exe
    Expression: elapsed time 00:00:03.3705103, result = 4613743627468,84
    Dynamic   : elapsed time 00:00:21.9322240, result = 4613743627468,84
    Native    : elapsed time 00:00:01.3294231, result = 4613743627468,84
    Vector    : elapsed time 00:00:01.0168434, result = 4613743627468,84
    
    ...\bin\x64\Release>Test.exe
    Expression: elapsed time 00:00:03.3426822, result = 4613743627468,84
    Dynamic   : elapsed time 00:00:22.5870899, result = 4613743627468,84
    Native    : elapsed time 00:00:01.3227761, result = 4613743627468,84
    Vector    : elapsed time 00:00:01.0202565, result = 4613743627468,84
    
    ...\bin\x64\Release>Test.exe
    Expression: elapsed time 00:00:03.3142676, result = 4613743627468,84
    Dynamic   : elapsed time 00:00:21.8089834, result = 4613743627468,84
    Native    : elapsed time 00:00:01.2854937, result = 4613743627468,84
    Vector    : elapsed time 00:00:00.9963579, result = 4613743627468,84
    

    表达式平均 3.3336 秒,动态 22.2331 秒,本机代码 1.3200 秒,矢量化代码 1.0159 秒。因此矢量化版本甚至比原生版本快 30%。

    int更好的结果:

    ...\bin\x64\Release>Test.exe
    Expression: elapsed time 00:00:02.9075671, result = -1870659584
    Dynamic   : elapsed time 00:00:22.9417084, result = -1870659584
    Native    : elapsed time 00:00:01.5297815, result = -1870659584
    Vector    : elapsed time 00:00:00.5281395, result = -1870659584
    

    与本机代码相比,矢量化提供了三倍的加速。

    • 6
  3. Андрей NOP
    2020-09-04T19:11:48Z2020-09-04T19:11:48Z

    不幸的是,没有数字接口,可用的最大值是值类型的基本ValueType

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5