Generate all possible combinations of a few variables of a maximum length using certain mathematical operators/functions?

I’m trying to do something that shouldn’t be impossible but is a bit cumbersome and tricky for me to figure out how to write: I want to first define a set of mathematical operators/functions that I allow, for example lets say I allow +, -, *, / and cos() of a variable (so, addition, subtraction, multiplication, division and cosine in other words). And I have a number of variables, lets say a and b to keep it simple.
I would like to generate all possible combinations of a and b that can be calculated using these allowed operators but whilst keeping the expression under a specific length. So say I want to limit the length to only allow the usage of a maximum of two variables, it could be that a is used two times, a is used one time, b is used two times, b is used one time or that a and b are together used to generate the combination.
To give and example of what I mean – using the rules I have stated above in my example, a and b would generate the following combinations:
a
b
a+b
a+a
b+a
b+b
a-a
a-b
b-a
b-b
a*a
a*b
b*b
b*a
a/a
a/b
b/a
a/b
b/b
cos(a)
cos(b)
cos(a)+b
cos(a)-b
cos(a)*cos(b)
a/cos(b)
cos(b)/cos(a)
And so on and so on. Even with just two variables and five allowed operators the number of combinations become pretty much too many to manually list, but I hope you understand what I want to create.
If this is possible to create I would also like to be able to add more allowed operators, so for example sin(), sqrt(), tan(), mean(),log(), exp() and so on. And I would also like to be able to generate the combinations with more than two variables and with a slightly longer allowed max length, for example 3 or 4 variable usages.
Is this in any way possible to generate?
Thanks in advance.

 Accepted Answer

There are three parts to what you want:
  1. generate all possible combinations of variables with an unary operator
  2. generate all possible combinations of 2 or more arguments and
  3. apply all possible binary operators to the previous combinations
For 1: This is the cartesian product of the set of unary operators with the set of variables. You achieve this with ndgrid
variables = {'a', 'b'}; %can be as many as you want. limited by memory
unaryops = {'?', 'cos(?)'}; %can be as many as you want. ? to be replaced by variable
[idxuop, idxvar] = ndgrid(1:numel(unaryops), 1:numel(variables)); %cartesian product of indices
uopvar = strrep(unaryops(idxuop), '?', variables(idxvar));
For 2: again this a cartesian product, but you need to add an empty element to the uopvar set (for the case where no binary operation is applied)
uopvar = [uopvar(:); {''}]; %add empty element
[idxvar1, idxvar2] = ndgrid(1:numel(uopvar)); %cartesian product with self
binoparg1 = uopvar(idxvar1(:));
binoparg2 = uopvar(idxvar2(:));
3 is fairly straighforward. You can use a loop or cellfun to apply all binary operators to the arguments of step 2, using strcat:
binaryops = {'+', '-', '/', '*'};
expressions = cellfun(@(bop) strcat(binoparg1, bop, binoparg2), binaryops(:), 'UniformOutput', false);
expressions = vertcat(expressions{:})
All that's left is some tidying up for those cases where no binary ops are applied (disallowing these would simply things). regexprep to remove these binaryops attached to empty variable, and unique to remove duplicate help:
expressions = unique(regexprep(expressions, sprintf('^[%1$s]|[%1$s]$', [binaryops{:}]), ''))

5 Comments

Note: I've just realised that as written step 2 and 3 only work with two variables. It can be extended for more but involves some cell arrays manipulations.
Step 1 works with as many variables as you want.
Step 2 for an indefinite amount of variables:
uopvar = [uopvar(:); {''}]; %add empty element
idxvars = cell(1, numel(variables)); %cell arrays to receive the output of ndgrid
[idxvars{:}] = ndgrid(1:numel(uopvar));
binopvars = cellfun(@(ixv) reshape(uopvar(ixv), [], 1), idxvars, 'UniformOutput', false);
For step 3, there's now an additional step, in that you need to generate all combinations of operators to go between the variables. You need sequences of numel(variables)-1 operators:
binaryops = {'+', '-', '/', '*'};
idxopseqs = cell(1, numel(variables)-1);
[idxopseqs{:}] = ndgrid(1:numel(binaryops));
opseqs = cellfun(@(ixos) reshape(binaryops(ixos), [], 1), idxopseqs, 'UniformOutput', false);
opseqs = [opseqs{:}];
To combine it all:
expressions = cell(size(opseqs, 1), 1);
for opidx = 1:size(opseqs, 1)
opseq = opseqs(opidx, :);
catargs = [binopvars; opseq, {''}];
expressions{opidx} = strcat(catargs{:});
end
expressions = vertcat(expressions{:})
The tidying up gets more complex as well:
expressions = unique(regexprep(expressions, sprintf('^[%1$s]|[%1$s](?=[%1$s])|[%1$s]$', [binaryops{:}]), ''))
Oh very nice! Thank you what a masterful answer, I would never have figured that out on my own.
I did find one thing odd though. When I run it with the first steps set up like this:
variables = {'a', 'b'}; %can be as many as you want. limited by memory
unaryops = {'a', 'cos(a)','sin(a)','tan(a)','sqrt(a)'};
the resulting expressions include some that are called for example “a/tbn(b)” which I don’t understand. Where does tbn come from?
unaryops is supposed to be made of strings with a '?' placeholder for the variable. Thus unaryops should be:
unaryops = {'?', ... %identity operation
'cos(?)', 'sin(?)', 'tan(?)', ... %trigonometric functions
'sqrt(?)'};
The '?' is later replaced by the actual variable in that line:
uopvar = strrep(unaryops(idxuop), '?', variables(idxvar));
Oh, I misunderstood that. It works great now, thank you!

Sign in to comment.

More Answers (1)

This is certainly possible and to perform this easily i'd tackle this in two parts.
  1. create function to perform single variable operations the sin(), cos(), tan(), sqrt()... with input of variable to be operated
  2. another function for inbetween operations (+,-,/,...) with input of two variables to be operated on.
each function would also take in a number which you'll use as a switch/case statement to get the output.
With something like this you can write nested for loops to go through combinations for however many variables.

1 Comment

I’m not entirely sure what you mean. Do you mean I should create a function that takes one variable and performs all the allowed operations on it? So that functionOne(a) returns:
a
Sin(a)
Cos(a)
Tan(a)
Etc.
And functionOne(b) returns:
b
Sin(b)
Cos(b)
Tan(b)
Etc.
And function two takes the outputs of functionOne(a) and functionOne(b) and generates the calculation between them? So that functionTwo(functionOne(a),functionOne(b)) returns:
a + b
Sin(a) + sin(b)
Cos(a) + Cos(b)
Tan(a) + tan(b)
Etc.
Or did I misunderstand you?

Sign in to comment.

Products

Asked:

on 8 Feb 2016

Commented:

on 8 Feb 2016

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!