17
\$\begingroup\$

What general tips do you have for golfing in MATLAB? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to MATLAB (e.g. "remove comments" is not an answer). Please post one tip per answer.

\$\endgroup\$
1

13 Answers 13

10
\$\begingroup\$

Something that one must know before starting to golf:

In MATLAB calculations a character behaves the same as its ascii code.

'abc' - 'a'  % Returns: [0 1 2]
'123' - '0'  % Returns: [1 2 3]
'“' == 8220  % Returns: 1 (logical)
'a':'e'==100 % Returns: [0 0 0 1 0] (logical)
\$\endgroup\$
10
\$\begingroup\$

Shortening property names

In MATLAB, strings identifying properties can be shortened as long as it does not result in ambiguity.

plot(X,'C','k') % Ambiguous property found.
plot(X,'Co','k') % Expands to Color  (black)

This actually won me a challenge :)

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Very nice, though the answer is correct I want to emphasize that this applies to the name of name, value pairs as shown above. (So not to things like sort(rand(4,1),'descend')) \$\endgroup\$ Commented Jul 30, 2015 at 9:53
  • 1
    \$\begingroup\$ It does apply to some of those things too, like conv(1:5,[1 1],'s') instead of conv(1:5,[1 1],'same') \$\endgroup\$
    – Luis Mendo
    Commented Dec 27, 2016 at 2:03
6
\$\begingroup\$

Casting as char can be done by concatenation with a char:

x='a'+magic(5) % Array with character codes of several letters

char(x) % The standard way
['' x] % The compact way

Though it only saves one char, this can be used quite frequently.

\$\endgroup\$
5
\$\begingroup\$

Strings are just character row vectors. This means that instead of

for i=numel(str)
    a=str(i)
    ...
end

you can simply write

for(a=str)
    ...
end

First time I used this: https://codegolf.stackexchange.com/a/58387/32352

\$\endgroup\$
4
\$\begingroup\$

Related, but not identical tips for Octave.

