Nested for loop flow control in MATLAB - matlab

trying to write a function to compute different account balances using the compound interest formula in matlab. In this case, it needs to be able to support multiple Interest rates and number of years input. I'm currently using nested for loops, and it works fine for the first set of interest rates, but then it runs only the last number of years calculating for the second and any later values in the interest input array
function ans = growth_of_money(P,I,n)
disp([' P ','n ','I ','Total']);
for I = I
for n = n
disp([P,n,I,compound_interest(P,I,n)]);
end
end
end
function T = compound_interest(P,I,n)
T= P.*((1.+I).^n);
end
This is the output I'm getting currently:
P n I Total
2 1 4 10
2 3 4 250
2 3 7 1024
What am I missing? How can I make it go back to the first value of n for the second run of I?

As others have pointed out, there are quite a few things wrong with your code. You can inspect the code below to work out how to improve your code.
For single value inputs (e.g., one value of n, one of I).
function growth_of_money(P,I,n)
T = compound_interest(P,I,n);
fprintf(1,'P: %.1f; N: %.1f; I: %.1f; Total: %.1f; \n',P,I,n,T);
end
function T = compound_interest(P,I,n)
T= P.*((1.+I).^n);
end
For multiple value inputs, and assuming you want to use disp function (which really, is not ideal), you can use something like this:
function growth_of_money(P,I,n)
disp('P N I Total');
for i=I
for k=n
T = compound_interest(P,i,k);
disp([P i k T]);
end
end
end
function T = compound_interest(P,I,n)
T= P.*((1.+I).^n);
end

Related

Make vector of elements less than each element of another vector

I have a vector, v, of N positive integers whose values I do not know ahead of time. I would like to construct another vector, a, where the values in this new vector are determined by the values in v according to the following rules:
- The elements in a are all integers up to and including the value of each element in v
- 0 entries are included only once, but positive integers appear twice in a row
For example, if v is [1,0,2] then a should be: [0,1,1,0,0,1,1,2,2].
Is there a way to do this without just doing a for-loop with lots of if statements?
I've written the code in loop format but would like a vectorized function to handle it.
The classical version of your problem is to create a vector a with the concatenation of 1:n(i) where n(i) is the ith entry in a vector b, e.g.
b = [1,4,2];
gives a vector a
a = [1,1,2,3,4,1,2];
This problem is solved using cumsum on a vector ones(1,sum(b)) but resetting the sum at the points 1+cumsum(b(1:end-1)) corresponding to where the next sequence starts.
To solve your specific problem, we can do something similar. As you need two entries per step, we use a vector 0.5 * ones(1,sum(b*2+1)) together with floor. As you in addition only want the entry 0 to occur once, we will just have to start each sequence at 0.5 instead of at 0 (which would yield floor([0,0.5,...]) = [0,0,...]).
So in total we have something like
% construct the list of 0.5s
a = 0.5*ones(1,sum(b*2+1))
% Reset the sum where a new sequence should start
a(cumsum(b(1:end-1)*2+1)+1) =a(cumsum(b(1:end-1)*2+1)+1)*2 -(b(1:end-1)+1)
% Cumulate it and find the floor
a = floor(cumsum(a))
Note that all operations here are vectorised!
Benchmark:
You can do a benchmark using the following code
function SO()
b =randi([0,100],[1,1000]);
t1 = timeit(#() Nicky(b));
t2 = timeit(#() Recursive(b));
t3 = timeit(#() oneliner(b));
if all(Nicky(b) == Recursive(b)) && all(Recursive(b) == oneliner(b))
disp("All methods give the same result")
else
disp("Something wrong!")
end
disp("Vectorised time: "+t1+"s")
disp("Recursive time: "+t2+"s")
disp("One-Liner time: "+t3+"s")
end
function [a] = Nicky(b)
a = 0.5*ones(1,sum(b*2+1));
a(cumsum(b(1:end-1)*2+1)+1) =a(cumsum(b(1:end-1)*2+1)+1)*2 -(b(1:end-1)+1);
a = floor(cumsum(a));
end
function out=Recursive(arr)
out=myfun(arr);
function local_out=myfun(arr)
if isscalar(arr)
if arr
local_out=sort([0,1:arr,1:arr]); % this is faster
else
local_out=0;
end
else
local_out=[myfun(arr(1:end-1)),myfun(arr(end))];
end
end
end
function b = oneliner(a)
b = cell2mat(arrayfun(#(x)sort([0,1:x,1:x]),a,'UniformOutput',false));
end
Which gives me
All methods give the same result
Vectorised time: 0.00083574s
Recursive time: 0.0074404s
One-Liner time: 0.0099933s
So the vectorised one is indeed the fastest, by a factor approximately 10.
This can be done with a one-liner using eval:
a = eval(['[' sprintf('sort([0 1:%i 1:%i]) ',[v(:) v(:)]') ']']);
Here is another solution that does not use eval. Not sure what is intended by "vectorized function" but the following code is compact and can be easily made into a function:
a = [];
for i = 1:numel(v)
a = [a sort([0 1:v(i) 1:v(i)])];
end
Is there a way to do this without just doing a for loop with lots of if statements?
Sure. How about recursion? Of course, there is no guarantee that Matlab has tail call optimization.
For example, in a file named filename.m
function out=filename(arr)
out=myfun(in);
function local_out=myfun(arr)
if isscalar(arr)
if arr
local_out=sort([0,1:arr,1:arr]); % this is faster
else
local_out=0;
end
else
local_out=[myfun(arr(1:end-1)),myfun(arr(end))];
end
end
end
in cmd, type
input=[1,0,2];
filename(input);
You can take off the parent function. I added it just hoping Matlab can spot the recursion within filename.m and optimize for it.
would like a vectorized function to handle it.
Sure. Although I don't see the point of vectorizing in such a unique puzzle that is not generalizable to other applications. I also don't foresee a performance boost.
For example, assuming input is 1-by-N. In cmd, type
input=[1,0,2];
cell2mat(arrayfun(#(x)sort([0,1:x,1:x]),input,'UniformOutput',false)
Benchmark
In R2018a
>> clear all
>> in=randi([0,100],[1,100]); N=10000;
>> T=zeros(N,1);tic; for i=1:N; filename(in) ;T(i)=toc;end; mean(T),
ans =
1.5647
>> T=zeros(N,1);tic; for i=1:N; cell2mat(arrayfun(#(x)sort([0,1:x,1:x]),in,'UniformOutput',false)); T(i)=toc;end; mean(T),
ans =
3.8699
Ofc, I tested with a few more different inputs. The 'vectorized' method is always about twice as long.
Conclusion: Recursion is faster.

Mnist dataset pattern recognition accuracy

I'm completely new to matlab and this is my first project. Mnist has 60000 picture between 0 and 9 for training and 1000 picture to test. what I did is try to make a pattern for all of this 10 class (0 to 9) by using mean.then for for recognition I use Euclidean distance. this is very simple but the accuracy is really low.
I don't know where is exactly my problem to give my back this percentage of accuracy. the accuracy :1.73%
here is my code
finding 10 pattern for all of our class:
root = 'F:\matlab\ex1\exercise-EquivaliencOfL2DistanceAndDotProduct\dataset';
fn = strcat (root, '\MnistTrainX.mat');
load (fn);
fn = strcat (root, '\MnistTrainY.mat');
load (fn);
weights = zeros (10, 784);
b = zeros (10, 1);
im=reshape(MnistTrainX(5,:),[28 ,28]);
imshow(im,[]);
imshow(im',[]);
for c=1 : 10
idx=find(MnistTrainY == c-1);
weights (c,:)=mean( MnistTrainX(idx,:));
end
trainAccuray = ComputeInnerProductAccuracy(weights,b, MnistTrainX,MnistTrainY);
display(trainAccuray);
fn = strcat (root, '\MnistTestX.mat');
load (fn);
fn = strcat (root, '\MnistTestY.mat');
load (fn);
testAccuray = ComputeInnerProductAccuracy(weights, b, MnistTestX, MnistTestY);
display(testAccuray);
and this is accuracy function
function [acc]=ComputeInnerProductAccuracy(weights, b, X, Y)
n = size(X, 1);
minmat = zeros (60000, 2);
endmat = zeros (60000, 10);
m = size(X);
a=0;
for i=1 : n
for j=1 : 10
endmat(i,j)=sum((X(i,:)-(weights(j,:))).^2,2);
end
[minmat(i,1) ,minmat(i,2)]= min(endmat(i,:));
if minmat(i,2)== Y(i)
a=a+1;
end
end
acc=(a*100)/60000;
end
Your code is mostly correct, though it's quite inefficient. I won't spend the time to make it more efficient as there are many areas that need addressing. Instead I'll focus on what is wrong. There are two things wrong with the code. Firstly is when you find which digit has the lowest distance:
[minmat(i,1) ,minmat(i,2)]= min(endmat(i,:));
Note that the second output of min produces the location of where the minimum is starting at index 1. The class values in Y should contain 0 to 9 but the output index of min in your case is from 1 to 10. The output minimum indices and the corresponding class values are 1 off from each other, which is probably the reason why you have such bad accuracy.
Therefore, you have to subtract 1 from minmat(i, 2) before you check to see if the minimum label is indeed the ground truth... or you can simply add 1 to Y(i) when checking:
[minmat(i,1) ,minmat(i,2)]= min(endmat(i,:));
if minmat(i,2)== Y(i)+1 % Change
a=a+1;
end
The second thing that is incorrect is that the "inner product" function (actually you're computing the Euclidean distance.... but let's put that aside for this answer) assumes that there are always 60000 inputs yet your test set doesn't have this many inputs. This will work fine on your training data but it will report the wrong accuracy for your test data. Make sure you change all instances of 60000 in the function to n. This variable you've already created in your code and determines how many inputs there are.

How can I rearrange a list of numbers so that every N numbers is nonrepeating?

So I have a list of 190 numbers ranging from 1:19 (each number is repeated 10 times) that I need to sample 10 at a time. Within each sample of 10, I don't want the numbers to repeat, I tried incorporating a while loop, but computation time was way too long. So far I'm at the point where I can generate the numbers and see if there are repetitions within each subset. Any ideas?
N=[];
for i=1:10
N=[N randperm(19)];
end
B=[];
for j=1:10
if length(unique(N(j*10-9:j*10)))<10
B=[B 1];
end
end
sum(B)
Below is an updated version of the code. this might be a little more clear in showing what I want. (19 targets taken 10 at a time without repetition until all 19 targets have been repeated 10 times)
nTargs = 19;
pairs = nchoosek(1:nTargs, 10);
nPairs = size(pairs, 1);
order = randperm(nPairs);
values=randsample(order,19);
targs=pairs(values,:);
Alltargs=false;
while ~Alltargs
targs=pairs(randsample(order,19),:);
B=[];
for i=1:19
G=length(find(targs==i))==10;
B=[B G];
end
if sum(B)==19
Alltargs=true;
end
end
Here are some very simple steps to do this, basically you just shuffle the vector once, and then you grab the last 10 unique values:
v = repmat(1:19,1,10);
v = v(randperm(numel(v)));
[a idx]=unique(v);
result = unique(v);
v(idx)=[];
The algorithm should be fairly efficient, if you want to do the next 10, just run the last part again and combine the results into a totalResult
You want to sample the numbers 1:19 randomly in blocks of 10 without repetitions. The Matlab function 'randsample' has an optional 'replacement' argument which you can set to 'false' if you do not want repetitions. For example:
N = [];
replacement = false;
for i = 1:19
N = [N randsample(19,10,replacement)];
end
This generates a 19 x 10 matrix of random integers in the range [1,..,19] without repetitions within each column.
Edit: Here is a solution that addresses the requirement that each of the integers [1,..,19] occurs exactly 10 times, in addition to no repetition within each column / sample:
nRange = 19; nRep = 10;
valueRep = true; % true while there are repetitions
nLoops = 0; % count the number of iterations
while valueRep
l = zeros(1,nRep);
v = [];
for m = 1:nRep
v = [v, randperm(nRange,nRange)];
end
m1 = reshape(v,nRep,nRange);
for n = 1:nRep
l(n) = length(unique(m1(:,n)));
end
if all(l == nRep)
valueRep = false;
end
nLoops = nLoops + 1;
end
result = m1;
For the parameters in the question it takes about 300 iterations to find a result.
I think you should approach this constructively.
It's easy to initially find a 19 groups that fulfill your conditions just by rearranging the series 1:19: series1 = repmat(1:19,1,10); and rearranged= reshape(series1,10,19)
then shuffle the values
I would select two random columns copy them and switch the values at two random positions
then make a test if it fulfills your condition - like: test = #(x) numel(unique(x))==10 - if yes replace your columns
just keep shuffling till your time runs out or you are happy
of course you might come up with more efficient shuffling or testing
I was given another solution through the MATLAB forum that works pretty well (Credit to Niklas Nylen over on the MATLAB forum). Computation time is pretty low too. It basically shuffles the numbers until there are no repetitions within every 10 values. Thanks all for your help.
y = repmat(1:19,1,10);
% Run enough iterations to get the output random enough, I selected 100000
for ii = 1:100000
% Select random index
index = randi(length(y)-1);
% Check if it is allowed to switch places
if y(index)~=y(min(index+10, length(y))) && y(index+1)~=y(max(1,index-9))
% Make the switch
yTmp = y(index);
y(index)=y(index+1);
y(index+1)=yTmp;
end
end

How to repeat without using "for" or "while" or "repeat" functions?

Is there a function that can repeat a segment of code for a given number of times?
for example:
t= 0;
while (t< 10)
if x==2
x=1
else
x=3;
end
end
How can i rewrite this function using another function ?
A recursive function can do this for you (assuming you can't use: for,while,repeat).
http://www.matrixlab-examples.com/recursion.html
Or, if the code executed in one iteration is independent on the results of other iterations, you can use arrayfun or cellfun.
For instance
fun = #(x) disp(['hello ' , num2str(x)]);
arrayfun(fun,1:5);
returns
hello 1
hello 2
hello 3
hello 4
hello 5
Personally I do like these constructs because I find them very expressive just as std::for_each in C++.
Nonetheless, they have proven to be slower than their naive-loop counterparts which get JITed away by Matlab (there are several Q/A about this issue here on SO).
Matlab automatically 'repeats' the code for you if you put it in a vector format:
x_vector = round(2*rand(10,1)) %Your x input
idx = (x_vector==2)
x_vector(idx) = 1;
x_vector(~idx) = 3;

need explanation about a matlab code snippet about moran process

I am new to Matlab. I was reading this code snippet, but in some parts (marked with asterisks) I don't understand what it means, so if anybody could help would be very much appreciated
function [A1nmb] = moran(initsize, popsize)
% MORAN generates a trajectory of a Moran type process
% which gives the number of genes of allelic type A1 in a population
% of haploid individuals that can exist in either type A1 or type A2.
% The population size is popsize and the initial number of type A1
% individuals os initsize.
% Inputs: initsize - initial number of A1 genes
% popsize - the total population size (preserved)
if (nargin==0)
initsize=10;
popsize=30;
end
A1nmb=zeros(1,popsize);
A1nmb(1)=initsize;
**lambda = inline('(x-1).*(1-(x-1)./N)', 'x', 'N');
mu = inline('(x-1).*(1-(x-1)./N)', 'x', 'N');**
x=initsize;
i=1;
while (x>1 & x<popsize+1)
if (lambda(x,popsize)/(lambda(x,popsize)+mu(x,popsize))>rand)
x=x+1;
A1nmb(i)=x;
else
x=x-1;
A1nmb(i)=x;
end;
i=i+1;
end;
nmbsteps=length(A1nmb);
***rate = lambda(A1nmb(1:nmbsteps-1),popsize) ...
+mu(A1nmb(1:nmbsteps-1),popsize);***
**jumptimes=cumsum(-log(rand(1,nmbsteps-1))./rate);**
jumptimes=[0 jumptimes];
stairs(jumptimes,A1nmb);
axis([0 jumptimes(nmbsteps) 0 popsize+1]);
The first line you marked
lambda = inline('(x-1).*(1-(x-1)./N)', 'x', 'N');
creates something called an inline function. It is equivalent to defining a mathematical function. Example:
y = inline('x^2')
would allow you to do
>> y(2)
4
This immediately explains the second line you marked.
rate = lambda(A1nmb(1:nmbsteps-1),popsize) ...
+mu(A1nmb(1:nmbsteps-1),popsize);
will compute the value of the function lambda(x,N) at x = A1nmb(1:nmbsteps-1) and N = popsize.
I will say immediately here that you should take a look at anonymous functions, a different format used to accomplish the same as inline. Only, anonymous functions are generally better supported, and usually a lot faster than inline functions.
Then, for the final line,
jumptimes = cumsum(-log(rand(1,nmbsteps-1))./rate);
is a nested command. rand will create a matrix containing pseudorandom numbers, log is the natural logarithm ("ln"), and cumsum creates a new matrix, where all the elements in the new matrix are the cumulative sum of the elements in the input matrix.
You will find the commands doc and help very useful. Try typing
doc cumsum
or
help inline
on the Matlab command prompt. Try that again with the commands forming the previous statement.
As a general word of advice: spend an insane lot of time reading through the documentation. Really, for each new command you encounter, read about it and play with it in a sandbox until you feel you understand it. Matlab only becomes powerful if you know all its commands, and there are a lot to get to know.
It defines an inline function object. For example this
lambda = inline('(x-1).*(1-(x-1)./N)', 'x', 'N')
defines lambda as a function with 2 variables. When you call lambda(A,n) Matlab simply expands the function you define in the first string. Thus lambda(A,n) using the variables you provide in the function call. lambda(A,n) would will evaluate to:
(A-1).*(1-(A-1)./n)
it just expands the function using the parameters you supply. Take a look at this link for more specific details http://www.mathworks.co.uk/help/techdoc/ref/inline.html
The cumsum function just returns the cumulative sum of a matrix along a particular dimension. Say we call cumsum on a vector X, then the value at element i in the result is equal to the sum of elements in X from index 1 to i. For example X = [1 2 1 3] we would get
AA = cumsum(X);
we would have
AA = [1 3 5 8]
See this link for more details and examples http://www.mathworks.co.uk/help/techdoc/ref/cumsum.html