简体   繁体   English

如何在GNU Octave / Matlab中计算向量的GCD

[英]How to compute the GCD of a vector in GNU Octave / Matlab

gcd (A1, A2, ...) computes the GCD of elements A1(1), A2(1), ... . gcd (A1, A2, ...)计算元素A1(1), A2(1), ...的GCD。 Being the elements stored in a vector A , how to compute gcd (A) ? 作为存储在向量A的元素,如何计算gcd (A)
(I mean, gcd (4, 2, 8) = 2 , gcd ([4, 2, 8] will raise an error in GNU Octave 4.0.0). (我的意思是, gcd (4, 2, 8) = 2gcd ([4, 2, 8]会在GNU Octave 4.0.0中引发错误)。

The following is crude, but seems to work on simple examples 以下是粗略的,但似乎适用于简单的例子

function g = gcd_array(vals)
if length(vals) == 1
    g = vals;
else
    g = gcd(vals(1), gcd_array(vals(2:end)));
endif

With cell array expansion 随着单元阵列的扩展

Here is a one-liner, valid only in octave (thanks to nirvana-msu for pointing out matlab's limitation): 这是一个单行,仅在八度音程中有效(感谢nirvana-msu指出matlab的限制):

A = [10 25 15];
gcd(num2cell(A){:})
# ans =  5

This use cell array expansion, which is a bit hidden there : 这使用单元阵列扩展,有点隐藏在那里

Accessing multiple elements of a cell array with the '{' and '}' operators will result in a comma separated list of all the requested elements 使用'{'和'}'运算符访问单元数组的多个元素将导致所有请求元素的逗号分隔列表

so here A{:} is interpreted as A(1), A(2), A(3) , and thus gcd(A{:}) as gcd(A(1), A(2), A(3)) 所以这里A{:}被解释为A(1), A(2), A(3) ,因而gcd(A{:})gcd(A(1), A(2), A(3))


Performance 性能

Still under octave 仍在八度音阶

A = 3:259;
tic; gcd(num2cell(A){:}); toc

Elapsed time is 0.000228882 seconds.

while with the gcd_vect in @nirvana_msu answer, gcd_vect中的gcd_vect回答,

tic; gcd_vect(A); toc

Elapsed time is 0.0184669 seconds.

This is because using recursion implies a high performance penalty (at least under octave). 这是因为使用递归意味着高性能损失(至少在八度音阶下)。 And actually for more than 256 elements in A, recursion limit is exhausted. 实际上,对于A中超过256个元素,递归限制已用尽。

tic; gcd_vect(1:257); toc

<... snipped bunch of errors as ...>
error: evaluating argument list element number 2
error: called from
gcd_vect at line 8 column 13

This could be improved a lot by using a Divide and conquer algorithm 使用Divide and conquer算法可以大大改善这一点

While the cell array expansion (octave only) scales well: 虽然单元阵列扩展(仅限八度)可以很好地扩展:

A = 127:100000;
tic; gcd(num2cell(A){:}); toc
Elapsed time is 0.0537438 seconds.

Divide and conquer algorithm (best) 划分和征服算法(最好)

This one should work under matlab too (not tested though. Feedback welcome). 这个应该在matlab下工作(虽然没有经过测试。欢迎反馈)。

It uses recursion too, like in other answers, but with Divide and conquer 它也使用递归,就像在其他答案中一样,但是使用Divide和conquer

function g = gcd_array(A)
  N = numel(A);

  if (mod(N, 2) == 0)
    % even number of elements
    % separate in two parts of equal length
    idx_cut = N / 2;
    part1 = A(1:idx_cut);
    part2 = A(idx_cut+1:end);
    % use standard gcd to compute gcd of pairs
    g = gcd(part1(:), part2(:));
    if ~ isscalar(g)
       % the result was an array, compute its gcd
       g = gcd_array(g);
    endif
  else
    % odd number of elements
    % separate in one scalar and an array with even number of elements
    g = gcd(A(1), gcd_array(A(2:end)));
  endif
endfunction

timings: 定时:

A = 127:100000;
tic; gcd_array(A); toc
Elapsed time is 0.0184278 seconds.

So this seems even better than cell array expansion. 所以这似乎比单元阵列扩展更好。

Note that unlike Octave, Matlab gcd function requires exactly two input arguments. 请注意,与Octave不同,Matlab gcd函数只需要两个输入参数。 You can use recursion to handle that, due to the fact that gcd(a,b,c) = gcd(a,gcd(b,c)) . 由于gcd(a,b,c) = gcd(a,gcd(b,c))你可以使用递归来处理它。 The following function accepts both input formats - either a single vector, or multiple scalars inputs, and should work both in Matlab and Octave: 以下函数接受两种输入格式 - 单个向量或多个标量输入,并且应该在Matlab和Octave中都有效:

function divisor = gcd_vect(a, varargin)
    if ~isempty(varargin)
        a = [a, varargin{:}];
    elseif length(a) == 1
        divisor = a;
        return;
    end
    divisor = gcd(a(1), gcd_vect(a(2:end)));
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM