Save outputs of nested for loops in MATLAB - matlab

I have the following codes which I wish to have an output matrix Rpp of (10201,3). I run this code (which takes a bit long) then I check the matrix size of Rpp and I see (1,3), I tried so many things I couldn't find any proper way. The logic of the codes is to take the 6 values (contain 4 constant values and 2 variable values (chosen from 101 values)) and make the calculation for 3 different i1 and store every output vector of 3 in a matrix with (101*101 (pairs of those 2 variable values)) rows and 3 (for each i1) columns.
I appreciate your help
Vp1=linspace(3000,3500,101);
Vp2=3850;
rho1=2390;
rho2=2510;
Vs1=linspace(1250,1750,101);
Vs2=2000;
i1=[10 25 40];
Rpp = zeros(length(Vp1)*length(Vs1),length (i1));
for n=1:length(Vp1)*length(Vs1)
for m=1:length (i1)
for l=1:length(Vp1)
for k=1:length(Vs1)
p=sin(i1)/Vp1(l);
i2=asin(p*Vp2);
j1=asin(p*Vs1(k));
j2=asin(p*Vs2);
a=rho2*(1-2*Vs2^2*p.^2)-rho1*(1-2*Vs1(k).^2*p.^2);
b=rho2*(1-2*Vs2^2*p.^2)+2*rho1*Vs1(k)^2*p.^2;
c=rho1*(1-2*Vs1(k)^2*p.^2)+2*rho2*Vs2^2*p.^2;
d=2*(rho2*Vs2^2-rho1*Vs1(k)^2);
E=b.*cos(i1)./Vp1(l)+c.*cos(i2)/Vp2;
F=b.*cos(j1)./Vs1(k)+c.*cos(j2)/Vs2;
G=a-d*(cos(i1)/Vp1(l)).*(cos(j2)/Vs2);
H=a-d*(cos(i2)/Vp2).*(cos(j1)/Vs1(k));
D=E.*F+G.*H.*p.^2;
Rpp=((b.*(cos(i1)/Vp1(l))-c.*cos((i2)/Vp2)).*F-(a+d*((cos(i1)/Vp1(l))).*(cos(j2)/Vs2)).*H.*p.^2)./D
end
end
end
end

Try this. You 2 outer loops didn't do anything. You never used m or n so I killed those 2 loops. Also you just kept overwriting Rpp on every loop so your initialization of Rpp didn't do anything. I added an index var to assign the results to the equation to what I think is the correct part of Rpp.
Vp1=linspace(3000,3500,101);
Vp2=3850;
rho1=2390;
rho2=2510;
Vs1=linspace(1250,1750,101);
Vs2=2000;
i1=[10 25 40];
Rpp = zeros(length(Vp1)*length(Vs1),length (i1));
index = 1;
for l=1:length(Vp1)
for k=1:length(Vs1)
p=sin(i1)/Vp1(l);
i2=asin(p*Vp2);
j1=asin(p*Vs1(k));
j2=asin(p*Vs2);
a=rho2*(1-2*Vs2^2*p.^2)-rho1*(1-2*Vs1(k).^2*p.^2);
b=rho2*(1-2*Vs2^2*p.^2)+2*rho1*Vs1(k)^2*p.^2;
c=rho1*(1-2*Vs1(k)^2*p.^2)+2*rho2*Vs2^2*p.^2;
d=2*(rho2*Vs2^2-rho1*Vs1(k)^2);
E=b.*cos(i1)./Vp1(l)+c.*cos(i2)/Vp2;
F=b.*cos(j1)./Vs1(k)+c.*cos(j2)/Vs2;
G=a-d*(cos(i1)/Vp1(l)).*(cos(j2)/Vs2);
H=a-d*(cos(i2)/Vp2).*(cos(j1)/Vs1(k));
D=E.*F+G.*H.*p.^2;
Rpp(index,:)=((b.*(cos(i1)/Vp1(l))-c.*cos((i2)/Vp2)).*F-(a+d*((cos(i1)/Vp1(l))).*(cos(j2)/Vs2)).*H.*p.^2)./D;
index = index+1;
end
end
Results:
>> size(Rpp)
ans =
10201 3

The way you use the for loop is wrong. You're running the calculation for length(Vp1)*length(Vs1) * length (i1) * length(Vp1) * length(Vs1) times. Here's the correct way. I changed l into lll just so I won't confuse it with the number 1. In each iteration of the first for loop, you're running length(Vs1) times, and you need to assign the result (a 1X3 array) to the Rpp by using a row number specified by k+(lll-1)*length(Vp1).
for lll=1:length(Vp1)
for k=1:length(Vs1)
p=sin(i1)/Vp1(lll);
i2=asin(p*Vp2);
j1=asin(p*Vs1(k));
j2=asin(p*Vs2);
a=rho2*(1-2*Vs2^2*p.^2)-rho1*(1-2*Vs1(k).^2*p.^2);
b=rho2*(1-2*Vs2^2*p.^2)+2*rho1*Vs1(k)^2*p.^2;
c=rho1*(1-2*Vs1(k)^2*p.^2)+2*rho2*Vs2^2*p.^2;
d=2*(rho2*Vs2^2-rho1*Vs1(k)^2);
E=b.*cos(i1)./Vp1(lll)+c.*cos(i2)/Vp2;
F=b.*cos(j1)./Vs1(k)+c.*cos(j2)/Vs2;
G=a-d*(cos(i1)/Vp1(lll)).*(cos(j2)/Vs2);
H=a-d*(cos(i2)/Vp2).*(cos(j1)/Vs1(k));
D=E.*F+G.*H.*p.^2;
Rpp(k+(lll-1)*length(Vp1),:)=((b.*(cos(i1)/Vp1(lll))-c.*cos((i2)/Vp2)).*F-(a+d*((cos(i1)/Vp1(lll))).*(cos(j2)/Vs2)).*H.*p.^2)./D;
end
end

Related

Lotto code,the previous number cannot appear again,how do i improve it

I use matlab to write this code,and it seems there is something wrong with logic,but i don't know where am i wrong and how to improve this.
i want to write a lotto code,and there are six numbers in it,the range of first six numbers is 1 to 38,the range of last number is 1 to 8.Here is my code
previous_number=randi([1,38],1,6)
last=randi([1,8],1,1) %produce the last number
for k =1:6
while last== previous_number %while that last number is the same as the value of one of the previous number
last=randi([1,8],1,1)%then produce the last number again,until the different value produce
end
end
ltto=[previous_number last]
but i found that the last number will still generate the same number as the first six numbers,for example,
"1" 2 33 55 66 10 "1"
1 "2" 33 55 66 10 "2"
Why?i have already said
while last==previous_number(k)
last=randi([1,8],1,1)
end
if i want to write the code in c or other program language,i think i can just use if ,while and loop,etc,like this basic loop,i can't use the "ismemeber"or randperm. how can i rewrite the code?
if i rewrite as
previous_number=randi([1,38],1,6)
last=randi([1,8],1,1) %produce the last number
for k =1:6
if last== previous_number(k) %while that last number is the same as the value of one of the previous number
last=randi([1,8],1,1)%then produce the last number again,until the different value produce
end
end
ltto=[previous_number last]
the result will also show me "1" 2 21 12 13 22 "1" sometimes
This occures because you first iterate over the numbers, then replace last according to the specific current iteration, without regarding the previous ones.
For example, in your example data, think that last = 10 so you get to the sixth iteration, find that last is equal to b(k) that is 10, so you replace it. But now it can generate 1, and you will finish the while loop and the for loop.
The solution is to compare last to all your vector, not iterate over it:
previous_number = b(1:6);
last = previous_number(1);
while ismember(last, previous_number)
last = randi(8); %produce the last number
end
[As of comments discussion:]
If you still want to compare each element separately, you can do it like that:
previous_number=randi([1,38],1,6)
last=randi(8)
k=0;
while k <= 5
k = k + 1;
if last == previous_number(k)
last = randi(8);
k = 0;
end
end
ltto=[previous_number last]

Rows without repetitions - MATLAB

