线性代数学习六,通过初等行变换对矩阵求逆

  这是另一种矩阵求逆的方法,其基本思想是若对A矩阵求逆,则在其右边附加一个同型的单位矩阵,然后对这个合并了的矩阵进行初等行变换至行最简形,此时再分割这个矩阵,矩阵左边的应该是一个单位矩阵,而右边的则是一就是原来A矩阵的逆。下面是相关的Lua代码,与以前代码相同的部分省略。

  1. -- Merge matrices horizontally
  2. MatrixMeta.merge=function(this,other)
  3.         if this.row~=other.row or this.column ~= other.column then
  4.                 return nil
  5.         end
  6.  
  7.         local data={}
  8.         for i=1,this.row do
  9.                 for j=1,this.column do
  10.                         table.insert(data,this:at(i,j))
  11.                 end
  12.  
  13.                 for j=1,this.column do
  14.                         table.insert(data,other:at(i,j))
  15.                 end
  16.         end
  17.  
  18.         return Matrix(data,this.row,this.row*2)
  19. end
  20.  
  21. -- Divide the matrix into two parts
  22. MatrixMeta.divide=function(this)
  23.         if this.column~=2*this.row then
  24.                 return nil
  25.         end
  26.  
  27.         local a={}
  28.         local b={}
  29.         local remainder
  30.         for i,v in ipairs(this.data) do
  31.                 remainder=i%this.column
  32.                 if remainder~=0 and remainder <= this.column/2 then
  33.                         table.insert(a,v)
  34.                 else
  35.                         table.insert(b,v)
  36.                 end
  37.         end
  38.        
  39.         a=Matrix(a,this.row,this.row)
  40.         b=Matrix(b,this.row,this.row)
  41.         return a,b
  42. end
  43.  
  44. -- Another way to get inversed matrix
  45. MatrixMeta.inverse2=function(this)
  46.         if this.row ~= this.column or Determinant(this.data):eval()==0 then
  47.                 return nil
  48.         end
  49.  
  50.         local e={}
  51.         for i=1,this.row-1 do      
  52.                 table.insert(e,1)              
  53.                 for j=1,this.row do
  54.                         table.insert(e,0)
  55.                 end
  56.         end
  57.         table.insert(e,1)
  58.        
  59.         e=Matrix(e,this.row,this.column)       
  60.         local merged=this:merge(e)
  61.         merged:reduce()
  62.         local a,b=merged:divide()
  63.         return b
  64. end
  65.  
  66.  
  67.  

Algorithm Comments(0) 2008年8月27日 12:18

线性代数学习五,向量的运算与用施密特正交化方法从线性无关组导出正交向量

这里充分使用了Lua的元表来实现Lua风格的面向对象程序设计,用Lua编程的感觉还是不错的。

  1. #!/usr/bin/env lua
  2.  
  3. VectorMeta={}
  4.  
  5. VectorMeta.__add=function(a,b)
  6.         local size=table.maxn(a)
  7.         assert(size==table.maxn(b))
  8.  
  9.         local data={}
  10.         for i=1,size do
  11.                 table.insert(data,a[i]+b[i])
  12.         end
  13.  
  14.         return Vector(data)
  15. end
  16.  
  17. VectorMeta.__sub=function(a,b)
  18.         local size=table.maxn(a)
  19.         assert(size==table.maxn(b))
  20.  
  21.         local data={}
  22.         for i=1,size do
  23.                 table.insert(data,a[i]-b[i])
  24.         end
  25.  
  26.         return Vector(data)
  27. end
  28.  
  29. VectorMeta.__mul=function(a,b)
  30.         if type(a)=="number" then
  31.                 local data={}
  32.                 for _,v in ipairs(b) do
  33.                         table.insert(data,a*v)
  34.                 end
  35.                 return Vector(data)
  36.         else
  37.                 local size=table.maxn(a)
  38.                 assert(size==table.maxn(b))
  39.                
  40.                 local sum=0
  41.                 for i=1,size do
  42.                         sum=sum+a[i]*b[i]
  43.                 end
  44.  
  45.                 return sum
  46.         end
  47. end
  48.  
  49. VectorMeta.__index=function(data,index)
  50.         if type(index)=="number" then
  51.                 return data[index]
  52.         else
  53.                 return VectorMeta[index]
  54.         end
  55. end
  56.  
  57. VectorMeta.length=function(data)
  58.         local sum=0
  59.         for _,v in ipairs(data) do
  60.                 sum=sum+v*v
  61.         end
  62.         return math.sqrt(sum)
  63. end
  64.  
  65. VectorMeta.print=function(this)
  66.         io.write('[')
  67.         for i=1,table.maxn(this)-1 do
  68.                 io.write(this[i],',')
  69.         end
  70.         io.write(this[table.maxn(this)],"]\n")
  71. end
  72.  
  73. -- Use Schmitt method to orthogonalize vectors
  74. VectorMeta.orthogonalize=function(...)
  75.         local alphas=arg
  76.         local betas={arg[1]}
  77.         for i=2,table.maxn(alphas) do
  78.                 local beta=alphas[i]
  79.                 for j=1,i-1 do
  80.                         beta=beta-((alphas[i]*betas[j])/(betas[j]*betas[j]))*betas[j]
  81.                 end
  82.                 assert(type(beta)=="table")
  83.                 table.insert(betas,beta)
  84.         end
  85.         return betas
  86. end
  87.  
  88. Vector=function(data)
  89.         setmetatable(data,VectorMeta)
  90.         return data
  91. end
  92.  

Algorithm Comments(0) 2008年8月25日 09:17

线性代数学习四,矩阵的运算和求逆

矩阵的运算是通过Lua的metatable来完成的,用起来和C++的操作符重载一样方便,而矩阵的求逆就是通过求伴随矩阵再除以其行列式的值而得。即

A^{-1}=\frac{1}{|A|}A^{*}

在实现过程中遇到了一些麻烦,有一个是我的对于伴随矩阵的理解有问题,原来伴随矩阵不仅仅要每个元素由其代数余子式代替,还得转置一下。

 

  1. #!/usr/bin/env lua
  2.  
  3. require 'determinant'
  4.  
  5. function Matrix(data,row,column)
  6.         local size=table.maxn(data)
  7.         assert(size==row*column)
  8.  
  9.         local matrix={}
  10.         matrix.data=data
  11.         matrix.row=row
  12.         matrix.column=column
  13.  
  14.         matrix.at=function(this,row,column)
  15.                 return this.data[(row-1)*this.column+column]
  16.         end
  17.  
  18.         matrix.set=function(this,row,column,num)
  19.                 this.data[(row-1)*this.column+column]=num
  20.         end
  21.  
  22.         matrix.swaprow=function(this,r1,r2)
  23.                 local temp
  24.                 for i=1,this.column do
  25.                         temp=this:at(r1,i)
  26.                         this:set(r1,i,this:at(r2,i))
  27.                         this:set(r2,i,temp)
  28.                 end
  29.         end
  30.  
  31.         matrix.simplifycolumn=function(this,column,layer)
  32.                 if this:at(layer,column) == 0 then
  33.                         for i=layer+1,this.row do
  34.                                 if this:at(i,column) ~= 0 then
  35.                                         this:swaprow(i,layer)
  36.                                         break
  37.                                 end
  38.                         end
  39.                 end
  40.  
  41.                 if this:at(layer,column) == 0 then
  42.                         return layer
  43.                 end
  44.  
  45.                 for i=1,this.row do
  46.                         if i~= layer then
  47.                                 local tomultiply=-this:at(i,column)/this:at(layer,column)
  48.                                 for j=1,this.column do
  49.                                         this:set(i,j,this:at(layer,j)*tomultiply+this:at(i,j))
  50.                                 end
  51.                         end
  52.                 end
  53.  
  54.                 return layer+1
  55.         end
  56.  
  57.         matrix.swapcolumn=function(this,c1,c2)
  58.                 local temp
  59.                 for i=1,this.row do
  60.                         temp=this:at(i,c1)
  61.                         this:set(i,c1,this:at(i,c2))
  62.                         this:set(i,c2,temp)
  63.                 end
  64.         end
  65.  
  66.         matrix.simplifyrow=function(this,row,layer)
  67.                 if this:at(row,layer) == 0 then
  68.                         for i=layer+1,this.column do
  69.                                 if this:at(row,i) ~= 0 then
  70.                                         this:swapcolumn(i,layer)
  71.                                         break
  72.                                 end
  73.                         end
  74.                 end
  75.                
  76.                 if this:at(row,layer) == 0 then
  77.                         return layer
  78.                 end
  79.  
  80.                 for i=1,this.column do
  81.                         if i ~= layer then
  82.                                 local tomultiply=-this:at(row,i)/this:at(row,layer)
  83.                                 for j=1,this.row do
  84.                                         this:set(j,i,this:at(j,layer)*tomultiply+this:at(j,i))
  85.                                 end
  86.                         end
  87.                 end
  88.  
  89.                 return layer+1
  90.         end
  91.  
  92.         -- Make the matrix to reduced form
  93.         matrix.reduce=function(this)
  94.                 local layer=1
  95.                 for i=1,this.column do
  96.                         layer=this:simplifycolumn(i,layer)
  97.                         if layer>this.row then
  98.                                 break
  99.                         end
  100.                 end
  101.                
  102.                
  103.                 for i=1,this.row do
  104.                         local tomultiply=nil
  105.                         for j=1,this.column do
  106.                                 if tomultiply then
  107.                                         this:set(i,j,this:at(i,j)*tomultiply)
  108.                                 elseif this:at(i,j)~= 0 then
  109.                                         tomultiply=1/this:at(i,j)
  110.                                         this:set(i,j,1)
  111.                                 end
  112.                         end
  113.                 end     
  114.         end
  115.  
  116.         -- Make the matrix to canonical form
  117.         matrix.canonicalize=function(this)
  118.                 this:reduce()
  119.                 local layer=1
  120.                 for i=1,this.row do
  121.                         layer=this:simplifyrow(i,layer)
  122.                         if layer>this.column then
  123.                                 break
  124.                         end
  125.                 end     
  126.         end
  127.  
  128.         -- Get the rank of the matrix
  129.         -- Matrix should be reduced before.
  130.         matrix.rank=function(this)
  131.                 local rank=0
  132.                 for i=1,this.row do
  133.                         for j=1,this.column do
  134.                                 if this:at(i,j) ~= 0 then
  135.                                         rank=rank+1
  136.                                         break
  137.                                 end
  138.                         end
  139.                 end
  140.  
  141.                 return rank
  142.         end
  143.  
  144.         matrix.print=function(this)
  145.                 for i=1,this.row do
  146.                         for j=1,this.column do
  147.                                 io.write(this:at(i,j),'\t')
  148.                         end
  149.                         io.write('\n')
  150.                 end
  151.         end
  152.  
  153.         -- Get the transposed matrix
  154.         matrix.transpose=function(this)
  155.                 local data={}
  156.  
  157.                 for i=1,this.column do
  158.                         for j=1,this.row do
  159.                                 table.insert(data,this:at(j,i))
  160.                         end
  161.                 end
  162.  
  163.                 return Matrix(data,this.column,this.row)
  164.         end
  165.  
  166.         matrix.cofactor=function(this,row,column)
  167.                 local data={}
  168.  
  169.                 for i=1,this.row do
  170.                         for j=1,this.column do
  171.                                 if i~=row and j~=column then
  172.                                         table.insert(data,this:at(i,j))
  173.                                 end
  174.                         end
  175.                 end
  176.  
  177.                 if (row+column)%2==0 then
  178.                         return Determinant(data):eval()
  179.                 else
  180.                         return -Determinant(data):eval()
  181.                 end
  182.         end
  183.  
  184.         -- Get the adjoint matrix of this matrix
  185.         matrix.adjoint=function(this)
  186.                 local data={}
  187.                 for i=1,this.row do
  188.                         for j=1,this.column do
  189.                                 table.insert(data,this:cofactor(i,j))
  190.                         end
  191.                 end
  192.  
  193.                 return Matrix(data,this.row,this.column):transpose()
  194.         end
  195.  
  196.         -- Get the inverse matrix of this matrix
  197.         -- If this matrix is not invertible, nil is returned
  198.         matrix.inverse=function(this)
  199.                 -- Only array can be invertible
  200.                 if this.row ~= this.column then
  201.                         return nil
  202.                 end
  203.  
  204.                 local det=Determinant(this.data):eval();
  205.                 if det==0 then
  206.                         return nil
  207.                 end
  208.                
  209.                 return (1/det)*this:adjoint()
  210.         end
  211.  
  212.         if MatrixCalc then
  213.                 setmetatable(matrix,MatrixCalc)
  214.         end
  215.  
  216.         return matrix
  217. end
  218.  
  219. -- The matrix calculation metatable
  220. MatrixCalc={}
  221.  
  222. MatrixCalc.__add=function(a,b)
  223.         assert(a.row==b.row and a.column==b.column)
  224.         local data={}
  225.  
  226.         for i=1,a.row*a.column do
  227.                 table.insert(data,a.data[i]+b.data[i])
  228.         end
  229.  
  230.         return Matrix(data,a.row,a.column)
  231. end
  232.  
  233. MatrixCalc.__sub=function(a,b)
  234.         assert(a.row==b.row and a.column==b.column)
  235.         local data={}
  236.  
  237.         for i=1,a.row*a.column do
  238.                 table.insert(data,a.data[i]-b.data[i])
  239.         end
  240.  
  241.         return Matrix(data,a.row,a.column)
  242. end
  243.  
  244. MatrixCalc.__unm=function(a)
  245.         local data={}
  246.  
  247.         for i=1,a.row*a.column do
  248.                 table.insert(data,-a.data[i])
  249.         end
  250.  
  251.         return Matrix(data,a.row,a.column)
  252. end
  253.  
  254. MatrixCalc.__mul=function(a,b)
  255.         if tonumber(a) then
  256.                 -- a is a number
  257.                 local data={}
  258.                 for i=1,b.row*b.column do
  259.                         table.insert(data,a*b.data[i])
  260.                 end
  261.                 return Matrix(data,b.row,b.column)
  262.         else
  263.                 -- a is a matrix
  264.                 assert(a.column==b.row)
  265.                
  266.                 local data={}
  267.                 for i=1,a.row do
  268.                         for j=1,b.column do
  269.                                 local value=0
  270.                                 for m=1,a.column do
  271.                                         value=value+a:at(i,m)*b:at(m,j)
  272.                                 end
  273.                                 table.insert(data,value)
  274.                         end
  275.                 end
  276.  
  277.                 return Matrix(data,a.row,b.column)
  278.         end
  279. end
  280.  
  281. MatrixCalc.__eq=function(a,b)
  282.         if a.row~=b.row or a.row~=b.column then
  283.                 return false
  284.         end
  285.  
  286.         for i=1,a.row*a.column do
  287.                 if a.data[i]~=b.data[i] then
  288.                         return false
  289.                 end
  290.         end
  291.  
  292.         return true
  293. end

