笔记:ASP.NET MVC 个人用户认证

起因:要上线一个内部的小系统,于是就动手实现,不过以前属于纯玩票,现在毕竟要实际用的东西,认证就要做了,后期要把认证绑到公司域的AD上,但是现在没有权限的情况下,内部测试只能用本地的认证系统了。

开始在完全搞不懂MVC这部分的情况下,想得蛮简单,小范围测试只需要几个人,手动创建完帐号字典,连数据库都不用,直接明文一验证就完了。结果发现框架里已经提供了比较完事的认证系统,就直接用吧,但是不会用……

经过一些时间的搜索,先记录一下。

首先依赖数据库,会使用LocalDB在App_data目录下生成一个mdf的LocalDB文件,一共5张表,用户的,用户组的,映射关系什么的,开始可以先不管。

在创建完框架直接调试,可以进入注册页面,先注册一个帐号,会要求用邮箱,密码也有复杂性要求,这部分如果要修改可以在App_Start\IdentityConfig.cs文件里修改PasswordValidator。

创建完,会发现本地数据库的表AspNetUsers里会多一个用户。当然密码不会用明文存……

然后我的需求1是帐号密码可以由我一个人创建,2是其他人可以登陆,但是不能创建新帐户。

尝试1:直接修改数据库,添加条目,发现AspNetUsers表里的ID是hash值,手动创建其实修改成数字应该也可以,不过显得不专业,放弃。

尝试2:注册、注销,循环注册多个帐户,太傻,放弃。

尝试3:登陆完注册导航按钮消失,直接输入url可以不?可以……这个办法已经可以了,偷懒的话可以把注册代码的注册成功后登陆注销,弄完帐户后再恢复。就下面这行

 await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);

需求1解决,需求2搜了点文章,解决这个问题的同时让我对MVC三个层次的工作方式又理解了一些。

解决办法:

  1. 让注册页必须由一定权限的人登陆,无权限的人会提示登陆,登陆完再验证权限。在AccountController.cs的Register部分注销匿名,添加Roles控制,即只允许Admins用户访问这个页面。
  2.         //
            // POST: /Account/Register
            [HttpPost]
            //[AllowAnonymous]
            [Authorize(Roles = "Admins")]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> Register(RegisterViewModel model)

    这时候如果调试,会发现所有人都无法访问这个界面,会提示你登陆,然后就看不到了。查了下Account下的几个代码,好像没有集成用户组管理的页面和代码。于是在数据库里手动添加。
    在数据库的AspNetRoles表里添加ID为1,Name为Admins的条目,以及ID为2,Name为Users的条目。
    在数据库的AspNetUserRoles表里,将刚注册的用户的ID(Hash值)添加到这里的UserID,RoleID为1,就可以设定这个用户的Roles为Admin了。添加的其他用户可以手动分配为2(Users),或者像我一样没需求的,可以不分配。

通过这个问题的搜索学习,还了解到了一些MVC的设计思路,之前自己瞎玩的时候搞得一团糟,几乎把所有的代码都放到了View层,虽然也尝试把数据结构放到了Models,但是还是耦合到一团球。

我之前理解路由(当然是错误的理解),Control层只是起到转发Action的作用,并实现一些数据处理,而且这些数据处理都是手写的,但是在研究注册页的注册按钮如何工作时,我凌乱了,半天没理解怎么弄的,找到了一个stackoverflow的高分回答,解释了一下Model和Control的Binding关系,读了两遍,再回去看代码,果然就明白了。

模型基本上只提供了一个数据结构,而且基本只是数据的值,不包含函数(方法),View在提交Post的时候,调用的是同一个路由,所以开始看的时候莫名的觉得这个页面既显示注册界面,又完成了注册功能。然后找到Controller的时候,虽然明白了Controller如何完成功能,但是又不明白这些数据结构是如何传递的。相对的,也不明白Model是如何在这里面起作用的。

这些能活下来的框架和设计,都是相对简单且实用的,理解起来其实并不是太困难,但是需要一些点拨和经验。

 

