I want to create a truth table in MatLab with i columns and i2 rows. For example, if i=2, then
T =
[0 0]
[1 0]
[0 1]
[1 1]
Code to do this has already been created here
This is part of a larger project, which requires i large. Efficiency is a concern. Is there more efficient code to create a truth table? Does MatLab have a built in function to do this?
Edit: Sorry about the formatting!
Something like this?
n=2;
d=[0:2^n-1].';
T=dec2bin(d,n)
T =
00
01
10
11
dec2bin will give you a character array, which you can convert to logical, if needed. There's also de2bi that gives you a numeric array directly, but you need a newer version of Matlab and the ordering of the bits is reversed.
Here's Luis Mendo's speedup, which replicates dec2bin (n and d are as above):
T=rem(floor(d*pow2(1-n:0)),2);
ndgrid is very much your friend here:
function t = truthTable(n)
dims = repmat({[false, true]}, 1, n);
[grids{1:n}] = ndgrid(dims{:});
grids = cellfun(#(g)g(:), grids, 'UniformOutput',false);
t = [grids{:}];
First you need to create grids for the number of dimensions in your truth table. Once you have those you can columnize them to get the column vectors you need and you can horizontally concatenate those column vectors to get your truth table.
I imagine the performance of this will be quite competitive.
>> truthTable(2)
ans =
0 0
1 0
0 1
1 1
>> truthTable(4)
ans =
0 0 0 0
1 0 0 0
0 1 0 0
1 1 0 0
0 0 1 0
1 0 1 0
0 1 1 0
1 1 1 0
0 0 0 1
1 0 0 1
0 1 0 1
1 1 0 1
0 0 1 1
1 0 1 1
0 1 1 1
1 1 1 1
>>
>> timeit(#() truthTable(20))
ans =
0.030922626777
EDIT: Use reshape instead of column dereferencing for further performance improvement
function t = truthTable(n)
dims = repmat({[false, true]}, 1, n);
[grids{1:n}] = ndgrid(dims{:});
grids = cellfun(#(g) reshape(g,[],1), grids, 'UniformOutput',false);
t = [grids{:}];
>> timeit(#() truthTable(20))
ans =
0.016237298777
I know this question has been dead a while, but I was wondering the same thing and found a solution I like a lot. Thought I'd share it here:
fullfact(ones(1, i) + 1) - 1
Related
I hope you can help with a little problem I am having.
I want to upsample and downsample a vector with zeros and ones. We have the functions upsample and downsample for that, however, the upsample function in Matlab only adds zeros to the vector. I would like to repeat the value, instead of just putting in zeros.
Unfortunately the upsample function does not do that. Thus, I tried to use repmat (in the third dimension) and then reshape to get back to the old format. I know it must be possible with these functions, but if I simply use them, the vector just gets duplicated and added to the end.
An example:
The input vector is: [1 0 0 1 0 1 0 1 1 1 0 0] (these should be random).
Now I want to upsample (say) by a factor of 2. Then I want to get:
[1 1 0 0 0 0 1 1 0 0 1 1 0 0 1 1 1 1 1 1 0 0 0 0].
Thanks in advance for any help!
You can use repelem:
>> repelem([1 0 1],2)
ans =
1 1 0 0 1 1
Or using repmat and reshape when input is a column vector:
>> input = [1 0 1];
>> reshape(repmat(input, 2, 1), 1, [])
ans =
1 1 0 0 1 1
I want to construct a matrix A in Matlab of dimension w x (m*w) where
each row is full of zeros except m consecutive ones that shift towards the right hand side as we move down to the rows.
Few examples can clarify
w=3,m=4
A=[1 1 1 1 0 0 0 0 0 0 0 0;
0 0 0 0 1 1 1 1 0 0 0 0;
0 0 0 0 0 0 0 0 1 1 1 1]
or
w=3, m=3
A=[1 1 1 0 0 0 0 0 0;
0 0 0 1 1 1 0 0 0;
0 0 0 0 0 0 1 1 1]
or
w=2, m=3
A=[1 1 1 0 0 0;
0 0 0 1 1 1]
I can't see how to proceed and any hint would be extremely helpful.
Step 1. Simplify the problem
If you write the "modified diagonal matrix" you are asking about as a row vector it will always look like the following
% 1 ... 1 0 ... ... 0 ... ... ... ... ... ... ... ... 1 ... 1
% m ones m*w zeros w-1 times the same as before m ones
Step 2. Think how to solve the simplified problem
The fundamental unit you need is a vector of m ones followed by m*w zeros;
Once you have built such vector, you need it to be repeated w times, MATLAB already knows how to do that;
The only thing you miss are the trailing ones: append them;
Now that the vector you were looking for is completed, you need to turn it into a matrix. MATLAB already knows how to do this too.
Final code
Once you understood the above steps, the final behaviour can be achieved even with a one-liner
>> m = 4; w = 3;
>> vec2mat([repmat([ones(1, m) zeros(1, m*w)], 1, w-1) ones(1, m)], w*m)
ans =
1 1 1 1 0 0 0 0 0 0 0 0
0 0 0 0 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 1 1 1 1
About speed
It's true, for loops aren't so slow anymore. I timed my one-liner solution, the trivial for loop and Luis Mendo's solution with eye() and repelem().
Click on images to zoom
Tested on the same machine, with MATLAB R2018a.
As you can see, as long as m and w are quite small, even if you could point out some differences in speed, them won't be noticeable to humans.
Anyway if you are going to work with bigger matrices, it becomes quite obvious which solution is the best.
Here are some approaches:
Using eye and repelem:
A = repelem(eye(w), 1, m);
Using eye and indexing:
A = eye(w);
A = A(1:w, ceil(1/m:1/m:w));
Using eye and kron:
A = kron(eye(w), ones(1,m));
Using singleton expansion:
A = bsxfun(#eq, (1:m).', ceil(1/m:1/m:w)); % Or A = (1:m).'==ceil(1/m:1/m:w);
I have a 3D matrix A (size m*n*k) where m=latitude, n*longitude and k=time.
I want only specific values from first and second dimension, specified by a logical matrix B (size m*n), and I want only the timesteps specified by vector C (size k).
In the end this should become a 2D matrix D, since the first two dimesions will colapse to one.
What is the most easy approach to do this?
And also is it possible to combine logical with linear indizes here? For example B is logical and C is linear?
Sample code with rand:
A=rand(10,10,10);
B=randi([0 1], 10,10);
C=randi([0 1], 10,1);
D=A(B,C) %This would be my approach which doesnt work. The size of D should be sum(B)*sum(c)
Another example without rand:
A=reshape([1:27],3,3,3);
B=logical([1,0,0;1,0,0;0,0,0]);
C=(1,3); %get data from timestep 1 and 5
D=A(B,C);%What I want to do, but doesnÄt work that way
D=[1,19;2,20];%Result should look like this! First dimension is now all data from dimesion 1 and 2. New dimesion 2 is now the time.
A = rand(4,4,4);
B = randi([0 1], 4,4)
B =
1 1 0 1
1 0 1 1
0 0 1 0
1 0 1 1
>> C = randi([0 1],1,1,4);
>> C(:)
ans =
0
1
1
0
Then use bsxfun or implicit expansion expansion whith .* if newer Matlab version to generate a matrix of logical for you given coordinates.
>> idx = logical(bsxfun(#times,B,C))
idx(:,:,1) =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
idx(:,:,2) =
1 1 0 1
1 0 1 1
0 0 1 0
1 0 1 1
idx(:,:,3) =
1 1 0 1
1 0 1 1
0 0 1 0
1 0 1 1
idx(:,:,4) =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
Then your output is D = A(idx). However, note that this D is now an Nx1 array. Where N is number of true elements is B times number of true elements in C. 10x True in B and 2x True in C:
>> size(D)
ans =
20 1
An easy way to do it is to first reshape A into an m*n-by-k matrix, then do your indexing:
result = reshape(A, [], size(A, 3));
result = result(B, C);
In this case C can be either a logical vector or vector of indices.
I have arrays that stores only binary numbers like the below, binaries are of the size 1x31. Now I want to make the last bit the first and the first bit the last and so on. The choice of data structure is probably very poor here -- when I learn to play with binaries I probably get rid of the array. The binaries make the ordering of the arrays far easier with a simple sort. Anyway this is puzzle now:
Is there some ready command in Matlab for changing desceding binary to asceding binary?
Input
>> C(21,:)
ans =
(1,11) 1
(1,16) 1
(1,17) 1
>> full(C(21,:))
ans =
Columns 1 through 11
0 0 0 0 0 0 0 0 0 0 1
Columns 12 through 22
0 0 0 0 1 1 0 0 0 0 0
Columns 23 through 31
0 0 0 0 0 0 0 0 0
Goal for the output with some command such as invertDec2Asc
>> invertDec2Asc(C(21,:))
ans =
(1,21) 1
(1,16) 1
(1,15) 1
Try using num2str followed by fliplr
revnum = fliplr( num2str(num) )
Test
num = ['101010';'010101']
revnum = fliplr( num2str(num) )
num =
101010
010101
revnum =
010101
101010
flipud or fliplr is what you're looking for.
Matlab documentation
fliplr([1 0 1 0]) = [0 1 0 1]
fliplr('1010') = '0101'
format of binaries in matlab: '1010', e.g. created with dec2bin(10)
Say I have a vector containing only logical values, such as
V = [1 0 1 0 1 1 1 1 0 0]
I would like to write a function in MATLAB which returns a 'streak' vector S for V, where S(i) represents the number of consecutive 1s in V up to but not including V(i). For the example above, the streak vector would be
S = [0 1 0 1 0 1 2 3 4 0]
Given that I have to do this for a very large matrix, I would very much appreciate any solution that is vectorized / efficient.
This should do the trick:
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
The complexity is only O(n), which I guess should be good enough.
For your sample input:
V = [1 0 1 0 1 1 1 1 0 0];
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
display(V);
display(S);
The result would be:
V =
1 0 1 0 1 1 1 1 0 0
S =
0 1 0 1 0 1 2 3 4 0
You could also do it completely vectorized with a couple intermediate steps:
V = [1 0 1 0 1 1 1 1 0 0];
Sall = cumsum(V);
stopidx = find(diff(V)==-1)+1;
V2=V;
V2(stopidx) = -Sall(stopidx)+[0 Sall(stopidx(1:end-1))];
S2 = cumsum(V2);
S = [0 S2(1:end-1)];
Afaik the only thing that can take a while is the find call; you can't use logical indexing everywhere and bypass the find call, because you need the absolute indices.
It's outside the box - but have you considered using text functions? Since strings are just vectors for Matlab it should be easy to use them.
Regexp contains some nice functions for finding repeated values.