Algorithm Comments(0) 2008年8月23日 07:47

线性代数学习三,将矩阵变为行最简形,标准形以及计算矩阵的秩

  1. #!/usr/bin/env lua
  2.  
  3. function Matrix(data,row,column)
  4.         local size=table.maxn(data)
  5.         assert(size==row*column)
  6.  
  7.         local matrix={}
  8.         matrix.data=data
  9.         matrix.row=row
  10.         matrix.column=column
  11.  
  12.         matrix.at=function(this,row,column)
  13.                 return this.data[(row-1)*this.column+column]
  14.         end
  15.  
  16.         matrix.set=function(this,row,column,num)
  17.                 this.data[(row-1)*this.column+column]=num
  18.         end
  19.  
  20.         matrix.swaprow=function(this,r1,r2)
  21.                 local temp
  22.                 for i=1,this.column do
  23.                         temp=this:at(r1,i)
  24.                         this:set(r1,i,this:at(r2,i))
  25.                         this:set(r2,i,temp)
  26.                 end
  27.         end
  28.  
  29.         matrix.simplifycolumn=function(this,column,layer)
  30.                 if this:at(layer,column) == 0 then
  31.                         for i=layer+1,this.row do
  32.                                 if this:at(i,column) ~= 0 then
  33.                                         this:swaprow(i,layer)
  34.                                         break
  35.                                 end
  36.                         end
  37.                 end
  38.  
  39.                 if this:at(layer,column) == 0 then
  40.                         return layer
  41.                 end
  42.  
  43.                 for i=1,this.row do
  44.                         if i~= layer then
  45.                                 local tomultiply=-this:at(i,column)/this:at(layer,column)
  46.                                 for j=1,this.column do
  47.                                         this:set(i,j,this:at(layer,j)*tomultiply+this:at(i,j))
  48.                                 end
  49.                         end
  50.                 end
  51.  
  52.                 return layer+1
  53.         end
  54.  
  55.         matrix.swapcolumn=function(this,c1,c2)
  56.                 local temp
  57.                 for i=1,this.row do
  58.                         temp=this:at(i,c1)
  59.                         this:set(i,c1,this:at(i,c2))
  60.                         this:set(i,c2,temp)
  61.                 end
  62.         end
  63.  
  64.         matrix.simplifyrow=function(this,row,layer)
  65.                 if this:at(row,layer) == 0 then
  66.                         for i=layer+1,this.column do
  67.                                 if this:at(row,i) ~= 0 then
  68.                                         this:swapcolumn(i,layer)
  69.                                         break
  70.                                 end
  71.                         end
  72.                 end
  73.                
  74.                 if this:at(row,layer) == 0 then
  75.                         return layer
  76.                 end
  77.  
  78.                 for i=1,this.column do
  79.                         if i ~= layer then
  80.                                 local tomultiply=-this:at(row,i)/this:at(row,layer)
  81.                                 for j=1,this.row do
  82.                                         this:set(j,i,this:at(j,layer)*tomultiply+this:at(j,i))
  83.                                 end
  84.                         end
  85.                 end
  86.  
  87.                 return layer+1
  88.         end
  89.  
  90.         -- Make the matrix to reduced form
  91.         matrix.reduce=function(this)
  92.                 local layer=1
  93.                 for i=1,this.column do
  94.                         layer=this:simplifycolumn(i,layer)
  95.                         if layer>this.row then
  96.                                 break
  97.                         end
  98.                 end
  99.                
  100.                
  101.                 for i=1,this.row do
  102.                         local tomultiply=nil
  103.                         for j=1,this.column do
  104.                                 if tomultiply then
  105.                                         this:set(i,j,this:at(i,j)*tomultiply)
  106.                                 elseif this:at(i,j)~= 0 then
  107.                                         tomultiply=1/this:at(i,j)
  108.                                         this:set(i,j,1)
  109.                                 end
  110.                         end
  111.                 end     
  112.         end
  113.  
  114.         -- Make the matrix to canonical form
  115.         matrix.canonicalize=function(this)
  116.                 this:reduce()
  117.                 local layer=1
  118.                 for i=1,this.row do
  119.                         layer=this:simplifyrow(i,layer)
  120.                         if layer>this.column then
  121.                                 break
  122.                         end
  123.                 end     
  124.         end
  125.  
  126.         -- Get the rank of the matrix
  127.         -- Matrix should be reduced before.
  128.         matrix.rank=function(this)
  129.                 local rank=0
  130.                 for i=1,this.row do
  131.                         for j=1,this.column do
  132.                                 if this:at(i,j) ~= 0 then
  133.                                         rank=rank+1
  134.                                         break
  135.                                 end
  136.                         end
  137.                 end
  138.  
  139.                 return rank
  140.         end
  141.  
  142.         matrix.print=function(this)
  143.                 for i=1,this.row do
  144.                         for j=1,this.column do
  145.                                 io.write(this:at(i,j),'\t')
  146.                         end
  147.                         io.write('\n')
  148.                 end
  149.         end
  150.  
  151.         return matrix
  152. end