伪技术贴:暗黑3死灵鼠标宏替代软件

暗黑3的机制现在很搞笑,很多Build要依靠大量高频率的点击来实现,所以导致了鼠标宏的出现,而且官方态度很暧昧,从来不承认这种辅助的合理性,也从来没封禁。

我了解使用这个东西的风险,所以自己写了个类似的东西,分享出来,如果能帮助到有类似需要的人最好,不过请使用的人自己承担风险。

源代码

下载点这个

界面就是三个时间间隔,后台是两个快捷键,按Ctrl+B一次,开始无限循环按3,再按Ctrl+3一次,停止无限循环,这个我用在吞噬技能上。对应界面的时间间隔3。注:开始想用Ctrl+3作为快捷键,但是这意味着在连续按3的情况下,只要按了Ctrl键,就等于按了Ctrl+3,就停下来了,所以一直纳闷为什么一放死地吞噬就掉了……Orz

另外一个快捷键是Alt+2,表现为按一次2,然后无限按1,按1的频率间隔是时间间隔1,整个时间持续为时间间隔2。这个对应为死地(设为2号技能)10秒里,不停地按1(尸矛)。

工作优先级

最近不是特别忙,心态上的。

最近2个月没有一个礼拜不出差的,每月基本都有14、5天不在家,老婆怀孕5、6个月心情也不是很平稳,又到了年底事情多的时候,按理应该挺忙挺慌,但是就是没有以前急迫的感觉了。

最主要的改变应该是自己吧,首先是工作和生活尽量分开,出差回来后的晚上和周末绝对不要坐电脑前,无论是工作还是打游戏放松。只要陪老婆的绝对时间够了,无论是看电视逛街还是发呆,结果都不差。

然后就是工作,自己安排工作不要安排或承诺2周以上或以后的事情,领导安排的工作,以及认为重要的事情,订好时间计划,严格保证执行。日常工作做好,如有时间来不及,做好解释工作,寻找解决方案,不能简单的拒绝。对于份外工作,和甩锅,不积极,不主动,不反应。

加班,首先周六日没有以前整天干活的情况发生了,有紧急情况还会忙一会,或者老婆自己有事的时候也能抽空干点活。每天的8小时以外,如果有事情需要做,我要先反思一下为什么今天需要加班,是我耽误了白天的进度,还是被其他事情干扰了,干扰项是否是足够重要的?一定需要加班解决,还是可以安排到明天。总之,加班是救急不救忙。

走自己的路,让别人说去吧

年轻的时候,除了大四那一年由于考研工作都不顺,比较灰心,其他的时间都是不可一世,没什么本事还是牛逼哄哄,那时候念叨这句话,就是我很牛逼,所以我就这么着,管其他人怎么着。

但这次不是,刚才突然有点想法,但是又很抽象,就想着怎么用语言总结一下,然后就出来这么一句。

起因是某个同事,因为某个事情找我支持,还有一个礼拜就要投标了,我却决定劝他放弃这个项目。当然问题出在我身上,我一直没有给出绝对明确的“放弃吧”,直到今天。我总是习惯于把人和事情结合到一起看,假设每个环节都有强大的逻辑处理和异常处理功能,就像组织良好的代码一样,但是实际上不是的,销售不会因为一些理性的判断就放弃项目,技术人员也不会因为项目很重要就冒风险,就算有,这样的人也会逐渐被淘汰掉,好的流程不会让问题在流转的过程中无声的消失掉,而是通过各种触发条件被激活,再由有关键权限的人做判断,做判断的方法和态度也就是企业文化了。

现在反思一下,当初虽然给出了No,但是没有力度,应该让销售觉得,如果不找领导压我,我就不会改变主意,这也就是把风险和问题,提前暴露,并交由足够权限的人妥善处理。

我现在还没有找准确这个度,这个风格,也就是我还没有找准现在的路,当然只是指这份工作,这个岗位上的,一旦找准了,就要坚持下去,冲突也好,愉快地合作也好,必须是符合我的工作范围,符合我的屁股的位置的。

迎接新财年

