Advertisement
Nokiyen

Matrix (2014/08/06 21:46)

Aug 6th, 2014
161
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.83 KB | None | 0 0
  1. -- [[
  2. [[
  3.  
  4. # matrix API みんなでつくればこわくない #
  5.  
  6. * TODO
  7. * どのような形式でデータを保持するかの策定
  8. *
  9. * 加減算は、それぞれの行と列の数が揃ってないとできないので、そのチェック
  10. * 逆行列は正方行列(縦と横の数が同じ)じゃないと出てこないので、チェック
  11. * 逆行列は、正則じゃないと出てこないので、計算として違法なことをしたのか数学的に出せないからなのかで戻り値を変える?
  12. * 2次はサラスの公式使って簡略化
  13.  
  14. * 細かいこと
  15. * 行:たてに増えていく。 1行取り出したら横に長い。 row
  16. * 列:よこに増えていく。 column
  17.  
  18. -- (kssr) 3DCGなら4x4まであれば良い、んですよね?
  19. -- (Quartz) ええ。しかし情報処理とかに使いたいときは、例えば通信の符号語長次元要りますね。
  20. -- (Quartz) まあそれは後回しで。
  21. -- (Noki 基本的には、n×m行列で、それぞれの成分をもった行列を生成するメソッドを基本に、
  22. -- (Noki 各演算子に、メタテーブルで演算を定義していくのが最初の到達地点ですかね。
  23. -- (Quartz) その辺は下に貼ったvectorAPI 参考に。
  24. -- (Noki) ですね。もしかしたら、最初は2×2の行列のみを扱うのを作って、慣れるのもありかもしれません。
  25. -- (Noki) 一般の行列は、ちょっち大変そう。
  26. -- (h2)まずはnew関数および最初の行列のデータ構造かな
  27. -- (Noki) ですかね。このAPIのnewメソッドを呼び出すたびに、新しい行列を生成(インスタンスを作る)でよさそうです。
  28. -- (Noki) まんま、vectorAPIがそうですガ。
  29. -- (h2)Luaのテーブルは実値じゃなくて参照だから・・・。それを忘れてプログラム書いて変なことになった経験がw 
  30. -- (Noki) 必ず参照渡しと言うのも、潔い言語ですよねw
  31. -- (Quartz) ああ、私もあります。
  32. -- (Noki) よし、僕は場が整って満足したので、今日は撤退しますw 明日も早いので…。
  33. -- (h2)おつかれさまー
  34. -- (Noki おつかれさまです。
  35. -- (kssr)おつかれさまですー
  36. -- (Quartz) お疲れ様でしたー
  37.  
  38. -- (Quartz) プライベートな変数要りますかね
  39. -- (h2)まずは最初からmxnの行列を作るnew関数?配列いじるlocal関数がいくつかいるかな
  40. -- (Quartz) mulScalar とかの簡単なのから実装していきますか。
  41. -- (h2)forでまわすか、再帰使うか。for でいいよね。あとは中でtable.insert ? 行数と列数も保存しておけば計算速い? あぁ、入力チェックがいる。
  42. -- (k)引数でもらった行列のデータを、何も加工せずに持っておくだけってダメ?
  43. -- (Q) テーブルなので、うっかり元のを変更すると行列の中身まで変わりますね。
  44. -- (h2)じゃあ独自書式で? だめだ応用がきかないw
  45. -- (Q) いったん自身の内部に複製する必要があるような。内部的には多次元配列のかたちのテーブルで
  46. -- (Q) それぞれの行列の要素をシャローコピーでいいんじゃないでしょうか
  47. -- (h2)念のため、中身表示用のpp関数欲しいな。僕はそれから作るね。
  48. -- (Q) 中で持ってるデータは変数なにに入れます? (h2)なにとは? とりあえずおまかせで。したがいます
  49. -- (Q) あれ? いまの構造のままだとプライベートメンバ持てない?
  50. -- (k) 今の話の流れと関係ないですが、行列のデータ部分だけ取得出来たら良いと思う。(textutils.serialize()とか使って遊びたくなるかも)
  51. -- (Q) 行列のオブジェクトそれぞれが関数持っててもデータがメモリ食うので、__index ありで行きましょう。privateなメンバはなしで。
  52. -- (h)そういえば、入力データのチェック考えてないな。チェック用関数、private関数はどこに定義すればよい? らじゃ
  53. -- (h)ほかにどんなMatrixの関数を実装すればよいのかな。
  54.  
  55. 2014/08/06
  56. -- (Noki) ここって、今誰がいるのか、わかると便利ですね…。
  57. --
  58.  
  59. ]]
  60. --]]
  61.  
  62.  
  63.  
  64.  
  65. -- コードここから
  66. -- ▼▼▼▼▼▼▼▼▼▼
  67. local _new
  68. -- map関数ほしくない? map({1,2,3},{4,5,6}, plus) => {5,7,9} みたいの
  69. local map = function(self, o, func)
  70. if #self ~= #o then error("Invalid arguments: different length") end
  71. local tmp = {}
  72. for i, v in ipairs(self) do table.insert(tmp, func(v, o[i])) end
  73. return tmp
  74. end
  75. local mapAdd = function(self, o) return map(self, o, function(x,y) return x+y end) end
  76. local mapSub = function(self, o) return map(self, o, function(x,y) return x-y end) end
  77.  
  78. -- mmap({1,2,3},
  79. -- 単位行列の作成 makeIdentity(3,2) => 3x2の単位行列
  80. local makeIdentityMatrix(m,n)
  81. local tmp={}
  82. for i=1,n do
  83. local tmp2={}
  84. for j=1,m do
  85. table.insert(tmp2, ((i==j) and 1) or 0)
  86. end
  87. table.insert(tmp, tmp2)
  88. end
  89. return tmp
  90. end
  91. --
  92.  
  93. -- 2つの行列の縦横がそろっているかチェック(使わなかった
  94. local eqaulColRow = function(self, o)
  95. return (self:col()==o:col() and self:row()==o:row())
  96. end
  97.  
  98. -- おもしろいものつくったよ。名づけてマトリックスイテレータ 行列の中身を 横、縦の順に回します。(h お昼休みの終焉 では夜に
  99. -- for col,row,v in makeMatrixIterator({{1,2,3},{4,5,6},{7,8,8}) do
  100. -- print(col, ' ', row,' ',v)
  101. -- end
  102. local makeMatrixIterator =function(m)
  103. local col, row = 0,1
  104. local num_col, num_row =#(m[1]), #m
  105. return function()
  106. while row <= num_row do
  107. while col < num_col do
  108. col=col+1
  109. return col,row, m[row][col]
  110. end
  111. row = row+1
  112. col=0
  113. end
  114. end
  115. end
  116. -- matrixMap2( {{1,2,3},{4,5,6}}, (function(a,b) return (a+b) end), 1)
  117. -- => {{2,3,4},{5,6,7}}
  118. local matrixMap2 = function(m, func, ...)
  119. local tmp={}
  120. for col,row,v in makeMatrixIterator(m) do
  121. tmp[row] = tmp[row] or {}
  122. tmp[row][col]=func(v,...)
  123. end
  124. return tmp
  125. end
  126.  
  127. local matrixMap = function(m, func)
  128. local tmp = {}
  129. for r, row in ipairs(m) do
  130. local tmp2 = {}
  131. for c, val in ipairs(row) do
  132. table.insert(tmp2,func(r,c,val))
  133. end
  134. table.insert(tmp,tmp2)
  135. end
  136. return tmp
  137. end
  138.  
  139. local array = function(...)
  140. local args = {...}
  141. local a = {}
  142. if #args == 1 then
  143. for i=1,args[1] do
  144. a[i]=0
  145. end
  146. else
  147. local n = table.remove(args,1)
  148. for i=1,n do
  149. a[i]=array(unpack(args))
  150. end
  151. end
  152. return a
  153. end
  154.  
  155. local isSquare = function(m) return m:row() == m:col() end
  156. local inv; -- 長さnの二次元配列をn次正方行列としてみなして逆行列を返す
  157. local cofactor;
  158. local det = function(m) -- 長さnの二次元配列をn次正方行列としてみなして行列式を返す
  159. if #m == 2 then return m[1][1]*m[2][2]-m[1][2]*m[2][1] end
  160. local ret = 0
  161. for i=1,#m do
  162. ret = ret + confactor(m,i,1)*m[i][1]
  163. end
  164. return ret
  165. end
  166.  
  167. local inv = function(m)
  168. local d = det(m)
  169. if d == 0 then return nil end
  170. local tmp = {}
  171. for r = 1,#m do
  172. local tmp2 = {}
  173. for c = 1,#m do
  174. table.insert(tmp2, cofactor(m,c,r)/d)
  175. end
  176. table.insert(tmp, tmp2)
  177. end
  178. return tmp
  179. end
  180.  
  181. cofactor = function(m, i, j) -- 長さnの二次元配列(n次正方行列)m の i,j 余因子
  182. local tmp = {}
  183. for r = 1,#m do
  184. if r ~= i then
  185. local tmp2 = {}
  186. for c = 1,#m do
  187. if c ~= j then
  188. table.insert(tmp2, m[r][c])
  189. end
  190. end
  191. table.insert(tmp, tmp2)
  192. end
  193. end
  194. return -1^(i+j)*det(tmp)
  195. end
  196.  
  197. -- validation 関数が必要だと思う 全ての行(row)の各要素が同じ数であることを確認。配列の形が直方体であること(?)
  198. local isMatrix = function(tbl)
  199. if type(tbl)~="table" or #tbl==0 then return false end
  200. local numArg=#tbl[1]
  201. for i,v in ipairs(tbl) do
  202. if #v~=numArg then return false end
  203. end
  204. return true
  205. end
  206.  
  207.  
  208. local matrix = {
  209. add = function(self, o) return _new(map(self, o, mapAdd)) end,
  210. sub = function(self, o) return _new(map(self, o, mapSub)) end,
  211.  
  212. mul = function( self, o ) --掛け算 mul( m1, m2 ) => m3
  213. local r = self:row()
  214. local n = self:col()
  215. local c = o:col()
  216. local tmp = {}
  217. for i=1,r do
  218. local tmp2 = {}
  219. for j=1,c do
  220. local s = 0
  221. for k=1,n do
  222. s = s+self._a[i][k]*o._a[k][j]
  223. end
  224. table.insert(tmp2,s)
  225. end
  226. table.insert(tmp,tmp2)
  227. end
  228. return _new(tmp)
  229. end,
  230. muls = function( self, n ) --(行列 x 数)、または(数 x 行列) muls( m1, n ) => m2
  231. local tmp = {}
  232. for i,v in ipairs(self._a) do
  233. local tmp2 = {}
  234. for j,w in ipairs(v) do
  235. table.insert(tmp2, n*w)
  236. end
  237. table.insert(tmp, tmp2)
  238. end
  239. return _new(tmp)
  240. end,
  241. det = function( self ) -- 行列式? det( m ) => num, 1次元はスカラー、2次元たすきがけ、3次元はサラス、4次って?
  242. -- 3 次以上は余因子行列を用いて算出します
  243. if not isSquare(self) then return nil end
  244. return det(self._a)
  245. end,
  246. inv = function( self ) -- 逆行列 inv(m1) => m2
  247. if not isSquare(self) then return nil end
  248. return _new(inv(self._a))
  249. end,
  250. toArray = function( self )
  251. local tmp = {}
  252. for i,v in ipairs(self._a) do
  253. table.insert(tmp, {unpack(v)})
  254. end
  255. return tmp
  256. end,
  257. clone = function( self )
  258. return _new( self._a )
  259. end,
  260. tostring = function( self ) --一瞬 textutils.serialize使おうとおもったけど、行列としてみづらいw
  261. local tmp=""; local sep=" " -- じゃあ、末端のスペースそのままでもいいね?(邪悪顔 ありりw もち
  262. for i,row in ipairs( self ) do -- 入れ子が深い関数には罪悪感を感じる
  263. for j,col in ipairs( row ) do -- 表示用セパレータ(横)はカンマ?スペース? 一般にはスペースのようです ok
  264. tmp=tmp..col..sep --(k)桁をそろえるように機能追加していい? (h2)おねがい。一瞬考えたけど、全部の要素を一旦みるのめんd
  265. -- (k)あとでtostring2をつくっておきます。とかいいつつ今日はもう寝ます。お先です。(h)新しいのできたらこの関数を消して上書きしてください。
  266. end
  267. tmp=tmp..(i~=#self and "\n" or "")
  268. end
  269. return tmp
  270. end,
  271. round = function(self, nTolerance) -- 各要素の小数値を丸める。nToleranceは公差。round(m)=>全ての要素を整数に丸めた行列
  272. local nTolerance = nTolerance or 1.0
  273. local _round = function(num)
  274. return math.floor( num + ((nTolerance*0.5)/nTolerance) * nTolerance)
  275. end
  276. return _new( matrixMap2(self, _round) )
  277. end,
  278. row = function(self) return #self end,
  279. col = function(self) return #(self[1]) end -- 各行データの長さは同じだって信じてる
  280. }
  281.  
  282.  
  283. -- debug 用
  284. -- debug用の行列表示用ヘルパー pretty print matrix
  285. local ppm = function(m) print(matrix.tostring(m)) end
  286.  
  287. --[[
  288. -- 和と差の検証用コード(h2 ではおやすみ ノシ
  289. a={{1,2,1},{1,3,5},{1,8,3} }
  290. b={{3,2,1},{3,2,1},{3,2,1} }
  291. c={{1.533,9,1.0001},{3.499,900.3,0.01}}
  292.  
  293. ppm(a)
  294. ppm(b)
  295. ppm(a:add(b))
  296. ppm(a:sub(b))
  297. ppm(c)
  298. ppm(round(c))
  299.  
  300. --]]
  301.  
  302.  
  303.  
  304.  
  305.  
  306. local mmetatable = {
  307. __index = matrix,
  308. __add = matrix.add,
  309. __sub = matrix.sub,
  310. __mul = function( s, o ) return type(o) == "table" and s:mul(o) or s:muls(o) end,
  311. __unm = function( m ) return v:mul(-1) end,
  312. __tostring = matrix.tostring,
  313. }
  314. -- ex. new({{1,1,1},{1,1,1},{1,1,1}}) h)こちらのほうがいいね
  315. -- unpackで一回分解しているから違うテーブルになるはずだけど
  316. -- (Q) ええ、これでいいと思います
  317. _new = function(...)
  318. local args = {...}
  319. local m = {
  320. _a = {},
  321. }
  322. local tbl
  323. if type(args[1]) == "table" then
  324. if args[1].toArray then
  325. tbl = args[1]:toArray()
  326. else tbl = args[1] end
  327. for i,v in ipairs(tbl) do
  328. table.insert(m._a, {unpack(v)})
  329. end
  330. else
  331.  
  332. end
  333. setmetatable( m, mmetatable )
  334. return m
  335. end
  336.  
  337. new = _new
  338.  
  339. -- ▲▲▲▲▲▲▲▲▲▲
  340. -- コードここまで
  341.  
  342. -- 参考用 vector API
  343.  
  344. local vector = {
  345. add = function( self, o )
  346. return vector.new(
  347. self.x + o.x,
  348. self.y + o.y,
  349. self.z + o.z
  350. )
  351. end,
  352. sub = function( self, o )
  353. return vector.new(
  354. self.x - o.x,
  355. self.y - o.y,
  356. self.z - o.z
  357. )
  358. end,
  359. mul = function( self, m )
  360. return vector.new(
  361. self.x * m,
  362. self.y * m,
  363. self.z * m
  364. )
  365. end,
  366. dot = function( self, o )
  367. return self.x*o.x + self.y*o.y + self.z*o.z
  368. end,
  369. cross = function( self, o )
  370. return vector.new(
  371. self.y*o.z - self.z*o.y,
  372. self.z*o.x - self.x*o.z,
  373. self.x*o.y - self.y*o.x
  374. )
  375. end,
  376. length = function( self )
  377. return math.sqrt( self.x*self.x + self.y*self.y + self.z*self.z )
  378. end,
  379. normalize = function( self )
  380. return self:mul( 1 / self:length() )
  381. end,
  382. round = function( self, nTolerance )
  383. nTolerance = nTolerance or 1.0
  384. return vector.new(
  385. math.floor( (self.x + (nTolerance * 0.5)) / nTolerance ) * nTolerance,
  386. math.floor( (self.y + (nTolerance * 0.5)) / nTolerance ) * nTolerance,
  387. math.floor( (self.z + (nTolerance * 0.5)) / nTolerance ) * nTolerance
  388. )
  389. end,
  390. tostring = function( self )
  391. return self.x..","..self.y..","..self.z
  392. end,
  393. }
  394.  
  395. local vmetatable = {
  396. __index = vector,
  397. __add = vector.add,
  398. __sub = vector.sub,
  399. __mul = vector.mul,
  400. __unm = function( v ) return v:mul(-1) end,
  401. __tostring = vector.tostring,
  402. }
  403.  
  404. function new( x, y, z )
  405. local v = {
  406. x = x or 0,
  407. y = y or 0,
  408. z = z or 0
  409. }
  410. setmetatable( v, vmetatable )
  411. return v
  412. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement