Saving intermediate variable values of a recursive function - matlab

I'm trying to code a function calculating difference quotients. I need to this for polynomial interpolation. Given nodes x = linspace(a,b,n+1), function values at the nodes y = func(x) I want to find the values of difference quotients f[x0], f[x_0,x_1], ..., f[x_0,x_1,...,x_n]. To calculate f[x_0,x_1,...,x_n] I will need f[x_0,x_1,...,x_(n-1)], etc., thus it would be a good idea save the intermediate steps on my way to f[x_0,x_1,...,x_n], so that on my way to f[x_0,x_1,...,x_n] I will save the preceding difference quotients as elements of a vector.
Could someone tell me how to correct my code in order to save appropriate difference quotient values? Here's the code:
function [fx, all_fx] = ilo2(a,b,x,y,fx,all_fx)
if a == b
fx(end+1) = y(a);
if a == 1
all_fx(end+1) = fx(end);
end
return
end
a;
b;
[c, all_fx] = ilo2(a+1,b,x,y,fx,all_fx);
[d, all_fx] = ilo2(a,b-1,x,y,fx,all_fx);
fx(end+1) = (c-d)/(b-a);
if a == 1
all_fx(end+1) = fx(end);
end
end
The difference quotients I need are under 'if a == 1' condition.

Ok, I think I fixed it:
function [all_fx,fx] = ilo(a,b,x,y,all_fx,fx)
if a == b
fx(end+1) = y(a);
if a == 1
all_fx(end+1) = fx(end);
end
return
end
[all_fx,c] = ilo(a+1,b,x,y,all_fx,fx);
[all_fx,d] = ilo(a,b-1,x,y,all_fx,fx);
fx(end+1) = (c-d)/(b-a);
if a == 1
all_fx(end+1) = fx(end);
end
end

Related

Extracting values from a function

I have a loop within a function that spits out values similar to:
E = 3,2,1,-1,-2
for
i = 1,2,3,4,5
I'm trying to extract the position where E becomes negative and then identify the step before it.
My attempt was something like
finalPos = find(i(E<0));
Firstly, it just doesn't seem right (my matlab syntax knowledge is poor as)
but secondly even if it did work it would tell me all positions where E is less than 0, where I only want to know the position before where E is no longer positive. i.e. E = 1, i = 3
Any help would be greatly appreciated!
Check the below:
E = [3,2,1,-1,-2] ;
idx = find(sign(E)==-1) % Get the sign and get index
idx = find(E<0) % Get by value
for i = 1:length(E)
if sign(E(i)) == -1
fprintf('E is Negative\n')
else
fprintf('E is Positive\n')
end
end
For last positive value
you can use a variable to store the last value
Example:-
E=[3,2,1,-1,-2]
finalpos=-1
for i = 1:5
if ( E<0)
finalpos=E(i-1);
break;
end
end
finalpos

Generate random matrix with eigenvalues

I am doing the following to generate random matrices with eigenvalues in a specific range:
function mat = randEig(dim, rReal)
D=diff(rReal).*rand(dim,1)+rReal(1);
P=rand(dim);
mat=P*diag(D)/P;
end
But I also want to be able to generate random real matrices with complex (conjugate) eigenvalues. How would one do that? The similarity transformation trick would return complex matrices.
EDIT: Okay I managed to do it by piggybacking on MATLAB's cdf2rdf function (which is basically the second function below).
function mat = randEig(dim, rangeEig, nComplex)
if 2*nComplex > dim
error('Cannot happen');
end
if nComplex
cMat=diff(rangeEig).*rand(dim-2*nComplex,1)+rangeEig(1);
for k=1:nComplex
rpart=(diff(rangeEig).*rand(1,1)+rangeEig(1))*ones(2,1);
ipart=(diff(rangeEig).*rand(1,1)+rangeEig(1))*i;
ipart=[ipart; -ipart];
cMat=[cMat; rpart+ipart];
end
else
cMat=diff(rangeEig).*rand(dim,1)+rangeEig(1);
end
D=cMat;
realDform = comp2rdf(diag(D));
P=rand(dim);
mat=P*realDform/P;
end
function dd = comp2rdf(d)
i = find(imag(diag(d))');
index = i(1:2:length(i));
if isempty(index)
dd=d;
else
if (max(index)==size(d,1)) | any(conj(d(index,index))~=d(index+1,index+1))
error(message('Complex conjugacy not satisfied'));
end
j = sqrt(-1);
t = eye(length(d));
twobytwo = [1 1;j -j];
for i=index
t(i:i+1,i:i+1) = twobytwo;
end
dd=t*d/t;
end
end
But the code is ugly, mainly the way rand is called multiple times is annoying). If someone wants to post an answer that calls rand once and manages to do the trick I will surely accept and upvote.
I made it either a single call or two calls with this:
function mat = randEig(dim, rangeEig, nComplex)
if 2*nComplex > dim
error('Cannot happen');
end
if nComplex
cMat=diff(rangeEig).*rand(2*nComplex,1)+rangeEig(1);
cPart=cMat(1:nComplex)*i;
cMat(1:nComplex)=[];
cPart=upsample(cPart,2);
cPart=cPart+circshift(-cPart,1);
cMat=upsample(cMat,2);
cMat=cMat+circshift(cMat,1);
cMat=cMat+cPart;
cMat=[diff(rangeEig).*rand(dim-2*nComplex,1)+rangeEig(1); cMat];
else
cMat=diff(rangeEig).*rand(dim,1)+rangeEig(1);
end
D=cMat;
realDform = comp2rdf(diag(D));
P=rand(dim);
mat=P*realDform/P;
end
function dd = comp2rdf(d)
i = find(imag(diag(d))');
index = i(1:2:length(i));
if isempty(index)
dd=d;
else
if (max(index)==size(d,1)) | any(conj(d(index,index))~=d(index+1,index+1))
error(message('Complex conjugacy not satisfied'));
end
j = sqrt(-1);
t = eye(length(d));
twobytwo = [1 1;j -j];
for i=index
t(i:i+1,i:i+1) = twobytwo;
end
dd=t*d/t;
end
end
If someone can make it a single call or shorter/more elegant code they are welcome to post an answer.

matlab vectorization if statement

Can someone please tell me the vectorized implementation of following matlab code. Predicted is an array containing either of the two values "pos" or "neg". I have to copy the values when condition comes true.
p = 1;
box = zeros(size(bbox));
for k = 1: size(predicted)
if predicted(k) == 'pos'
box(p,:) = bbox(k,:);
p = p + 1;
end
end
bbox=rand(100); %demo data
predicted = rand(1,100)>0.5; %logical values
%You want to convert your array of strings into an array of logical values
%predicted=strcmp(predicted,'pos');
box=bbox(predicted,:);

Error of declaration of variable

I have a problem at the beginning of my function. The function is to combine several data column from some objects. Error happens at the beginning of function. It says as follows:
Error in find_by_coor (line 2)
for i = 1:length(obj_ac)
Here is only the declaration of variable and loop, but Matlab somehow returned error. I have no idea so would like someone to help me. I attached my code as follows. Thanks a lot in advance.
function arr = find_by_coor(obj_ac,obj_gps,obj_sen_dir,lat1,long1,lat2,long2)
for i = 1:length(obj_ac)
if eq(obj_sen_dir(i).sensor,4) && strcmp(obj_sen_dir(i).direction,'outbound')
ind = obj_gps(i).save_var_gps(:,1)>lat1;
if isempty(find(ind)) == 1
continue
end
temp = obj_gps(i).save_var_gps(ind,:);
ind = temp(:,1)<lat2;
if isempty(find(ind)) == 1
continue
end
temp2 = temp(ind,:);
ind = temp2(:,2)<long1;
if isempty(find(ind)) == 1
continue
end
temp3 = temp2(ind,:);
ind = temp3(:,2)>long2;
if isempty(find(ind)) == 1
continue
end
temp4 = temp3(ind,:);
mint = min(temp4(:,5))-min(obj_gps(i).save_var_gps(:,5));
maxt = max(temp4(:,5))-min(obj_gps(i).save_var_gps(:,5));
if isempty(mint) == 1 || isempty(maxt) == 1
continue
end
if floor(mint*(1.6516e+03)) == 0 || floor(maxt*(1.6516e+03)) == 0
continue
end
temp5 = obj_ac(i).save_var(floor(mint*(1.6516e+03)):floor(maxt*(1.6516e+03)));
temp6 = abs(fft(temp5));
arr(i,:) = [i objs(i).daten var(temp5) max(temp5) min(temp5) mean(temp5) std(temp5) mode(temp5) var(temp6) max(temp6) min(temp6) mean(temp6) std(temp6) mode(temp6)];
disp(i);
end
end
end
The problem is that when you run the function, the output variable arr is never assigned. In Matlab you must always assign a function output if you choose to have it in the definition. For example
function [a,b] = setAB()
err = 0; % Gives an error if err is true
a = 1;
if ~err
b = 1;
end
The reason is most certainly that for some inputs, all values fall into one of the if statements and you do never reach the point where arr is assigned. A good solution for this is to assign a default value for arr in the beginning. That may for example be nan or -1 or, in your case maybe an array arr = nan(wanted size) or arr = -1*ones(wanted size). If you do not preallocate arr you will likely get a "matrix out of bounds" error instead, should you solve the first issue.
It does not mean that you always need to have an output though.
function [] = noOutput()
disp('Hi, I am a void!');
You may also choose to return as many values as number of outputs.
function varargout = variableArgs()
a = 1;
b = 2;
c = 3;
if (nargout == 1)
varargout{1} = a;
elseif (nargout == 2)
varargout{1} = b;
varargout{2} = c;
else
error('Wrong number of output arguments!');
end
I am not saying which of the approaches you should use or that any of them are good. Normally I use varargout in case I write plotting functions. Then I may want to return nothing in case I do not have an output argument. Then I want to return handles or any extra information. Further as you may have understood there is also a varargin that may be of more use.

How to improve execution time of the following Matlab code

Please help me to improve the following Matlab code to improve execution time.
Actually I want to make a random matrix (size [8,12,10]), and on every row, only have integer values between 1 and 12. I want the random matrix to have the sum of elements which has value (1,2,3,4) per column to equal 2.
The following code will make things more clear, but it is very slow.
Can anyone give me a suggestion??
clc
clear all
jum_kel=8
jum_bag=12
uk_pop=10
for ii=1:uk_pop;
for a=1:jum_kel
krom(a,:,ii)=randperm(jum_bag); %batasan tidak boleh satu kelompok melakukan lebih dari satu aktivitas dalam satu waktu
end
end
for ii=1:uk_pop;
gab1(:,:,ii) = sum(krom(:,:,ii)==1)
gab2(:,:,ii) = sum(krom(:,:,ii)==2)
gab3(:,:,ii) = sum(krom(:,:,ii)==3)
gab4(:,:,ii) = sum(krom(:,:,ii)==4)
end
for jj=1:uk_pop;
gabh1(:,:,jj)=numel(find(gab1(:,:,jj)~=2& gab1(:,:,jj)~=0))
gabh2(:,:,jj)=numel(find(gab2(:,:,jj)~=2& gab2(:,:,jj)~=0))
gabh3(:,:,jj)=numel(find(gab3(:,:,jj)~=2& gab3(:,:,jj)~=0))
gabh4(:,:,jj)=numel(find(gab4(:,:,jj)~=2& gab4(:,:,jj)~=0))
end
for ii=1:uk_pop;
tot(:,:,ii)=gabh1(:,:,ii)+gabh2(:,:,ii)+gabh3(:,:,ii)+gabh4(:,:,ii)
end
for ii=1:uk_pop;
while tot(:,:,ii)~=0;
for a=1:jum_kel
krom(a,:,ii)=randperm(jum_bag); %batasan tidak boleh satu kelompok melakukan lebih dari satu aktivitas dalam satu waktu
end
gabb1 = sum(krom(:,:,ii)==1)
gabb2 = sum(krom(:,:,ii)==2)
gabb3 = sum(krom(:,:,ii)==3)
gabb4 = sum(krom(:,:,ii)==4)
gabbh1=numel(find(gabb1~=2& gabb1~=0));
gabbh2=numel(find(gabb2~=2& gabb2~=0));
gabbh3=numel(find(gabb3~=2& gabb3~=0));
gabbh4=numel(find(gabb4~=2& gabb4~=0));
tot(:,:,ii)=gabbh1+gabbh2+gabbh3+gabbh4;
end
end
Some general suggestions:
Name variables in English. Give a short explanation if it is not immediately clear,
what they are indented for. What is jum_bag for example? For me uk_pop is music style.
Write comments in English, even if you develop source code only for yourself.
If you ever have to share your code with a foreigner, you will spend a lot of time
explaining or re-translating. I would like to know for example, what
%batasan tidak boleh means. Probably, you describe here that this is only a quick
hack but that someone should really check this again, before going into production.
Specific to your code:
Its really easy to confuse gab1 with gabh1 or gabb1.
For me, krom is too similar to the built-in function kron. In fact, I first
thought that you are computing lots of tensor products.
gab1 .. gab4 are probably best combined into an array or into a cell, e.g. you
could use
gab = cell(1, 4);
for ii = ...
gab{1}(:,:,ii) = sum(krom(:,:,ii)==1);
gab{2}(:,:,ii) = sum(krom(:,:,ii)==2);
gab{3}(:,:,ii) = sum(krom(:,:,ii)==3);
gab{4}(:,:,ii) = sum(krom(:,:,ii)==4);
end
The advantage is that you can re-write the comparsisons with another loop.
It also helps when computing gabh1, gabb1 and tot later on.
If you further introduce a variable like highestNumberToCompare, you only have to
make one change, when you certainly find out that its important to check, if the
elements are equal to 5 and 6, too.
Add a semicolon at the end of every command. Having too much output is annoying and
also slow.
The numel(find(gabb1 ~= 2 & gabb1 ~= 0)) is better expressed as
sum(gabb1(:) ~= 2 & gabb1(:) ~= 0). A find is not needed because you do not care
about the indices but only about the number of indices, which is equal to the number
of true's.
And of course: This code
for ii=1:uk_pop
gab1(:,:,ii) = sum(krom(:,:,ii)==1)
end
is really, really slow. In every iteration, you increase the size of the gab1
array, which means that you have to i) allocate more memory, ii) copy the old matrix
and iii) write the new row. This is much faster, if you set the size of the
gab1 array in front of the loop:
gab1 = zeros(... final size ...);
for ii=1:uk_pop
gab1(:,:,ii) = sum(krom(:,:,ii)==1)
end
Probably, you should also re-think the size and shape of gab1. I don't think, you
need a 3D array here, because sum() already reduces one dimension (if krom is
3D the output of sum() is at most 2D).
Probably, you can skip the loop at all and use a simple sum(krom==1, 3) instead.
However, in every case you should be really aware of the size and shape of your
results.
Edit inspired by Rody Oldenhuis:
As Rody pointed out, the 'problem' with your code is that its highly unlikely (though
not impossible) that you create a matrix which fulfills your constraints by assigning
the numbers randomly. The code below creates a matrix temp with the following characteristics:
The numbers 1 .. maxNumber appear either twice per column or not at all.
All rows are a random permutation of the numbers 1 .. B, where B is equal to
the length of a row (i.e. the number of columns).
Finally, the temp matrix is used to fill a 3D array called result. I hope, you can adapt it to your needs.
clear all;
A = 8; B = 12; C = 10;
% The numbers [1 .. maxNumber] have to appear exactly twice in a
% column or not at all.
maxNumber = 4;
result = zeros(A, B, C);
for ii = 1 : C
temp = zeros(A, B);
for number = 1 : maxNumber
forbiddenRows = zeros(1, A);
forbiddenColumns = zeros(1, A/2);
for count = 1 : A/2
illegalIndices = true;
while illegalIndices
illegalIndices = false;
% Draw a column which has not been used for this number.
randomColumn = randi(B);
while any(ismember(forbiddenColumns, randomColumn))
randomColumn = randi(B);
end
% Draw two rows which have not been used for this number.
randomRows = randi(A, 1, 2);
while randomRows(1) == randomRows(2) ...
|| any(ismember(forbiddenRows, randomRows))
randomRows = randi(A, 1, 2);
end
% Make sure not to overwrite previous non-zeros.
if any(temp(randomRows, randomColumn))
illegalIndices = true;
continue;
end
end
% Mark the rows and column as forbidden for this number.
forbiddenColumns(count) = randomColumn;
forbiddenRows((count - 1) * 2 + (1:2)) = randomRows;
temp(randomRows, randomColumn) = number;
end
end
% Now every row contains the numbers [1 .. maxNumber] by
% construction. Fill the zeros with a permutation of the
% interval [maxNumber + 1 .. B].
for count = 1 : A
mask = temp(count, :) == 0;
temp(count, mask) = maxNumber + randperm(B - maxNumber);
end
% Store this page.
result(:,:,ii) = temp;
end
OK, the code below will improve the timing significantly. It's not perfect yet, it can all be optimized a lot further.
But, before I do so: I think what you want is fundamentally impossible.
So you want
all rows contain the numbers 1 through 12, in a random permutation
any value between 1 and 4 must be present either twice or not at all in any column
I have a hunch this is impossible (that's why your code never completes), but let me think about this a bit more.
Anyway, my 5-minute-and-obvious-improvements-only-version:
clc
clear all
jum_kel = 8;
jum_bag = 12;
uk_pop = 10;
A = jum_kel; % renamed to make language independent
B = jum_bag; % and a lot shorter for readability
C = uk_pop;
krom = zeros(A, B, C);
for ii = 1:C;
for a = 1:A
krom(a,:,ii) = randperm(B);
end
end
gab1 = sum(krom == 1);
gab2 = sum(krom == 2);
gab3 = sum(krom == 3);
gab4 = sum(krom == 4);
gabh1 = sum( gab1 ~= 2 & gab1 ~= 0 );
gabh2 = sum( gab2 ~= 2 & gab2 ~= 0 );
gabh3 = sum( gab3 ~= 2 & gab3 ~= 0 );
gabh4 = sum( gab4 ~= 2 & gab4 ~= 0 );
tot = gabh1+gabh2+gabh3+gabh4;
for ii = 1:C
ii
while tot(:,:,ii) ~= 0
for a = 1:A
krom(a,:,ii) = randperm(B);
end
gabb1 = sum(krom(:,:,ii) == 1);
gabb2 = sum(krom(:,:,ii) == 2);
gabb3 = sum(krom(:,:,ii) == 3);
gabb4 = sum(krom(:,:,ii) == 4);
gabbh1 = sum(gabb1 ~= 2 & gabb1 ~= 0)
gabbh2 = sum(gabb2 ~= 2 & gabb2 ~= 0);
gabbh3 = sum(gabb3 ~= 2 & gabb3 ~= 0);
gabbh4 = sum(gabb4 ~= 2 & gabb4 ~= 0);
tot(:,:,ii) = gabbh1+gabbh2+gabbh3+gabbh4;
end
end