离新年春节还有三四个月,不过公司级别的财政年度已经结束了,对于工作上,10月已经是新的一年了。9月份真是忙到昏天黑地,看看8月份的blog上还有闲暇研究一下解析几何,这次更新就只能等到国庆假期了。

这一两个月,感觉自己的变化非常大,和过去2年的动荡有关系,终于能看到一个相对稳定的阶段,也就有了总结和展望的前提。虽然总结的时候,看到的更多是自己愚蠢的一面,不过no pain no gain,如果吸取教训更重要,32岁,虽然已经不年轻,但是还有时间,还有机会。

抛去楞头青和犯得蠢不提,最近2年积累太少,一是input少,二是output少,过去做技术工作,遇到什么问题就学什么,同事和客户问什么,就说什么,输入输出都有。现在做两边不靠的工作,需要学什么要自己去总结,一些经验如何归纳也要自己抽空做,实际上无论输入输出都没怎么弄,就会有种虚浮的感觉。

还有时间管理,一直有个想法,要把每天和每周的工作提前计划好,然后严格执行,但是一直没有成功。新财年,还是想要试一试,目前看来,负责的东西有点多,按照之前的经验,一个人不可能干完,对于完全没可能的?事情,只能想办法尽可能地提高效率,尽量完成度高一些,另外一方面,完不成的情况下,也要有足够的取舍,以及证据说明自己尽力了。

业余时间,想多看看书,过去两年一直在看工具书和网络小说,17年底,找一些严肃小说看吧,看点苦大仇深的调节一下,总看爽文也不行。

放弃使用MathNet的Spatial库

这个库也只是更新到0.4,而且更新的非常慢,感觉项目基本停滞。

这次主要的目的之一也是锻炼自己,那么参考代码重新实现也对我自己更有利。

里面的一些结构非常值得参考,一些函数可以直接拿来用,不过这个库还是像通常能见到的库的一样是纯几何的,而不是我想实现的测量上的,所以还是要保留自己的一些思路。

另外就是目前在补回归分析的课,还有解析几何的课(高数下)。最近要花一些时间在这些基础上,代码要少写一些。

两个向量的叉积实现

需求是,根据坐标系的两个轴(向量),求出另外一个轴,由于我高数下(解析几何)不及格,所以一直没有想起来对应的定义。之前自己解决的办法是,求出这两个向量构造的平面,然后平面的法向就是解。

今天在写我的GDTGeometry库时又遇到这个问题,这次先去翻了翻新买的高数下教材,发现这就是向量叉积的定义,可惜当初并没有记牢。于是想直接在MathNet库里找,结果里面只有multiply(点积)和pointwise-multiply(逐点乘积),又特意找了下翻译叉积是Cross-Multiply,我觉得像基础课的很多定义,应该加上英文注释。

搜了下mathnet cross multiply,找到了stack overflow上的一篇问答,这个函数在Iridium子库里实现,不过这个库已经太监了,而且2012年就太监了,到现在还没有整合到numerics(主库里),简直是无语,我很想说这么个简单的功能我commit一个patch吧,不过仔细想了下,问题应该出在叉积通常只用在3维空间里,低维和高维的推广好像都有点问题。

overflow上的回答里已经有了我要的代码:

    using DLA = MathNet.Numerics.LinearAlgebra.Double;

    public static DLA.Vector Cross(DLA.Vector left, DLA.Vector right)
    {
        if ((left.Count != 3 || right.Count != 3))
        {
            string message = "Vectors must have a length of 3.";
            throw new Exception(message);
        }
        DLA.Vector result = new DLA.DenseVector(3);
        result[0] = left[1] * right[2] - left[2] * right[1];
        result[1] = -left[0] * right[2] + left[2] * right[0];
        result[2] = left[0] * right[1] - left[1] * right[0];

        return result;
    }

针对三维向量。

推导在高数下教材上有,和我一样数学差的可以去查。结论是

\[c=a\times b=\begin{vmatrix}i&j&k\\a_x&a_y&a_z\\b_x&b_y&b_z\end{vmatrix}\]

所以

\[i_c=\begin{vmatrix}a_y&a_z\\b_y&b_z\end{vmatrix}\]

\[j_c=-\begin{vmatrix}a_x&a_z\\b_x&b_z\end{vmatrix}\]

\[k_c=\begin{vmatrix}a_x&a_y\\b_x&b_y\end{vmatrix}\]

开始想把这部分写成DenseVector的扩展函数,后来想想既然只能用在3维向量上,就放到自己的Vector(三维)类里了。

仿射变换

实际上坐标测量里研究的变换只有两种,一种是旋转,上一篇文章那样的,另外一种就是平移。

基本坐标系(自然基)

\(O=\begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0&1&0\\0&0&0&1\end{bmatrix}\)

点P

\(\begin{bmatrix}4\\1\\0\\1\end{bmatrix}\)

代表坐标系的矩阵由\(3*3\)变成了\(4*4\),坐标的列向量也由3个元素加了一个1。

虽然介绍仿射变换方法的文章很多,但是原理性的介绍没找到,这种表达形式意味着空间由3维变成了4维,虽然第四维的坐标永远是1,如果有哪位朋友有相关文章提供阅读,感激不尽。

先将坐标系延基本坐标系平移\([0,-3,0]\),再延Z轴旋转45度,在新坐标系

\(A=\begin{bmatrix}\frac{\sqrt{2}}{2}&-\frac{\sqrt{2}}{2}&0&0\\\frac{\sqrt{2}}{2}&\frac{\sqrt{2}}{2}&0&-3\\0&0&1&0\\0&0&0&1\end{bmatrix}\)

下的坐标为点

\(P_{A}=\begin{bmatrix}4\sqrt{2}\\0\\0\\1\end{bmatrix}\)

满足

\[OP=AP_{A}\]

新坐标系的过渡矩阵(相对于自然基)和之前的相比,加了一列(基的个数+1),是新坐标系0点在基本坐标系的坐标值。

将基本坐标系平移\([-10,-10,-10]\)再延Y轴旋转90度。

\(B=\begin{bmatrix}0&0&1&-10\\0&1&0&-10\\-1&0&0&-10\\0&0&0&1\end{bmatrix}\)

在B坐标系下P点坐标

\(P_{B}=B^{-1}P=\begin{bmatrix}-10\\11\\14\\1\end{bmatrix}\)

按向量空间的定义,由A到B的过渡矩阵为\(A^{-1}B\)

\(\begin{bmatrix}0&\frac{\sqrt{2}}{2}&\frac{\sqrt{2}}{2}&-12.0208\\0&\frac{\sqrt{2}}{2}&-\frac{\sqrt{2}}{2}&2.12132\\-1&0&0&-10\\0&0&0&1\end{bmatrix}\)

 

B到A的过渡矩阵\(Q^{-1}\)为

\(\begin{bmatrix}0&0&-1&-10\\\frac{\sqrt{2}}{2}&\frac{\sqrt{2}}{2}&0&7\\\frac{\sqrt{2}}{2}&-\frac{\sqrt{2}}{2}&0&10\\0&0&0&1\end{bmatrix}\)

过渡矩阵的最后一行依旧是\(\begin{bmatrix}0,0,0,1\end{bmatrix}\)

这样点的变换在平移和旋转坐标系的数学关系就整理好了。

在向量空间里,所有的向量表示的都是向量(既有方向,又有长度-大小),这和空间上的点坐标是类似的,但是在测量或者建模时,点还有一个特性就是点的法向(矢量),这个参数只有方向,没有大小(总是单位长度1)。

当坐标系平移时,这个法向是不会随之平移的,只能当坐标系旋转时,法向才会随之旋转。

所以对于法向,坐标的最后一个值永远为0,这样在矩阵变换时,就不会受最后基的最后一个列向量影响了。

算是写完,消耗时间大约是2个晚上加周六周日的业余时间,当然周六周日我都有早上打篮球,周六还去理了发。之所以要把这些很简单的东西整理一遍,而不是把网上现成的材料拷贝保留下来,原因有两个。