Algorithm Comments(0) 2008年8月23日 05:00

线性代数学习二,用克拉默法则求解线性方程组

  这是书上1.4的内容,进度得加快

  1. #!/usr/bin/env lua
  2.  
  3. require 'determinant'
  4.  
  5. function Cramer(n)
  6.         assert(n>0)
  7.  
  8.         local cramer={}
  9.  
  10.         -- the coefficient determinant
  11.         cramer.D={}
  12.  
  13.         -- this D1,D2,D3...
  14.         cramer.Ds={}
  15.         for i=1,n do
  16.                 table.insert(cramer.Ds,{})
  17.         end
  18.  
  19.         -- this number of unknown numbers
  20.         cramer.n=n
  21.  
  22.         -- add an equation
  23.         -- for example:
  24.         -- x1+2x2-x3+3x4=2
  25.         -- use cramer:add(1,2,-1,3,2)
  26.         cramer.add=function(this,...)
  27.                 local toadd={...}
  28.                 assert(table.maxn(toadd)==this.n+1)
  29.  
  30.                 for i=1,this.n do
  31.                         table.insert(this.D,toadd[i])
  32.                         for j=1,this.n do
  33.                                 table.insert(this.Ds[j],toadd[i])
  34.                         end
  35.                 end
  36.  
  37.                 local last=toadd[this.n+1];
  38.                 local times=table.maxn(this.D)/this.n
  39.                 for i=1,this.n do
  40.                         this.Ds[i][(times-1)*this.n+i]=last
  41.                 end
  42.         end
  43.  
  44.         -- Use Cramer Rules to solve linear equations
  45.         -- When solution does not exist, nil is returned,
  46.         -- and solution table returned otherwise.
  47.         cramer.solve=function(this)
  48.                 assert(table.maxn(this.D)==this.n*this.n)
  49.  
  50.                 local D=Determinant(this.D):eval();
  51.                 if D==0 then
  52.                         return nil
  53.                 end
  54.  
  55.                 local solutions={}
  56.                 local Dn;
  57.                 for i=1,this.n do
  58.                         Dn=Determinant(this.Ds[i]):eval();
  59.                         table.insert(solutions,Dn/D)
  60.                 end
  61.  
  62.                 return solutions;
  63.         end
  64.  
  65.         return cramer
  66. end

下面是一个例子:

 

  1. cramer=Cramer(4)
  2. cramer:add(1,2,-1,3,2)
  3. cramer:add(2,-1,3,-2,7)
  4. cramer:add(0,3,-1,1,6)
  5. cramer:add(1,-1,1,4,-4)
  6.  
  7. solutions=cramer:solve()
  8. for _,v in ipairs(solutions) do
  9.         print(v)
  10. end

输出是1,3,2,-1,符合书上答案

Algorithm Comments(0) 2008年8月22日 11:00

线性代数学习一,计算行列式

复习线性代数,写了一个计算行列式的Lua脚本:

  1. #!/usr/bin/env lua
  2.  
  3. -- This is the constructor of a determinant
  4. -- It get an array of numbers as argument
  5. -- And add methods for it
  6. function Determinant(data)
  7.         local det={}
  8.         det.row=math.sqrt(table.maxn(data))
  9.  
  10.         -- If the number is not proper, raise error
  11.         if math.floor (det.row) ~= det.row then
  12.                 error("Determinant's data number must be n^2!")
  13.         end
  14.  
  15.         det.data=data
  16.        
  17.         -- index function
  18.         det.at=function(this,row,column)
  19.                 return this.data[(row-1)*this.row+column];
  20.         end
  21.        
  22.         det.cofactor=function(this,row,column)
  23.                 local cofactor={}
  24.                 local r=1
  25.                 local c=0
  26.                 for _,v in ipairs(this.data) do
  27.                         c=c+1;
  28.                        
  29.                         if c==this.row+1 then
  30.                                 r=r+1
  31.                                 c=1
  32.                         end
  33.  
  34.                         if not (r==row or c==column) then
  35.                                 table.insert(cofactor,v)
  36.                         end
  37.                 end
  38.                
  39.                 return cofactor
  40.         end     
  41.  
  42.         -- evaluation function
  43.         det.eval=function(this)
  44.                 local a=this.data
  45.                 -- this is the simplest case
  46.                 if this.row==2 then
  47.                         return a[1]*a[4]-a[2]*a[3]
  48.                
  49.                 -- for accerleration
  50.                 elseif this.row==3 then
  51.                         return a[1]*a[5]*a[9]+a[3]*a[4]*a[8]+a[2]*a[6]*a[7]-a[3]*a[5]*a[7]-a[1]*a[6]*a[8]-a[2]*a[4]*a[9]
  52.                 else
  53.                         local value=0
  54.                         local cofactor=0
  55.                         local add=true
  56.  
  57.                         for i=1,this.row do
  58.                                 cofactor=Determinant(this:cofactor(1,i))
  59.                                 cofactor=this.data[i]*cofactor:eval()
  60.                                
  61.                                 if add then
  62.                                         value=value+cofactor
  63.                                 else
  64.                                         value=value-cofactor
  65.                                 end
  66.                                 add = not add
  67.                         end
  68.  
  69.                         return value
  70.                 end
  71.         end                         
  72.        
  73.         return det
  74. end
  75.  
  76.  

 

Algorithm Comments(0) 2008年8月22日 09:15

一个算24点的算法

  最近,我用Lua写了一个算24点的算法,这个算法的思想很简单,就是不断穷举表达式,然后计算每个表达式的值是否为24。如果为24,则输出。问题的难点在于如何穷举表达式,我的做法是将要处理的数和另外穷举出的运算符组成一个含有七个元素的数组(四个操作数,三个操作符),然后遍历这个数据的全排列,将每个排列看成一个逆波兰式,如果计算成功,则将逆波兰式再转成中辍表达式输出。这个算法有一个明显的缺点,它会产生很多冗余的式子(即形式不同,但运算实质相同)。

  1. #!/usr/bin/env lua
  2.  
  3. -- used for store four points
  4. array={}
  5.  
  6. -- reverse the whole array from first to last
  7. function array.reverse(this,first,last)
  8.         first = first or 1
  9.         last = last or table.maxn(this)
  10.         while first < last do
  11.                 this[first],this[last]=this[last],this[first]
  12.                 first=first+1
  13.                 last=last-1
  14.         end
  15. end
  16.  
  17. -- this algorithm is modified from the C++ STL
  18. -- change array to next permutation and returns true
  19. -- return false if the current is the last permutation
  20. function array.next_permutation(this,first,last)
  21.         local len=table.maxn(this)
  22.         if len==0 or len==1  then
  23.                 return false
  24.         end
  25.  
  26.         first = first or 1
  27.         last = last or len
  28.        
  29.         local i=last
  30.         while true do
  31.                 local ii=i
  32.                 i=i-1
  33.                 if this[i] < this[ii] then
  34.                         local j=last
  35.                         while this[i] >= this[j] do
  36.                                 j=j-1
  37.                         end
  38.                         this[i],this[j]=this[j],this[i]
  39.                         this:reverse(ii,last)
  40.                         return true
  41.                 end
  42.  
  43.                 if i==first then
  44.                         return false
  45.                 end
  46.         end
  47. end
  48.  
  49. -- show the array with space seperated
  50. function array.show(this)
  51.         for _,i in ipairs(this) do
  52.                 io.write(i,' ')
  53.         end
  54.         io.write('\n')
  55. end
  56.  
  57. optable={"+","-","*","/"}
  58. opfuncs={}
  59. opfuncs["+"]=function(a,b)
  60.         return a+b
  61. end
  62.  
  63. opfuncs["-"]=function(a,b)
  64.         return a-b
  65. end
  66.  
  67. opfuncs["*"]=function(a,b)
  68.         return a*b
  69. end
  70.  
  71. opfuncs["/"]=function(a,b)
  72.         if b==0 then
  73.                 return nil
  74.         else
  75.                 return a/b
  76.         end
  77. end
  78.  
  79.  
  80. -- stack class for calculation
  81. function Stack()
  82.         local stack={}
  83.         stack.push=table.insert
  84.         stack.pop=table.remove
  85.         stack.size=table.maxn
  86.         stack.bottom=function(this)
  87.                 return this[1]
  88.         end
  89.         return stack
  90. end
  91.  
  92. -- calculate from polish notation
  93. function array.eval(toeval)
  94.         local stack=Stack()
  95.         for _,v in ipairs(toeval) do
  96.                 local op=opfuncs[v]
  97.                 if op then
  98.                         if stack:size() <2 then
  99.                                 return nil
  100.                         end
  101.                         local b=stack:pop()
  102.                         local a=stack:pop()
  103.                         local result=op(a,b)
  104.                         if result then
  105.                                 stack:push(result)
  106.                         else
  107.                                 return nil
  108.                         end
  109.                 else
  110.                         stack:push(tonumber(v))
  111.                 end
  112.         end
  113.  
  114.         return stack:bottom()
  115. end
  116.  
  117. -- precedence table, "n" is number
  118. precedence={
  119.         ["+"] = 1;
  120.         ["-"] = 1;
  121.         ["*"] = 2;
  122.         ["/"] = 2;
  123.         ["n"] = 3;
  124. }
  125.  
  126. -- convert from polish expression to normal expression
  127. function array.show_expr(this)
  128.         local strStack=Stack()
  129.         local typeStack=Stack()
  130.        
  131.         for _,v in ipairs(this) do
  132.                 if opfuncs[v] then
  133.                         local b=strStack:pop()
  134.                         local a=strStack:pop()
  135.  
  136.                         local bp=precedence[typeStack:pop()]
  137.                         local ap=precedence[typeStack:pop()]
  138.                         local vp=precedence[v]
  139.                        
  140.                         if ap<=vp then
  141.                                 a="("..a..")"
  142.                         end
  143.  
  144.                         if bp<=vp then
  145.                                 b="("..b..")"
  146.                         end
  147.  
  148.                         strStack:push(a..v..b)
  149.                         typeStack:push(v)
  150.                 else
  151.                         strStack:push(v)
  152.                         typeStack:push("n")
  153.                 end
  154.         end
  155.         print(strStack:bottom())
  156. end
  157.  
  158.  
  159. -- get table copy(contains its methods)
  160. function array.copy(this)
  161.         local copy={}
  162.         for index,value in pairs(this) do
  163.                 copy[index]=value
  164.         end
  165.         return copy
  166. end
  167.  
  168. -- the main method to calculate 24 points
  169. function array.calc(this)
  170.         local order=1
  171.         for i=1,4 do
  172.                 for j=i,4 do
  173.                         for k=j,4 do
  174.                                 local toeval=this:copy()
  175.                                 table.insert(toeval,optable[i])
  176.                                 table.insert(toeval,optable[j])
  177.                                 table.insert(toeval,optable[k])
  178.                                 table.sort(toeval)                       
  179.                                 while true do
  180.                                         if tostring(toeval:eval())=="24" then
  181.                                                 io.write(order,": ")
  182.                                                 toeval:show_expr()
  183.                                                 order=order+1
  184.                                         end
  185.  
  186.                                         if not toeval:next_permutation() then
  187.                                                 break
  188.                                         end
  189.                                 end
  190.                         end
  191.                 end
  192.         end
  193. end
  194.  
  195. for i=1,4 do
  196.         if not arg[i] then
  197.                 io.stderr:write("Please input at least four arguments!\n")
  198.                 return 1
  199.         end
  200.  
  201.         local num=tonumber(arg[i])
  202.         if not num then
  203.                 io.stderr:write("Arguments must be numbers\n")
  204.                 return 2
  205.         end
  206.        
  207.         if num<1 or num > 13 then
  208.                 io.stderr:write("Number must in [1,13]\n")
  209.                 return 3
  210.         end
  211.  
  212.         if math.floor(num)~=num then
  213.                 io.stderr:write("Number must be an integer!\n")
  214.                 return 4
  215.         end
  216.  
  217.         table.insert(array,arg[i])
  218. end
  219.  
  220. array:calc()
  1.  
  2.  

     

