iOS-矩陣與線性代數的關係____仿射變換
大多數人在高中,或者大學低年級,都上過一門課《線性代數》。這門課其實是教矩陣。
剛學的時候,還蠻簡單的,矩陣加法就是相同位置的數字加一下。
矩陣減法也類似。
矩陣乘以一個常數,就是所有位置都乘以這個數。
但是,等到矩陣乘以矩陣的時候,一切就不一樣了。
這個結果是怎麼算出來的?
教科書告訴你,計算規則是,第一個矩陣第一行的每個數字(2和1),各自乘以第二個矩陣第一列對應位置的數字(1和1),然後將乘積相加( 2 x 1 + 1 x 1),得到結果矩陣左上角的那個值3。
也就是說,結果矩陣第m行與第n列交叉位置的那個值,等於第一個矩陣第m行與第二個矩陣第n列,對應位置的每個值的乘積之和。
怎麼會有這麼奇怪的規則?
我一直沒理解這個規則的含義,導致《線性代數》這門課就沒學懂。研究生時發現,線性代數是向量計算的基礎,很多重要的數學模型都要用到向量計算,所以我做不了複雜模型。這一直讓我有點傷心。
前些日子,受到一篇文章的啟發,我終於想通了,矩陣乘法到底是什麼東西。關鍵就是一句話,矩陣的本質就是線性方程式,兩者是一一對應關係。如果從線性方程式的角度,理解矩陣乘法就毫無難度。
下面是一組線性方程式。
矩陣的最初目的,只是為線性方程組提供一個簡寫形式。
老實說,從上面這種寫法,已經能看出矩陣乘法的規則了:係數矩陣第一行的2和1,各自與 x 和 y 的乘積之和,等於3。不過,這不算嚴格的證明,只是線性方程式轉為矩陣的書寫規則。
下面才是嚴格的證明。有三組未知數 x、y 和 t,其中 x 和 y 的關係如下。
x 和 t 的關係如下。
有了這兩組方程式,就可以求 y 和 t 的關係。從矩陣來看,很顯然,只要把第二個矩陣代入第一個矩陣即可。
從方程式來看,也可以把第二個方程組代入第一個方程組。
上面的方程組可以整理成下面的形式。
最後那個矩陣等式,與前面的矩陣等式一對照,就會得到下面的關係。
矩陣乘法的計算規則,從而得到證明。
仿射變換
前言
今天接觸了檢視的2D變換,於是翻看了Apple Doc裡面關於CGAffineTransform的用法.一開始我是直接閱讀CGAffineTransformMakeScale
官方文件寫得比較抽象加之陌生詞彙較多,所以讀了5分鐘還是沒搞清楚這個縮放和矩陣的關係(大學時候線性代數卷面分97,太久沒用沒靈感了),接著百度Google了一下發現大部分文章都是介紹怎麼使用CGAffineTransform,並沒有對CGAffineTransform本身的資料結構和矩陣關係結合起來,所以只能自己研究了,現在總結一下整個分析過程.關於仿射變換和CGAffineTransform的介紹這裡不再描述.
CGAffineTransform的用法
- 生成需要變換的CGAffineTransform結構體
- 將CGAffineTransform賦值給view.transform屬性(這一部通常定義成動畫)
- 動畫結束,將view.transform設定為CGAffineTransformIdentity,還原原樣
一般來說,第1,2步應該是合併在一起的,類似
1234 | //直接使用CGAffineTransformMake()函式初始化結構體aView.transform = CGAffineTransformMake(a,b,c,d,tx,ty);//或者系統封裝好的方法aView.transform = CGAffineTransformMakeScale(x, y); |
設定動畫方法如下:
12345678910111213141516171819 | //如果用動畫實現,程式碼則類似[UIView animateWithDuration:0.3 animations:^{ //放大2倍 aView.transform = CGAffineTransformMakeScale(2, 2);} completion:^(BOOL finished){ [UIView animateWithDuration:0.3 animations:^{ //回播動畫 aView.transform = CGAffineTransformIdentity; }];}];//動畫關鍵幀大於1的話,一般用下面這個方法確保動畫正確執行[UIView animateKeyframesWithDuration:1.0 delay:0.0 options:0 animations:^{ [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.8 animations:^{ aView.transform = CGAffineTransformMakeScale(2, 2); }]; [UIView addKeyframeWithRelativeStartTime:0.8 relativeDuration:0.2 animations:^{ aView.transform = CGAffineTransformIdentity; }];} completion:nil]; |
動畫結束後設置CGAffineTransformIdentity,其實就是把view.transform設定為預設值,如果放在動畫裡執行其實就相當於回播了一下動畫.第三步非必須的.
CGAffineTransform的本質
上面我提前把CGAffineTransform的用法說了,目的是想讓大家心裡有底,知道CGAffineTransform的用法其實很簡單,但是心裡會產生一個疑問,就是CGAffineTransform和矩陣是什麼關係?當然如果你心裡沒有這個疑問,那你百度一下縮放平移選擇等基本用法就夠了,也沒必要往下看了
在CoreGraphics/CGAffineTransform.h標頭檔案裡面可以看到,CGAffineTransform的本質就是一個結構體,結構體長成這樣
1234 | struct CGAffineTransform { CGFloat a, b, c, d; CGFloat tx, ty;}; |
從文件中可以看到CGAffineTransform代表一個3*3的矩陣 ,因為第三列固定是0
0 1,所以
CGAffineTransform結構體裡面就只有6個元素了.此時你可能有疑問為什麼第三列固定是0 0 1?下面會說到.
運算後view上的每一個點的x,y分別記為x’,y’計算公式為:
(其中x,y就是view做2D運算前的每一個點的座標,x’,y’就是2D變化後的view的每一個點的座標)
CGAffineTransform的變換與矩陣的關係
CGAffineTransform的本質就是一個結構體,而從CGAffineTransform的用法一節可以看到整個動畫從頭到尾無非就是在改變這個結構體的值,那麼這個結構體的變化又和檢視的變化怎麼聯絡起來?這裡個問題原本是從2D仿射變換的定義中推匯出變換的公式,再從公式轉為矩陣的設定的.這裡我們就假設不懂2D仿射變換的公式吧,從程式中找到CGAffineTransform經過變換後的值,再與變換的公式對比,看看變換的值是否匹配矩陣,再理解變換和矩陣的關係.下面分別從檢視的放大,縮小,平移,旋轉分析.
放大和縮小
動畫之前,先打印出view.transform的預設值
123 | NSLog(@"CGAffineTransformIdentity:%@",NSStringFromCGAffineTransform(aView.transform));//得到結果=>CGAffineTransformIdentity: [1, 0, 0, 1, 0, 0]//也就是a=1,d=1,其他全是0 |
將檢視放大2倍之後,再打印出view.transform的值
1234 | aView.transform = CGAffineTransformMakeScale(2, 2);NSLog(@"Become bigger:%@",NSStringFromCGAffineTransform(aView.transform));//得到結果=>Become bigger: [2, 0, 0, 2, 0, 0]//也就是a=2,d=2,其他全是0 |
如果你細心一點就會發現,其實CGAffineTransformMakeScale(x,y),設定的就是矩陣中的a和d.現在我們把經過放大2倍所生成的矩陣,按照CGAffineTransform的本質小節中的變換公式計算一下,看看能夠得到什麼結果
簡單計算一下矩陣,可以得到
123 | x' = 2x;y' = 2y;1 = 1; |
好像發現了什麼了~