1.自己整理一遍,把自以为理解的东西讲一遍,可以排查一下自己是否真正理解了,而且理解的比较全面了。

2.2013年我第一次尝试用C#解决实际的问题,在用代码实现的过程中,发现复杂度比我估计得要多(然后我并没有吸取教训,直到现在我预估依然很差)。而且,虽然矩阵的理论很完备,并不意味着算法是统一的,比如DirectX用行向量,OpenGL用列向量,再比如Math.Net里的DenseMatrix.OfArray构造的矩阵,将数组里的元素按列优先的顺序存入数组。这就导致我在没有足够理论储备的情况下只能用实际数据的测试的方法实现了需要的功能。等到2015年我因为其他事情要扩展这部分代码时,我发现我已经无法修改了。

所以这次借写新库的机会,将理论整理清楚,未来写代码时就不会现一会矩阵乘向量,一会向量乘矩阵,最后乱成一团的情况了。

using System;
using MathNet.Numerics.LinearAlgebra.Double;
using System.Diagnostics;

namespace CoordinateTest
{
    class Program
    {
        static void Main(string[] args)
        {
            dimension3();
            dimension4();
        }
        static void dimension3 ()
        {
            DenseMatrix O = DenseMatrix.OfArray(new double[,]
{
                {1,0,0 },
                {0,1,0 },
                {0,0,1 }
});
            double SqrtTwo = Math.Sqrt(2) / 2.0;
            DenseMatrix A = DenseMatrix.OfArray(new double[,]
            {
                {SqrtTwo,-SqrtTwo,0 },
                {SqrtTwo,SqrtTwo,0 },
                {0,0,1 }
            });
            DenseMatrix B = DenseMatrix.OfArray(new double[,]
            {
                {0,0,1 },
                {0,1,0 },
                {-1,0,0 }
            });

            DenseVector P = DenseVector.OfArray(new double[] { 1, 1, 0 });
            DenseVector PA = A.Inverse() * P as DenseVector;
            DenseVector PB = B.Inverse() * P as DenseVector;

            Debug.WriteLine(PA);
            Debug.WriteLine(PB);
        }
        static void dimension4()
        {
            DenseMatrix O = DenseMatrix.OfArray(new double[,]
            {
                {1,0,0,0 },
                {0,1,0,0 },
                {0,0,1,0 },
                {0,0,0,1 }
            });
            DenseVector P = DenseVector.OfArray(new double[] { 4, 1, 0, 1 });
            double SqrtTwo = Math.Sqrt(2) / 2.0;

            DenseMatrix A = DenseMatrix.OfArray(new double[,]
            {
                {SqrtTwo,-SqrtTwo,0,0 },
                {SqrtTwo,SqrtTwo,0,-3 },
                {0,0,1,0 },
                {0,0,0,1 }
            });
            DenseVector PA = DenseVector.OfArray(new double[] { 8* SqrtTwo, 0, 0, 1 });

            Debug.WriteLine(A * PA);
            DenseMatrix B = DenseMatrix.OfArray(new double[,]
            {
                {0,0,1,-10 },
                {0,1,0,-10 },
                {-1,0,0,-10 },
                {0,0,0,1 }
            });
            Debug.WriteLine(B.Inverse());

            DenseVector PB = B.Inverse() * P as DenseVector;
            Debug.WriteLine(PB);

            DenseMatrix Q = A.Inverse() * B as DenseMatrix;
            Debug.WriteLine(Q);

            Debug.WriteLine(Q * PB);
            Debug.WriteLine(A * Q);

            Debug.WriteLine(Q.Inverse());

            //for vector test
            DenseVector v1 = DenseVector.OfArray(new double[] { SqrtTwo, SqrtTwo, 0, 0 });

            Debug.WriteLine(A.Inverse() * v1);
        }
    }
}

 

输出为:

DenseVector 3-Double
1.41421
      0
      0

DenseVector 3-Double
0
1
1

DenseVector 4-Double
4
1
0
1

DenseMatrix 4x4-Double
0  0  -1  -10
0  1   0   10
1  0   0   10
0  0   0    1

DenseVector 4-Double
-10
 11
 14
  1

DenseMatrix 4x4-Double
 0  0.707107   0.707107  -12.0208
 0  0.707107  -0.707107   2.12132
-1         0          0       -10
 0         0          0         1

DenseVector 4-Double
    5.65685
4.44089E-16
          0
          1

DenseMatrix 4x4-Double
 0  0  1  -10
 0  1  0  -10
-1  0  0  -10
 0  0  0    1

DenseMatrix 4x4-Double
       0          0  -1  -10
0.707107   0.707107   0    7
0.707107  -0.707107   0   10
       0          0   0    1

DenseVector 4-Double
1
0
0
0

 

 

向量空间的基本算法验证

主要是基变换与坐标变换。

先不考虑坐标平移(仿射)的情况,只考虑坐标系旋转(0点不变的情况)。

笛卡尔坐标系是三个轴正交的坐标系,在向量空间里,其实就是维数为3的向量空间的一个基,且3个向量正交且都是单位向量。

假设存在一个基本坐标系(自然基),那么三个轴分别是

\(X_{O}=\begin{bmatrix}1\\0\\0\end{bmatrix}Y_{O}=\begin{bmatrix}0\\1\\0\end{bmatrix}Z_{O}=\begin{bmatrix}0\\0\\1\end{bmatrix}\)

对应的坐标系矩阵为

\(O=\begin{bmatrix}1&0&0\\0&1&0\\0&0&1\end{bmatrix}\)

将坐标系延Z轴旋转45度,对应的三个轴变为

\(X_{A}=\begin{bmatrix}\frac{\sqrt{2}}{2}\\\frac{\sqrt{2}}{2}\\0\end{bmatrix}Y_{A}=\begin{bmatrix}-\frac{\sqrt{2}}{2}\\\frac{\sqrt{2}}{2}\\0\end{bmatrix}Z_{A}=\begin{bmatrix}0\\0\\1\end{bmatrix}\)

得到坐标系

\(A=\begin{bmatrix}\frac{\sqrt{2}}{2}&-\frac{\sqrt{2}}{2}&0\\\frac{\sqrt{2}}{2}&\frac{\sqrt{2}}{2}&0\\0&0&1\end{bmatrix}\)

将坐标系延Y轴旋转90度,对应的三个轴变为

\(X_{B}=\begin{bmatrix}0\\0\\-1\end{bmatrix}Y_{B}=\begin{bmatrix}0\\1\\0\end{bmatrix}Z_{B}=\begin{bmatrix}1\\0\\0\end{bmatrix}\)

得到坐标系

\(B=\begin{bmatrix}0&0&1\\0&1&0\\-1&0&0\end{bmatrix}\)

在C#里用Math.Net实现

            DenseMatrix O = DenseMatrix.OfArray(new double[,]
            {
                {1,0,0 },
                {0,1,0 },
                {0,0,1 }
            });
            double SqrtTwo = Math.Sqrt(2) / 2.0;
            DenseMatrix A = DenseMatrix.OfArray(new double[,]
            {
                {SqrtTwo,-SqrtTwo,0 },
                {SqrtTwo,SqrtTwo,0 },
                {0,0,1 }
            });
            DenseMatrix B = DenseMatrix.OfArray(new double[,]
            {
                {0,0,-1 },
                {0,1,0 },
                {1,0,0 }
            });

定义一个点(向量),即整个向量空间集合里的一个元素,这个点P在基本坐标系下的坐标值为\(\left(1,1,0\right)\),即存在一组常数

\[\begin{cases}\lambda_{O1}=1\\ \lambda_{O2}=1\\ \lambda_{O3}=0\end{cases}\]

满足

\(\begin{align*}P&=\lambda_{O1}X_{O}+\lambda_{O2}Y_{O}+\lambda_{O3}Z_{O}\\&=1*\begin{bmatrix}1\\0\\0\end{bmatrix}+1*\begin{bmatrix}0\\1\\0\end{bmatrix}+0*\begin{bmatrix}0\\0\\1\end{bmatrix}\end{align*}\)