Algorithm Comments(0) 2008年6月28日 14:13

更新的防止游戏修改的算法

  上次我把程序发给了另外一个高中同学,他似乎并没有花多少时间就找出来了。但是看他给我的描述,似乎找到不是真正的存放关键数据的内存。还是给廖传政同学修改的差不多,由于要通过画矩形来显示结果,所以必定要提供一个变量来表示真实的数据。如果放在一个函数的局部变量里,由于局部变量是存放在栈中的,只要调用堆栈相同就可以找到。所以放在堆空间更好,但是我却忽略了一点。如下:

  1. void *p=malloc(100);
  2. free(p);
  3. p=malloc(100)

  这个式子中,p在free前后的值很多情况下是相同的,这样,即使在堆空间,也可能完全相同。为了防止这种情况,我又使用了随机得到一个堆内存的方法,为了防止栈空间也相同,我同样使用了随机层次的递归,这样一来,我觉得应该会提高破解的难度。更新的代码如下:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <time.h>
  5.  
  6. #include <SDL/SDL.h>
  7.  
  8. SDL_Surface *screen = NULL;
  9. int blood;
  10.  
  11. int *encode;
  12. int *decode;
  13. SDL_Rect *rects;
  14.  
  15. int index_of(int array[],size_t size,int value){
  16.     int index;
  17.     for(index=0;index<size;index++){
  18.         if(array[index]==value)
  19.             return index;
  20.     }
  21.     return -1;
  22. }
  23.  
  24.  
  25. void begin_game(){
  26.     srand(time(NULL));
  27.     encode=(int *)malloc(1024*sizeof(int));
  28.     decode=(int *)malloc(1024*sizeof(int));
  29.     rects=(SDL_Rect *)malloc(1024*sizeof(SDL_Rect));
  30.    
  31.     int i,r;
  32.     for(i=0;i<101;i++){
  33.         r=rand()%101;
  34.         if(index_of(encode,i,r)==-1)
  35.             encode[i]=r;
  36.         else{
  37.             i--;
  38.             continue;
  39.         }           
  40.     }
  41.    
  42.     for(i=0;i<101;i++){
  43.         decode[i]=index_of(encode,101,i);
  44.     }   
  45.    
  46.     blood=encode[100];
  47. }
  48.  
  49. void end_game(){
  50.     free(encode);
  51.     free(decode);
  52.     free(rects);
  53. }
  54.  
  55. void draw (int layer){
  56.     if(layer>0)
  57.         return draw(--layer);
  58.    
  59.     Uint32 color;   
  60.     color=SDL_MapRGB(screen->format,0,0,0);
  61.     SDL_FillRect (screen, NULL, color);   
  62.     color = SDL_MapRGB (screen->format, 0xFF, 0, 0);
  63.    
  64.     SDL_Rect *rect=&rects[rand()%1024];
  65.     rect->w = decode[blood]*5;
  66.     rect->h = 20;
  67.     rect->x = 100;
  68.     rect->y = 100;
  69.     SDL_FillRect (screen, rect, color);
  70.  
  71.     SDL_Flip (screen);
  72.     SDL_Delay (10);
  73. }
  74.  
  75.  
  76.  
  77.  
  78. int main (int argc, char *argv[]){
  79.     char *msg;
  80.     int done;
  81.    
  82.     SDL_Init (SDL_INIT_VIDEO);
  83.     atexit (SDL_Quit);
  84.  
  85.     screen = SDL_SetVideoMode (640, 480, 16, SDL_SWSURFACE | SDL_DOUBLEBUF);
  86.     SDL_WM_SetCaption ("SDL MultiMedia Application", NULL);
  87.     begin_game();
  88.    
  89.  
  90.     done = 0;
  91.     SDL_Event event;
  92.    
  93.     while (!done){
  94.         SDL_WaitEvent (&event);
  95.         switch (event.type){
  96.             case SDL_KEYDOWN:{
  97.                 switch(event.key.keysym.sym){
  98.                     case SDLK_UP:blood = decode[blood]+10>100 ? blood : encode[decode[blood]+10];break;
  99.                     case SDLK_DOWN:blood = decode[blood]-10<0 ? blood : encode[decode[blood]-10];break;
  100.                 }
  101.             }
  102.                 break;
  103.             case SDL_QUIT:
  104.                 done = 1;
  105.                 break;
  106.             default:
  107.                 break;
  108.         }
  109.         draw (rand()%10);
  110.     }
  111.    
  112.     end_game();
  113.  
  114.     return 0;
  115. }
  116.  

