在MATLAB中找到矩阵的最大公约数

时间:2015-09-20 20:56:01

标签: matlab matrix vectorization

我正在寻找一种方法来将某些矩阵元素除以其最小公约数。

例如,我有载体

[0,0,0; 2,4,2;-2,0,8]

我可以告诉最低公约数是2,所以除法后的矩阵将是

[0,0,0;1,2,1;-1,0,4]

可以计算这个的内置方法是什么?

提前致谢

P.S。我个人不喜欢使用循环进行此计算,似乎内置计算可以执行矩阵元素划分。

4 个答案:

答案 0 :(得分:7)

既然你不喜欢循环,那么递归函数呢?

iif = @(varargin) varargin{2 * find([varargin{1:2:end}], 1, 'first')}();
gcdrec=@(v,gcdr) iif(length(v)==1,v, ...
                     v(1)==1,1, ...
                     length(v)==2,@()gcd(v(1),v(2)), ...
                     true,@()gcdr([gcd(v(1),v(2)),v(3:end)],gcdr));
mygcd=@(v)gcdrec(v(:)',gcdrec);

A=[0,0,0; 2,4,2;-2,0,8];
divisor=mygcd(A);
A=A/divisor;

第一个函数iif将定义内联条件结构。这允许定义递归函数gcdrec,以找到数组的最大公约数。这个iif的工作方式如下:它测试第一个参数是否为true,如果是,则返回第二个参数。否则它会测试第三个参数,如果那个 true,那么它将返回第四个,依此类推。您需要使用@()保护递归函数以及有时出现在其中的其他数量,否则您可能会收到错误。

使用iif递归函数gcdrec的工作原理如下:

  • 如果输入向量是标量,则返回它
  • 否则,如果向量的第一个分量为1,则无法恢复,因此返回1(允许快速返回大型矩阵)
  • 如果输入向量的长度为2,则通过gcd
  • 返回最大公约数
  • 否则它会使用缩短的向量调用自身,其中前两个元素被其最大公约数替换。

为方便起见,函数mygcd只是一个前端。

应该非常快,我想只有递归深度可能是一个非常大问题的问题。我使用A=randi(100,N,N)-50N=100N=1000N=5000以及tic / {{进行了快速计时检查以与@Adriaan的循环版本进行比较1}}。

  1. toc
    • 循环0.008秒
    • 递归0.002秒
  2. N=100
    • 循环0.46秒
    • 递归0.04秒
  3. N=1000
    • 循环11.8秒
    • 递归0.6秒
  4. 更新:有趣的是,我没有绊倒递归限制(默认为500)的唯一原因是我的数据没有公约除数。设置随机矩阵并将其加倍将导致达到N=5000的递归限制。因此对于大型矩阵来说,这不会起作用。再说一次,对于小矩阵,@ Adriaan的解决方案完全没问题。

    我还尝试在每个递归步骤中将其重写为输入向量的一半:这确实解决了递归限制问题,但它非常慢(N=100为2秒,261 N=100)的秒数。在某处可能存在中间地带,矩阵大小很大(ish),运行时间并不差,但我还没有找到它。

答案 1 :(得分:5)

 A = [0,0,0; 2,4,2;-2,0,8];
 B = 1;
 kk = max(abs(A(:))); % start at the end
 while B~=0 && kk>=0
     tmp = mod(A,kk);
     B = sum(tmp(:));
     kk = kk - 1;
 end
 kk = kk+1;

这可能不是最快的方式,但它现在可以做到。我在这里做的是初始化一些计数器B,以便在获取mod后存储矩阵中所有元素的总和。 kk只是一个贯穿整数的计数器。 mod(A,kk)计算A中每个元素的除法后的模数。因此,如果所有元素都可以被2整除,则每个元素将返回0。 sum(tmp(:))然后从模数矩阵中生成一列,将其求和以获得一些数字。当且仅当该数字为0时,存在公约数,因此A中的所有元素都可以被kk完全整除。一旦发生这种情况,你的循环就会停止,你的公约数就是kk中的数字。由于kk每次计数减少,实际上一个值太低,因此添加一个。

注意:我刚刚编辑了循环以向后运行 ,因为您正在寻找最好的CD,而不是最小的CD。如果您有[4,8;16,8]这样的矩阵,它会停在2,而不是4。为此道歉,现在这种方法有效,尽管这里的其他解决方案都要快得多。

最后,划分矩阵可以这样做:

divided_matrix = A/kk;

答案 2 :(得分:4)

同意,我也不喜欢这个循环!让我们杀了他们 -

unqA = unique(abs(A(A~=0))).';             %//'
col_extent = [2:max(unqA)]';               %//'
B = repmat(col_extent,1,numel(unqA));
B(bsxfun(@gt,col_extent,unqA)) = 0;
divisor = find(all(bsxfun(@times,bsxfun(@rem,unqA,B)==0,B),2),1,'first');
if isempty(divisor)
    out = A;
else
    out = A/divisor;
end

示例运行

案例#1:

A =
     0     0     0
     2     4     2
    -2     0     8
divisor =
     2
out =
     0     0     0
     1     2     1
    -1     0     4

案例#2:

A =
     0     3     0
     5     7     6
    -5     0    21
divisor =
     1
out =
     0     3     0
     5     7     6
    -5     0    21

答案 3 :(得分:3)

这是另一种方法。让A成为您的输入数组。

  1. 获取非零值A并获取其绝对值。调用生成的向量B
  2. 测试从1max(B)的每个号码,看看它是否划分B的所有条目(即,如果除法的其余部分为零)。
  3. 取这个号码最大的。
  4. 代码:

    A = [0,0,0; 2,4,2;-2,0,8];                  %// data
    B = nonzeros(abs(A));                       %// step 1
    t = all(bsxfun(@mod, B, 1:max(B))==0, 1);   %// step 2
    result = find(t, 1, 'last');                %// step 3