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.
13 Answers
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)
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 :)
-
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 likesort(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 ofconv(1:5,[1 1],'same')
\$\endgroup\$ Commented Dec 27, 2016 at 2:03
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.
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
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')
.
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.
-
1\$\begingroup\$ Great trick! You can even golf it down to
fft(1:n==2)
\$\endgroup\$– flawrCommented Oct 16, 2017 at 15:13 -
\$\begingroup\$ @flawr I never know the precedence rules... Thanks! \$\endgroup\$ Commented Oct 16, 2017 at 15:19
nnz
can sometimes save you a few bytes:
- Imagine you want the sum of a logical matrix
A
. Instead ofsum(sum(A))
orsum(A(:))
, you can usennz(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 usennz(x)
. This is applicable for instance ifx
is a string.
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.
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
-
\$\begingroup\$ Second kernel as numbers, same byte count:
toeplitz([0 1 0])
\$\endgroup\$ Commented Mar 27, 2019 at 9:55
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;
-
2\$\begingroup\$ I like to use
~(1:n)
for 1-d zero vectors. \$\endgroup\$– sintaxCommented Jun 20, 2016 at 19:51
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)
-
\$\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\$– flawrCommented Feb 4, 2020 at 8:34
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);
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