在MATLAB中求矩阵的最大公约数

在MATLAB中求矩阵的最大公约数,matlab,matrix,vectorization,Matlab,Matrix,Vectorization,我在寻找一种方法,用最小公约数来划分某个矩阵元素 例如,我有向量 [0,0,0; 2,4,2;-2,0,8] 我知道最小公约数是2,所以除法后的矩阵是 [0,0,0;1,2,1;-1,0,4] 什么是可以计算这个的内置方法 提前谢谢 p、 我个人不喜欢在这个计算中使用循环,似乎有一个内置的计算可以执行矩阵元素划分 A = [0,0,0; 2,4,2;-2,0,8]; B = 1; kk = max(abs(A(:))); % start at the end while B~=0 &

我在寻找一种方法,用最小公约数来划分某个矩阵元素

例如,我有向量

[0,0,0; 2,4,2;-2,0,8]
我知道最小公约数是2,所以除法后的矩阵是

[0,0,0;1,2,1;-1,0,4]
什么是可以计算这个的内置方法

提前谢谢

p、 我个人不喜欢在这个计算中使用循环,似乎有一个内置的计算可以执行矩阵元素划分

 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
只是一个通过整数运行的计数器。为
A
中的每个元素计算除法后的模数。因此,如果所有元素都可以被2整除,那么每个元素都将返回0
sum(tmp(:)
然后从模矩阵中生成一列,求和得到一些数字。当且仅当该数字为0时,存在公约数,因此
a
中的所有元素都可被
kk
整除。一旦发生这种情况,循环就会停止,公约数就是
kk
中的数字。由于
kk
每次计数都会减少,因此实际上是一个太低的值,因此增加了一个

注意:我刚刚编辑了循环以向后运行,因为您正在查找最大的cd,而不是最小的cd。如果你有一个像
[4,8;16,8]
这样的矩阵,它会停在
2
,而不是
4
。对此表示歉意,虽然这里的其他两种解决方案都要快得多,但现在可以这样做了

最后,可以按如下方式划分矩阵:

divided_matrix = A/kk;

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

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
,以查找数组的最大公约数。其工作原理如下:它测试第一个参数是否为
true
,如果是,则返回第二个参数。否则它将测试第三个参数,如果这是
true
,则返回第四个参数,依此类推。您需要保护递归函数,有时还需要使用
@()
保护其中出现的其他数量,否则可能会出现错误

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

divided_matrix = A/kk;
  • 如果输入向量是标量,则返回它
  • 否则,如果向量的第一个分量为1,则没有机会恢复,因此它返回1(允许快速返回大型矩阵)
  • 否则,如果输入向量的长度为2,它将通过
    gcd
  • 否则它用一个缩短的向量来调用自己,其中前两个元素用它们的最大公约数替换
功能
mygcd
只是为了方便起见的前端

应该很快,我想对于非常大的问题,只有递归深度可能是个问题。我用
a=randi(100,N,N)-50
,用
N=100
N=1000
N=5000
tic
/
toc
对@adrian的循环版本进行了快速计时检查

  • N=100
    • 循环0.008秒
    • 递归0.002秒
  • N=1000
    • 循环0.46秒
    • 递归0.04秒
  • N=5000
    • 循环11.8秒
    • 递归0.6秒
  • 更新:有趣的是,我没有突破递归限制(默认为500)的唯一原因是我的数据没有公约数。设置一个随机矩阵并将其加倍将导致达到
    N=100
    的递归极限。所以对于大型矩阵,这是行不通的。再说一次,对于小矩阵,@Adrian的解是完美的


    我还尝试在每个递归步骤中将其重写为输入向量的一半:这确实解决了递归限制问题,但速度非常慢(对于
    N=100
    ,2秒;对于
    N=1000
    ,261秒)。可能有一个中间地带,矩阵大小很大(ish),运行时也没那么糟糕,但我还没有找到它。

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

    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:

    案例2:


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

  • 获取
    A
    的非零值并获取其绝对值。调用结果向量
    B
  • 测试从
    1
    max(B)
    的每个数字,并查看它是否将
    B
    的所有条目进行除法(即,除法的剩余部分是否为零)
  • 以这类人数最多的为例
  • 代码:


    你只需要除法的代码,而不是找到最小公约数,对吗?@Max说实话,找到最小公约数也会很好,你可能想改变你的问题标题,因为这个问题的答案是
    A/A
    。我支持@JJMDriessen的建议,因为如果你开始学习任何matlab,关于A/A的部分都是微不足道的。但是“向量的gcd”部分更有趣,答案都集中在这一点上。@JJM Driessen,Andras Deak是的,这个新标题看起来好多了,谢谢,我的列表中的元素0有问题,我不能使用“min”或“rdivide”,因为它有时会给我Nan的值。但这应该行,谢谢你。但是要注意计算需求,这就是为什么我还添加了
    maxIter
    catch。对于具有较大最小公约数的大型矩阵,这可能会变慢。我认为可以安全地假设
    maxIter=max
    
    A =
         0     3     0
         5     7     6
        -5     0    21
    divisor =
         1
    out =
         0     3     0
         5     7     6
        -5     0    21
    
    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