这几个变量满足关系

\[OP=AP_{A}=BP_{B}\]

那么P在A和B基上的坐标为:

\[P_{A}=A^{-1}P=\begin{bmatrix}\sqrt{2}\\0\\0\end{bmatrix}\]

\[P_{B}=B^{-1}P=\begin{bmatrix}0\\1\\1\end{bmatrix}\]

代码实现

            DenseVector P = DenseVector.OfArray(new double[] { 1, 1, 0 });
            DenseVector PA = A.Inverse() * P as DenseVector;
            DenseVector PB = B.Inverse() * P as DenseVector;

            Debug.WriteLine(PA);    DenseVector 3-Double 1.41421 0 0
            Debug.WriteLine(PB);    DenseVector 3-Double 0 1 1

过渡矩阵

\(B=AA^{-1}B\)

另\(Q=A^{-1}B\)

则\(B=AQ\)

过渡矩阵(基变换),实际是上两个坐标系之间的变化关系。

又因为

\[AP_{A}=BP_{B}\]

所以\[P_{A}=A^{-1}BP_{B}\]

\[P_{A}=QP_{B}\]

\[P_{B}=Q^{-1}P_{A}\]

值得注意的是过渡矩阵在坐标系变换和坐标变换起的作用是相反的。这也好理解,一个是坐标系转,一个是点在转,所以方向是相反的。

重温线性代数

之前决定动手写一个库,每当有空想动手写的时候却又卡住不能动,理论知识太欠缺,只能从基础补起,在京东上买了同济的一套数学教材,4本包括线性代数,高数上下,概率。其中线性代数是我最关注的,空间上的坐标变换在计算机实现都是靠矩阵。

花了两个晚上,草草看完,只挑对自己有用的部分做一下记录。

第一章是矩阵定义,性质,结合方程组的介绍,矩阵的变换。第二章是方阵的行列式(determinant),定义,性质,展开,矩阵求逆。

记得当初学的时候是先学的行列式,然后是矩阵,从数学史的发展,也是先有的行列式,然后才有矩阵。不过先介绍哪个都一样,教材都把行列式和矩阵结合线性方程组来介绍,完全不直观。

这两章的内容对我来说意义已经不大了,目标就是计算机实现,很多定义和算法都集成得很好了,由于实际问题都是有解的,所以很多为了验证有解而需要检查的步骤也不需要做了。

矩阵运算的代码,C# with Math.Net

            DenseMatrix A;
            DenseMatrix B;
            A = DenseMatrix.OfArray(
                new double[,]{
                { 3,-1,2},
                { 2,1,-2}
                }
                );
            B = DenseMatrix.OfArray(
                new double[,]{
                { 1,5,1},
                { -2,-1,0}
                }
                );
            Console.WriteLine((A + 2 * B).ToString());
            Console.WriteLine((3 * A - B).ToString());
            Console.WriteLine((A * B.Transpose()).ToString());
            Console.WriteLine((A.Transpose() * B));
            DenseMatrix A = DenseMatrix.OfArray(new double[,]
            {
                {1,2,3 },
                {2,3,1 },
                {3,1,2 }
            });
            Console.WriteLine(A.Inverse().ToString());

            A = DenseMatrix.OfArray(new double[,]
            {
                {1,-1,-1 },
                {-2,2,1 },
                {2,-1,3 }
            });
            Console.WriteLine(A.Solve(DenseVector.OfArray(new double[] { -1, 1, 1 })));

第三章和第五章是向量空间与线性空间,我实在不理解为什么国内几乎所有的教材都无时无刻不把线性方程组拿出来,看起来很容易对应上,但是并不能激发人的好奇啊,学习线性代数就为了给方程组求解?真不如直接跟解析几何对应上,直观,而且也能给学生一些期望值,知道学了以后能干什么。

这两章我并没有搞清区别,向量空间是线性空间的特殊子集,但是对我来说,几何三维空间用哪个并没有区别。

[table “1” not found /]

下一步的工作,用代码实现一些坐标和坐标系的实现。