Algorithm Comments(0) 2008年4月25日 11:32

防止游戏修改的一个小算法

  前几天我看到廖传政又在修改游戏,于是就给他较劲,故意编个游戏来让他修改不了。据他说,假如要让血槽不动,一般是找到内存变化的地方,然后锁定就是了。从这一点出发,我想到如果表示内存变化的那个地方完全没的规律,那么就比较难以查找了。
  我的想法是,真实的数据不能直接放在内存中,而是代之以一个神秘的数值,这个神秘的数值也完全零乱,也就是说血条上升或下降时,这个值会变化,但不一定也是上升或是下降,也是就他们之间不存在简单的线性关系,更进一步说,如果把这个神秘数与真实数的对应关系是一个函数的话,那这个函数是非线性的,其实光线性还不够,而且要是找不出单调的区间。但是它们之间又要可逆,就是要找出一个可逆的非单调函数
  我的基本思想是,假如要加密的是0到100之间的整数,那么就先生成把0到100随机存放到一个encode数组,作为加密的数组,例如一个真实数56,它所对应的加密数就是encode[56],然后产生一个解密数组,分别存放0到100在encode数组中的索引,也就是for any i in 0..100,encode[decode[i]]=i。如果把encode看成是一个函数,那它的逆函数就是decode。
  真正在内存里保存的数都是那个“神秘数”,而真实数都得通过decode数组才能读出,通过encode来写,由于加密和解密的部分都只是几次寻址操作,所以效率并不低,而且我们只对关键的数值进行加密。其它不管。
  这个算法我给他做了以后,他算是半个成功了,因为找到了那个显示血槽对象的相关内存,但是真正存放数据的地方却没有找到。本来游戏是用Irrlicht写的3D,很占内存和CPU,下面是我用SDL改写的,完全只保留了最基本的演示:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <time.h>
  5.  
  6. #include <SDL/SDL.h>
  7.  
  8. SDL_Surface *screen = NULL;
  9. int blood;
  10.  
  11. int *encode;
  12. int *decode;
  13.  
  14. int index_of(int array[],size_t size,int value){
  15.     int index;
  16.     for(index=0;index<size;index++){
  17.         if(array[index]==value)
  18.             return index;
  19.     }
  20.     return -1;
  21. }
  22.  
  23.  
  24. void begin_game(){
  25.     srand(time(NULL));
  26.     encode=(int *)malloc(1024*sizeof(int));
  27.     decode=(int *)malloc(1024*sizeof(int));
  28.    
  29.     int i,r;
  30.     for(i=0;i<101;i++){
  31.         r=rand()%101;
  32.         if(index_of(encode,i,r)==-1)
  33.             encode[i]=r;
  34.         else{
  35.             i--;
  36.             continue;
  37.         }           
  38.     }
  39.    
  40.     for(i=0;i<101;i++){
  41.         decode[i]=index_of(encode,101,i);
  42.     }   
  43.    
  44.     blood=encode[100];
  45. }
  46.  
  47. void end_game(){
  48.     free(encode);
  49.     free(decode);
  50. }
  51.  
  52. void draw (){
  53.    
  54.     Uint32 color;   
  55.     color=SDL_MapRGB(screen->format,0,0,0);
  56.     SDL_FillRect (screen, NULL, color);   
  57.     color = SDL_MapRGB (screen->format, 0xFF, 0, 0);
  58.    
  59.     SDL_Rect *rect=(SDL_Rect *)malloc(sizeof(SDL_Rect));
  60.     rect->w = decode[blood]*5;
  61.     rect->h = 20;
  62.     rect->x = 100;
  63.     rect->y = 100;
  64.     SDL_FillRect (screen, rect, color);
  65.     free(rect);
  66.  
  67.     SDL_Flip (screen);
  68.     SDL_Delay (10);
  69. }
  70.  
  71.  
  72.  
  73.  
  74. int main (int argc, char *argv[]){
  75.     char *msg;
  76.     int done;
  77.    
  78.     SDL_Init (SDL_INIT_VIDEO);
  79.     atexit (SDL_Quit);
  80.  
  81.     screen = SDL_SetVideoMode (640, 480, 16, SDL_SWSURFACE | SDL_DOUBLEBUF);
  82.     SDL_WM_SetCaption ("SDL MultiMedia Application", NULL);
  83.     begin_game();
  84.    
  85.  
  86.     done = 0;
  87.     SDL_Event event;
  88.    
  89.     while (!done){
  90.         SDL_WaitEvent (&event);
  91.         switch (event.type){
  92.             case SDL_KEYDOWN:{
  93.                 switch(event.key.keysym.sym){
  94.                     case SDLK_UP:blood = decode[blood]+10>100 ? blood : encode[decode[blood]+10];break;
  95.                     case SDLK_DOWN:blood = decode[blood]-10<0 ? blood : encode[decode[blood]-10];break;
  96.                 }
  97.             }
  98.                 break;
  99.             case SDL_QUIT:
  100.                 done = 1;
  101.                 break;
  102.             default:
  103.                 break;
  104.         }
  105.         draw ();
  106.     }
  107.    
  108.     end_game();
  109.  
  110.     return 0;
  111. }
  112.  

Algorithm Comments(1) 2008年4月19日 17:39