我正在寻找一种方法来将某些矩阵元素除以其最小公约数。
例如,我有载体[0,0,0; 2,4,2;-2,0,8]
我可以告诉最低公约数是2,所以除法后的矩阵将是
[0,0,0;1,2,1;-1,0,4]
可以计算这个的内置方法是什么?
提前致谢
P.S。我个人不喜欢使用循环进行此计算,似乎内置计算可以执行矩阵元素划分。
答案 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
的工作原理如下:
gcd
为方便起见,函数mygcd
只是一个前端。
应该非常快,我想只有递归深度可能是一个非常大问题的问题。我使用A=randi(100,N,N)-50
,N=100
,N=1000
和N=5000
以及tic
/ {{进行了快速计时检查以与@Adriaan的循环版本进行比较1}}。
toc
:
N=100
:
N=1000
:
更新:有趣的是,我没有绊倒递归限制(默认为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
成为您的输入数组。
A
并获取其绝对值。调用生成的向量B
。1
到max(B)
的每个号码,看看它是否划分B
的所有条目(即,如果除法的其余部分为零)。代码:
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