I have a matrix (4096x4) containing all possible combinations of four values taken from a pool of 8 numbers.
...
3 63 39 3
3 63 39 19
3 63 39 23
3 63 39 39
...
I am only interested in the rows of the matrix that contain four unique values. In the above section, for example, the first and last row should be removed, giving us -
...
3 63 39 19
3 63 39 23
...
My current solution feels inelegant-- basically, I iterate across every row and add it to a result matrix if it contains four unique values:
result = [];
for row = 1:size(matrix,1)
if length(unique(matrix(row,:)))==4
result = cat(1,result,matrix(row,:));
end
end
Is there a better way ?
Approach #1
diff and sort based approach that must be pretty efficient -
sortedmatrix = sort(matrix,2)
result = matrix(all(diff(sortedmatrix,[],2)~=0,2),:)
Breaking it down to few steps for explanation
Sort along the columns, so that the duplicate values in each row end up next to each other. We used sort for this task.
Find the difference between consecutive elements, which will catch those duplicate after sorting. diff was the tool for this purpose.
For any row with at least one zero indicates rows with duplicate rows. To put it other way, any row with no zero would indicate rows with no duplicate rows, which we are looking to have in the output. all got us the job done here to get a logical array of such matches.
Finally, we have used matrix indexing to select those rows from matrix to get the expected output.
Approach #2
This could be an experimental bsxfun based approach as it won't be memory-efficient -
matches = bsxfun(#eq,matrix,permute(matrix,[1 3 2]))
result = matrix(all(all(sum(matches,2)==1,2),3),:)
Breaking it down to few steps for explanation
Find a logical array of matches for every element against all others in the same row with bsxfun.
Look for "non-duplicity" by summing those matches along dim-2 of matches and then finding all ones elements along dim-2 and dim-3 getting us the same indexing array as had with our previous diff + sort based approach.
Use the binary indexing array to select the appropriate rows from matrix for the final output.
Approach #3
Taking help from MATLAB File-exchange's post combinator
and assuming you have the pool of 8 values in an array named pool8, you can directly get result like so -
result = pool8(combinator(8,4,'p'))
combinator(8,4,'p') basically gets us the indices for 8 elements taken 4 at once and without repetitions. We use these indices to index into the pool and get the expected output.
For a pool of a finite number this will work. Create is unique array, go through each number in pool, count the number of times it comes up in the row, and only keep IsUnique to 1 if there are either one or zero numbers found. Next, find positions where the IsUnique is still 1, extract those rows and we finish.
matrix = [3,63,39,3;3,63,39,19;3,63,39,23;3,63,39,39;3,63,39,39;3,63,39,39];
IsUnique = ones(size(matrix,1),1);
pool = [3,63,39,19,23,6,7,8];
for NumberInPool = 1:8
Temp = sum((matrix == pool(NumberInPool))')';
IsUnique = IsUnique .* (Temp<2);
end
UniquePositions = find(IsUnique==1);
result = matrix(UniquePositions,:)

Matlab: Loop issue

This is quite a simple issue, but I've been struggling with it. sortedd and sortedfinal_d are 8 x 1000 Matrices and I am using the loop below to check if any of the elements in sortedfinal_d lies between two consecutive elements of sortedd, in terms of magnitude. I'm doing this along each row. overall_p is a 8 x 1000 Matrix as well, but at the end of this process I end up having final_p as a Matrix of Zeros. I don't know why this is.
for k=2:1000
for s=1:1000
for j=1:8
if sortedd(j,k) > sortedfinal_d(j,s) && sortedfinal_d(j,s) > sortedd(j,k-1)
final_p(j,s) = overall_p(j,k);
end
end
end
end
EDIT: Added data for the inputs as shown below:
sortedd (first four columns) =
0.219977361620113 0.219996752039812 0.220344444223787 0.220593274018691
0.272807483153955 0.273682693068593 0.273846498221277 0.274060049642900
0.327201460264565 0.327375792227635 0.327572790857546 0.327856448530021
0.380389118311424 0.380845274148177 0.380893687870765 0.381015090963159
0.434832574575088 0.434860658844550 0.435021604722982 0.435119929919457
0.487119089589798 0.488128501559782 0.488207451439073 0.488430455768512
0.540652551559395 0.541303305046034 0.542195194863130 0.542234381085921
0.595254195563241 0.595296064375604 0.595376090156252 0.595377962767971
sortedfinal_d =
0.182086792394190 0.182406508309366 0.182406508309366 0.182808976400818
0.233058295607543 0.233058295607543 0.233158455616954 0.233158455616954
0.286243848617693 0.286357973626582 0.286918095670684 0.287393171241241
0.336938335090164 0.336938335090164 0.337094505106945 0.337669618738100
0.390287818652551 0.390567879874952 0.390567879874952 0.390670502700602
0.446995120903824 0.447270251510681 0.447452123072880 0.447597175111267
0.501060785098551 0.501060785098551 0.501060785098551 0.501060785098551
0.551311219045087 0.551463923687602 0.551463923687602 0.551653815175502
Thanks a lot
Do you have to use loops to accomplish this?
matching_d = sortedfinal_d(:,1:end-1) < sortedd(:,2:end) ...
& sortedd(:,2:end) < sortedfinal_d(:,2:end);
final_p(matching_d) = overall_p(matching_d);
If you can show us a small sample input (say, 1x5 versions of sortedd and sortedfinal_d) and output (the corresponding matching_d) it would be easier for us to help troubleshoot.
If i set your matrices to random numbers, final_p does return some numbers.
So your code works as is. Post your dataset or at least describe the dataset in some detail, this will make it much easier to diagnose the problem.
I have reduced 1000 down to 10 and re-ordered the iteration variables to i, j, k to make it easier to follow:
sortedd = rand(8, 10);
sortedfinal_d = rand(8, 10);
overall_p = rand(8, 10);
for i=2:10
for j=1:10
for k=1:8
if sortedd(k,i) > sortedfinal_d(k,j) && sortedfinal_d(k,j) > sortedd(k,i-1)
final_p(k,j) = overall_p(k,i);
end
end
end
end
final_p

Extracting rows from .mat table using for loop in MATLAB

What I have is a variable X which has values assigned to it in the form of a table of 9 columns and around 100 rows. Here is an example:
X =
Columns 1 through 7
-2.2869 -1.1168 0.1430 -4.0753 1.7620 -6.3229 -3.1997
-2.2504 -1.1022 0.2046 -3.9865 1.7423 -6.2172 -3.1231
-2.2138 -1.0876 0.2663 -3.8977 1.7226 -6.1115 -3.0465
-2.1772 -1.0730 0.3279 -3.8089 1.7029 -6.0058 -2.9700
I need to create a for loop that extracts the first r rows of the first 'p' colmuns. For example r=3 and p=4.
Any idea on how I can do that?
I suggest you don't use a for-loop, but rather index directly into the matrix:
out = X(1:r,1:p)
returns the first r rows and p columns of X.

Using SUM and UNIQUE to count occurrences of value within subset of a matrix

So, presume a matrix like so:
20 2
20 2
30 2
30 1
40 1
40 1
I want to count the number of times 1 occurs for each unique value of column 1. I could do this the long way by [sum(x(1:2,2)==1)] for each value, but I think this would be the perfect use for the UNIQUE function. How could I fix it so that I could get an output like this:
20 0
30 1
40 2
Sorry if the solution seems obvious, my grasp of loops is very poor.
Indeed unique is a good option:
u=unique(x(:,1))
res=arrayfun(#(y)length(x(x(:,1)==y & x(:,2)==1)),u)
Taking apart that last line:
arrayfun(fun,array) applies fun to each element in the array, and puts it in a new array, which it returns.
This function is the function #(y)length(x(x(:,1)==y & x(:,2)==1)) which finds the length of the portion of x where the condition x(:,1)==y & x(:,2)==1) holds (called logical indexing). So for each of the unique elements, it finds the row in X where the first is the unique element, and the second is one.
Try this (as specified in this answer):
>>> [c,~,d] = unique(a(a(:,2)==1))
c =
30
40
d =
1
3
>>> counts = accumarray(d(:),1,[],#sum)
counts =
1
2
>>> res = [c,counts]
Consider you have an array of various integers in 'array'
the tabulate function will sort the unique values and count the occurances.
table = tabulate(array)
look for your unique counts in col 2 of table.