4

gcd (A1, A2, ...) computes the GCD of elements A1(1), A2(1), .... Being the elements stored in a vector A, how to compute gcd (A)?
(I mean, gcd (4, 2, 8) = 2, gcd ([4, 2, 8] will raise an error in GNU Octave 4.0.0).

nightcod3r
  • 752
  • 1
  • 7
  • 26
  • 2
    @stewie-griffin, which Matlab version you use? 2015b and [2016a](http://uk.mathworks.com/help/matlab/ref/gcd.html) definitely require two input arguments. – nirvana-msu May 28 '16 at 13:36

3 Answers3

1

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
K. Lindsay
  • 33
  • 1
  • 8
1

With cell array expansion

Here is a one-liner, valid only in octave (thanks to nirvana-msu for pointing out matlab's limitation):

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))


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,

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.

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

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).

It uses recursion too, like in other answers, but with Divide and 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.

ederag
  • 2,409
  • 25
  • 49
  • 1
    This only works in Octave, **not Matlab**, since `gcd` in Matlab requires exactly two input arguments. – nirvana-msu May 29 '16 at 13:04
  • 1
    That is not to mention that `num2cell(A){:}` is not a valid Matlab syntax. Matlab does not support dynamic indexing of this sort, you'll have to do it in two separate calls, or use one of those [ugly hacks](http://stackoverflow.com/questions/3627107/how-can-i-index-a-matlab-array-returned-by-a-function-without-first-assigning-it). – nirvana-msu May 29 '16 at 13:20
  • @nirvana-msu Thanks for pointing out matlab's limitations. But the question has the `octave` tag. – ederag May 29 '16 at 15:03
  • the question mentions both Matlab and Octave, both in title and tags. So the least you should have done is make it very clear in your answer that it is only relevant to Octave, not Matlab. Otherwise that would confuse people coming here looking for a Matlab solution, especially since OP accepted this answer (which is a mistake, given the way the question is phrased currently). – nirvana-msu May 29 '16 at 15:06
  • 1
    @nirvana-msu The number of recursion calls in other solutions was a strong limitation, at least under octave. Please find an updated solution that is faster, and hopefully working both under octave and matlab. – ederag Jun 01 '16 at 09:57
0

Note that unlike Octave, Matlab gcd function requires exactly two input arguments. You can use recursion to handle that, due to the fact that 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:

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
nirvana-msu
  • 3,877
  • 2
  • 19
  • 28