首页  编辑  

一个关于矩阵的算法

Tags: /超级猛料/Alogrith.算法和数据结构/数值运算/   Date Created:

m*n矩阵的每个分量只填"0"或"1"---- 0-1矩阵

求解所有包含i个"1"的这样的0-1矩阵[max(m,n)-1< i < mn+1 的整数]

要求所求得的矩阵不能出现某行或某列出现全"0"现象

500分,求解!

注:我使用"回溯"算法实现,对于5*5,计算的时间就让我等的不耐烦了!

:lccc 时间:00-11-2 2:16:37 ID:383253  

假设矩阵为m行n列:

A11 , A12 , ..., A1n

...................

Am1 , Am2 , ..., Amn

设 Ci表示第i行之和(1<=i<=m), Di表示第i列之和(1<=i<=n),

  Sx表示所有Ci之积,Sy表示所有Di之积。

我想,对于任何一个给出的矩阵,计算Ci、Di、Sx、Sy是

很容易、快速的,如果Sx*Sy=0排除即可。(Sx<>0,排除有

一行全为0的情况,Sy<>0排除有一列全为0的情况。)

供君参考,若可以,希望能告诉我你用它的作用

请大家注意:

 求出所有的矩阵是关键,至于是不是要求不存在全"0"行或列并不是主要的.

正如cheka所说的,有点想八后问题,所以最先我就是用了"回溯"算法,可惜对于

5*5的矩阵,就有点受不了了(其实,需要运算的还有一层:对于给定整数N, (m,n)

是满足m+n=N(m>0,n>0)的一组分解,对于每种分解,都要求出所有可能的0-1矩阵.)

回头我把握的弱智算法贴上来,请各位帮忙想想能不能有更好的解法.我的Pascal

解法在N=10时,PIII667+128MB上的机器,就求了10多分钟,要是N=100,岂不是要100年?

我的弱智算法,请大家帮忙看看,由没有问题(Delphi5环境下,Filename=tp.dpr)

Program tp;

{$APPTYPE CONSOLE}

uses SysUtils;

const

 MAXN = 10;

type

 TMAX = 1..MAXN;

 TMAX2= 1..(MAXN div 2)*(MAXN div 2);

 TAset= array[1..MAXN] of shortint;

 PSet = ^TSet;

 TSet = Record

          row,col:TAset;

          next:PSet;

        End;

var

 i,j,m,n,max: TMAX;

 A: array[1..MAXN,1..MAXN] of shortint;

 num: longint;

 PHead: PSet;

///////////////////////////////////////////////

procedure printA(m,n:TMAX; e:TMAX2);

var

 i,j:TMAX;

begin

 for i:=1 to m do

 begin

   for j:=1 to n do

     write(A[i,j]);

   writeln;

 end;

 inc(num);

 writeln(m,'*',n,' array ',e,' edges. (',num,')');

 writeln;

end;

///////////////////////////////////////////////

function IsSafe(x,y:TMAX; ce,e:TMAX2):Boolean;

var

 i,j:TMAX;

 s:shortint;

begin

 IsSafe := TRUE;

 A[x,y] := 1;

 if (m-x)*n+(n-y)ox then oy:=1;

   for y:=oy to n do

     if IsSafe(x,y,ce,e) then

     begin

       A[x,y]:=1;

       if ceox then oy:=1;

   for y:=oy to n do

     if IsSafe(x,y,ce,e) then

     begin

       A[x,y]:=1;

       if ce

--------------------------------------------------------------------------------

来自:愚夫 时间:00-11-3 12:30:07 ID:384870  

为什么剩下的一段程序粘不上去?

--------------------------------------------------------------------------------

来自:wjiachun 时间:00-11-3 12:33:20 ID:384876  

我来帮你贴上去!

function IsSafe(x,y:TMAX; ce,e:TMAX2):Boolean;

var

 i,j:TMAX;

 s:shortint;

begin

 IsSafe := TRUE;

 A[x,y] := 1;

 if (m-x)*n+(n-y)ox then oy:=1;

   for y:=oy to n do

     if IsSafe(x,y,ce,e) then

     begin

       A[x,y]:=1;

       if ce<e then

         search(x,y+1,ce+1,e,m,n)

       else

         printA(m,n,e);

       A[x,y]:=0;

     end;

 end;

end;

///////////////////////////////////////////////

Begin

 num:=0;

 PHead:=nil;

 for i:=1 to MAXN do

   for j:=1 to MAXN do

     A[i,j]:=0;

 for i:=1 to (MAXN div 2) do

 begin

   m:=i; max:=m;

   n:=MAXN - m;

   if max<n then max:=n;

   for j:=max to m*n do     // max(m,n)

   begin

     search(1,1,1,j,m,n);   // search(ox,oy,ce,e,m,n)

   end;

 end;

 writeln('you get ',num:10,' array(s)');

 readln;

End.

:KentKing 时间:00-11-4 4:56:44 ID:385548  

愚夫兄,当你求得i(在你程序中是j)=Max(m,n)时的一个解时,除了这i个填"1"的位置外

其它位置随便填"0"、填"1"不都是你的解吗?

犯得着加一层循环>> for j:=max to m*n do 去逐个搜索吗?:)

另外如果你还想提高一下求解速度的话,建议你直接把m*n的0-1矩阵看成是m*n位的二进制

数。这样问题就转化为求解0..2^(m*n)-1整数区间内特定Max(m,n)位为1的数。

Step1. 用回溯法解得i=Max(m,n)时的k个解, 暂存为 R[1..k];

Step2. i := 2^(m*n)-1;

      while i > 0 do

      begin

        for j := 1 to k do

        begin

          if (i and R[j]) = R[j] then

          begin  // i 为 解

            将i转化为m*n矩阵输出;

            break;

          end;

        end;

        i := i + 1;

      end;

当然,如果m*n太大(超过32),你不得不用浮点数表示i和R[j]时。就必须再写个函数实现将

浮点数转换为 ((m*n+7) div 8) 个字节存贮的整数。比较 (i and R[j])=R[j] 时也得逐字

节运算了。不过我想应该还是比用二维数组快多了吧。

以上意见, 仅供参考。希望各位高手多多指正。

--------------------------------------------------------------------------------

来自:KentKing 时间:00-11-4 5:04:56 ID:385549  

哈哈, 笔误笔误

Step2 中应为 i := i - 1

--------------------------------------------------------------------------------

来自:愚夫 时间:00-11-6 15:28:17 ID:387852  

TO KentKing:

 解释一下,主程序中的for j:=max to m*n 是为了求解组合(m,n)时,

分别放入max个"1"、max+1个"1"、max+2个"1"... m*n个"1"的

所有0-1矩阵的种类。实际上,整个问题是要求解对于给定整数MAXN,

在所有满足m+n=MAXN的组合(m,n)下,有j个"1"(j取max(m,n)到m*n)

的所有0-1矩阵,我不知道我的程序是否有问题,请您务必指教。谢谢!

--------------------------------------------------------------------------------

来自:KentKing 时间:00-11-7 12:37:48 ID:388618  

Sorry, 我想我没说清楚我的意思。

>> 当你求得i(在你程序中是j)=Max(m,n)时的一个解时,除了这i个填"1"的位置外

>> 其它位置随便填"0"、填"1"不都是你的解吗?

>> 犯得着加一层循环>> for j:=max to m*n do 去逐个搜索吗?:)

我的意思是:求解j=(max+1..m*n)这个区间内的解时可以在j=max的解的基础上用更简单

的方法(参见我上个帖子的Step2)求得。而不必对每一个j(max> for i:=1 to (MAXN div 2) do 这个外层循环应该是没什

么问题的。另外由于你的程序好象是没有贴完(我没有找到 search), 我也就没有仔细读

你代码的其它部分了。

--------------------------------------------------------------------------------

来自:KentKing 时间:00-11-7 12:43:31 ID:388620  

Sorry, 我想我没说清楚我的意思。

>> 当你求得i(在你程序中是j)=Max(m,n)时的一个解时,除了这i个填"1"的位置外

>> 其它位置随便填"0"、填"1"不都是你的解吗?

>> 犯得着加一层循环>> for j:=max to m*n do 去逐个搜索吗?:)

我的意思是:求解j=(max+1..m*n)这个区间内的解时可以在j=max的解的基础上用更简单

的方法(参见我上个帖子的Step2)求得。而不必对每一个j(max> for i:=1 to (MAXN div 2) do 这个外层循环应该是没什

么问题的。另外由于你的程序好象是没有贴完(我没有找到 search), 我也就没有仔细读

你代码的其它部分了。

来自:KentKing 时间:00-11-7 12:48:54 ID:388628  

(kao!!为什么老出错,再贴一次 )

****************************************************************

Sorry, 我想我没说清楚我的意思。

>> 当你求得i(在你程序中是j)=Max(m,n)时的一个解时,除了这i个填"1"的位置外

>> 其它位置随便填"0"、填"1"不都是你的解吗?

>> 犯得着加一层循环>> for j:=max to m*n do 去逐个搜索吗?:)

我的意思是:求解j=(max+1..m*n)这个区间内的解时可以在j=max的解的基础上用更简单

的方法(参见我上个帖子的Step2)求得。而不必对每一个j( max< j <=m*n )都采用j=max

时的方法来求解。(Understand? 我觉得这几句话读起来是有些拗口 :)

至于求解整个问题,我想 >> for i:=1 to (MAXN div 2) do 这个外层循环应该是没什

么问题的。另外由于你的程序好象是没有贴完(我没有找到 search), 我也就没有仔细读

你代码的其它部分了。

来自:KentKing 时间:00-11-7 13:53:13 ID:388684  

槽糕,槽糕,我的方法是错的。无法求得j=(max+1..m*n)时的所有解。

不过我有了个新的方法。实现后,晚上再贴上来吧。

白天访问大富翁实在是太慢太慢了...!!!

我真命苦!我真命苦!我真命苦!我真命苦!我真命苦!我真命苦!我真命苦!我真命苦!

来自:KentKing 时间:00-11-7 21:30:19 ID:388961  

程序如下:

program KK;

uses

 SysUtils;

const

 MAXN = 10;

 ITEMCOUNT = (MAXN div 2)*(MAXN-(MAXN div 2));

var

 Num,Num0, v: Longword;

 m, n: Integer;

 s0,s: String;

///////////////////////////////////////////////

// 转化v为二进制字串s, 返回值为s中'1'的个数

function ToBinaryStr(v: Longword): Integer;

var

 v0: Longword;

 p: Integer;

begin

 s := s0;

 p := 0;

 Result := 0;

 while (v > 0) do

 begin

   Inc(p);

   v0 := v shr 1;

   if (v0 shl 1) <> v then

   begin

     s[p] := '1';

     Result := Result + 1;

   end;

   v := v0;

 end;

end;

// 检查是否为解

function IsValid: Boolean;

var

 i, j: Integer;

begin

 Result := False;

 // 此处按原题意应是比较 ToBinaryStr(v) < Max(m, n)

 // 考虑到m取值区间为1..(MAXN div 2),所以总有n=Max(m, n)成立

 if ToBinaryStr(v) < n then Exit;

 // 检查行

 for i := 0 to m-1 do

 begin

   for j := 1 to n do

   begin

     if s[i*n + j] = '1' then break;

   end;

   if j > n then Exit;

 end;

 // 检查列

 for j := 1 to n do

 begin

   for i := 0 to m-1 do

   begin

     if s[i*n + j] = '1' then break;

   end;

   if i = m then Exit;

 end;

 Result := True;

end;

// 按m*n(m行n列)的矩阵输出

procedure OutputMatrix;

var

 i: Integer;

begin

 for i := 0 to m-1 do

 begin

   writeln(Copy(s, i*n + 1, n));

 end;

 writeln;

end;

///////////////////////////////////////////////////

begin

 writeln('Start Time: ', TimeToStr(Now));

 writeln;

 Num  := 0;

 s0 := '';

 for m := 1 to ITEMCOUNT do s0 := s0 + '0';

 for m := 1 to (MAXN div 2) do

 begin

   n := MAXN - m;

   Num0 := Num;

   for v := 1 to (1 shl (m*n))-1 do

   begin

     if IsValid then

     begin

       OutputMatrix;

       Inc(Num);

     end;

   end;

   writeln(m, '*', n, ': ', Num - Num0,' Solution(s)');

   writeln;

 end;

 writeln('Total: ', Num,' Solution(s)');

 writeln;

 writeln('End Time: ', TimeToStr(Now));

end.

测试结果:

 D4下编译,PII266, 96M内存,Win98

 

 MAXN=9时:耗时17秒,共811252组解,重定向生成的输出文件22.7MB

          为排除I/O耗时的影响,注释掉 OutputMatrix 后运行,耗时5秒

 

 MAXN=10时:由于我的硬盘剩余空间只有600多MB,第一次运行中途因硬盘空间耗尽而退出

            注释掉 OutputMatix 后运行,耗时3分13秒(193秒),共36745865组解

其它一些与测试有关的数字:

   符号说明:N#(MAXN=#时解的数目)

             T#(MAXN=#时算法耗时)

             V#(MAXN=#时v的最大取值)  

   N10 / N9 = 36745865 / 811252 = 45.295

   T10 / T9 = 193 / 5 = 38.6

   V10 / V9 = (2^(5*5)-1) / (2^(4*5)-1) 约等于 2^5 = 32

   

按T9*(N10/N9)来粗略估计,MAXN=10时生成输出文件大概需耗时770秒(12分50秒)

单独考虑m=5,n=5时,5*5的0-1矩阵总的取值区间大小为2^(5*5) = 33554432

而此时的解为24997921个,占总取值区间的74.5%。这个数字能不能给我们一些

反向求解此问题的启发呢?

以上程序及结果,仅供参考, 欢迎探讨 :)