A little known and little used feature of both MATLAB and Octave is that most builtin functions can be called without parentheses, in which case they will treat whatever follows it as a string (as long as it doesn't contain spaces). If it contains spaces you need quotation marks. This can frequently be used to save a byte when using disp:

disp('Hello, World!')
disp 'Hello, World!'

Other, less useful examples include:

nnz PPCG
ans = 4

size PPCG
ans = 1  4

str2num 12
ans = 12

I've actually used this twice in the "How high can you count?"-challenge:

strchr sssssssssssssst t

is equivalent to strchr('sssssssssssssst','t') and returns 15.

nnz nnnnnnnnnnnnnn

is equivalent to nnz('nnnnnnnnnnnnnn') and returns 14.

Stuff like gt r s works too (equivalent to 'r'>'s' or gt('r','s').

\$\endgroup\$
4
\$\begingroup\$

Roots of unity via discrete Fourier transform

Given a positive integer n, the standard way to generate the n-th roots of unity is

exp(2j*pi*(0:n-1)/n)

This gives the roots starting at 1 and moving in the positive angular direction. If order doesn't matter, this can be shortened to

exp(2j*pi*(1:n)/n)

Since exp(2j*pi/4) equals the imaginary unit (j), this can be written more compactly as follows (trick due to @flawr):

j.^(4*(0:n-1)/n)

or

j.^(4*(1:n)/n)

But the discrete Fourier transform provides an even shorter way (thanks to @flawr for removing two unnecessary parentheses):

fft(1:n==n)

which gives the roots starting at 1 and moving in the positive angular direction; or

fft(1:n==2)

which starts at 1 and moves in the negative angular direction.


Try all of the above here.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Great trick! You can even golf it down to fft(1:n==2) \$\endgroup\$
    – flawr
    Commented Oct 16, 2017 at 15:13
  • \$\begingroup\$ @flawr I never know the precedence rules... Thanks! \$\endgroup\$
    – Luis Mendo
    Commented Oct 16, 2017 at 15:19
3
\$\begingroup\$

nnz can sometimes save you a few bytes:

  • Imagine you want the sum of a logical matrix A. Instead of sum(sum(A)) or sum(A(:)), you can use nnz(a) (nnz implitictly applies (:)).
  • If you want to know the number of elements of an array, and you can be sure there are no zeros, instead of numel(x) you can use nnz(x). This is applicable for instance if x is a string.
\$\endgroup\$
3
\$\begingroup\$

Iteration over vectors in matrices.

Given a set of vector as matrix, you can actually iterate over them via a single for loop like

for v=M
    disp(v);
end

while "traditionally" you probably would have done it like

for k=1:n
    disp(M(:,k));
end

I've only learned about this trick just now from @Suever in this challenge.

\$\endgroup\$
3
\$\begingroup\$

2D Convolution Kernels

This is maybe a niche topic, but apparently some people like to use convolution for various things here. [citation needed]

In 2D following kernels are often needed:

0 1 0
1 1 1
0 1 0

This can be achieved using

v=[1,2,1];v'*v>1 %logical
v=[1,0,1];1-v'*v  %as numbers

which is shorter than

[0,1,0;1,1,1;0,1,0]

Another kernel often used is

0 1 0
1 0 1
0 1 0

which can be shortened using

v=[1,-1,1];v'*v<0   % logical
[0,1,0;1,0,1;0,1,0] % naive verison
\$\endgroup\$
1
  • \$\begingroup\$ Second kernel as numbers, same byte count: toeplitz([0 1 0]) \$\endgroup\$
    – Luis Mendo
    Commented Mar 27, 2019 at 9:55
2
\$\begingroup\$

The built-in ones and zeros are a typically a waste of space. You can achieve the same result by simply multiplying an array/matrix (of the desired size) by 0 (to get the output of zeros) and add 1 if you want the output of ones.

d = rand(5,2);

%// Using zeros
z = zeros(size(d));

%// Not using zeros
z = d*0;

%// Using ones
o = ones(size(d));

%// Not using ones
o = 1+d*0

This also works if you want to create a column or row vector of zeros or ones the size of one dimension of a matrix.

p = rand(5,2);

z = zeros(size(p,1), 1);
z = 0*p(:,1);

o = ones(size(p, 1), 1);
o = 1+0*p(:,1);

If you want to create a matrix of a specific size you could use zeros but you could also just assign the last element to 0 and have MATLAB fill in the rest.

%// This
z = zeros(2,3);

%// vs. This
z(2,3) = 0;
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I like to use ~(1:n) for 1-d zero vectors. \$\endgroup\$
    – sintax
    Commented Jun 20, 2016 at 19:51
2
\$\begingroup\$

I quite often find myself using meshgrid or ndgrid, let's say we want to compute a mandelbrot image, then we initialize e.g.

[x,y]=meshgrid(-2:1e-2:1,-1:1e-2,1)

Now for the mandelbrot set we need another matrix c of the size of x and y but initialized with zeros. This can easily be done by writing:

c=x*0;

You can also initialize it to another value:

c=x*0+3;

But you can actually save some bytes by just adding another dimension in meshgrid/ndgrid:

[x,y,c]=meshgrid(-2:1e-2:1,-1:1e_2,1, 0); %or for the value 3
[x,y,c]=meshgrid(-2:1e-2:1,-1:1e_2,1, 3);

And you can do this as often as you want:

[x,y,c1,c2,c3,c4,c5]=meshgrid(-2:1e-2:1,-1:1e_2,1, 1,pi,exp(3),1e5,-3i)
\$\endgroup\$
1
  • \$\begingroup\$ Note that in the meantime there is automatic broadcasting: In many cases the first example could be replaced by x=-2:1d-2:1;y=x'. \$\endgroup\$
    – flawr
    Commented Feb 4, 2020 at 8:34
1
\$\begingroup\$

Counting the number of non zero elements in a matrix

zeronum(i)=nnz C;
zeronum(i)=nnz(C);
zeronum(i)=sum(C~=0);
zeronum(i)=sum(abs(C)>=eps);
zeronum(i)=numel(find(C~=0));
zeronum(i)=prod(size(find(C~=0)));

Horizontal replication is 3 copies, vertical replication is 2 copies

repmat([1,2;3,4],2,3)
kron(ones(2,3),[1,2;3,4])

Multiple variables, all initialized with the same data

[A,B,C,D]=deal([1 4;2 5;3 6]);

%%%%
A = [1 4; 2 5; 3 6];
B=A;
C=A;
D=A;

Declare a zero matrix of the same size as matrix A

X=0*A(:,1);
X=zeros(size(A));
X=zeros(size(A),'like',A);
\$\endgroup\$
0
\$\begingroup\$

Summation of a sequence of functions

  • For summing up functions f(x_n) where n is a vector of consecutive integers, feval is adviced rather than symsum.

    Syms x;symsum(f(x),x,1,n);
    Sum(feval(@(x)f(x),1:n));
    

    Notice that an elementary operation .* and ./ is necessary instead of pairwise binary operations * and /

  • If the function can be naively written no one from either last ways is suitable.

    for example if the function is log you can simply do: sum(log(1:n)) , which represents:

    Sum(f(1:n));
    

    for relatively sophisticated functions as log(n)/x^n you can do:

    Sum(log(1:n)./5.^(1:n))
    

    and even shorter in some cases when a function is longer as f(x)=e^x+sin(x)*log(x)/x....

    Sum(feval(@(y)e.^(y)+sin(y).*log(y)./y,1:n))
    

    that is remarkably shorter than sum(feval(@(y)e.^(1:n)+sin(1:n).*log(1:n)./(1:n),1:n))


Note: This trick can be applied for other inclusive operators as prod or mean


\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.