How to generate a combination of 5 elements out of 8 elements where the final list should have only the combination of 5 elements which are two or more elements different from each other?
I started replacing the elements with numbers from 1-8 and generated the initial list using:
x = 1:8;
v = permn(x,5);
The initial list consists 8^5 numbers. e. g. 11111,11112,11113.....88888.
If I take the starting seed as 11111, the sorted numbers can be like, 11122, 11123 and so on. Because these numbers are atleast two element difference with 11111. But then from this second list, both 11122 and 11123 can't go to the next list because they have only one element difference.
The final list should have all these unique set of elements which has atleast two or more element difference with each other.
Can someone please help me to implement the condition?
An iterative solution have and order o(n^2) with n=size(v,1):
m=1
while m < size(v,1)
part2 = v(m+1:end, :);
%compare each row with the following rows e.g. part2.
u = bsxfun(#ne, v(m,:), part2);
%check if number of different elements greater than 1
s = sum(u,2) > 1;
%extract those rows and append to the current rows
v= [v(1:m,:); part2(s,:)];
m = m + 1;
end
The final v will have unique elements.
Create a bubble sort with inner and outer loop, compare elements from inner loop and outer loop based on rank (rank = 1 if two or more elements differ) thereafter swap elements if rank <> 0.
Pseudo Code
For I= 0 to (N-1)
For J= I+1 to N
If Rank(Array(I), Array(J)) <> 0 Then
Swap(Array(I), Array(J))
End
End
End
Related
I have a 64 X 64 matrix that I need to find the column-wise mean values for.
However, instead of dividing by the total number of elements in each column (i.e. 64), I need to divide by the total number of non-zeros in the matrix.
I managed to get it to work for a single column as shown below. For reference, the function that generates my matrix is titled fmu2(i,j).
q = 0;
for i = 1:64
if fmu2(i,1) ~= 0;
q = q + 1;
end
end
for i = 1:64
mv = (1/q).*sum(fmu2(i,1));
end
This works for generating the "mean" value of the first column. However, I'm having trouble looping this procedure so that I will get the mean for each column. I tried doing a nested for loop, but it just calculated the mean for the entire 64 X 64 matrix instead of one column at a time. Here's what I tried:
q = 0;
for i = 1:64
for j = 1:64
if fmu2(i,j) ~= 0;
q = q +1;
end
end
end
for i = 1:64
for j = 1:64
mv = (1/q).*sum(fmu2(i,j));
end
end
Like I said, this just gave me one value for the entire matrix instead of 64 individual "means" for each column. Any help would be appreciated.
For one thing, do not call the function that generates your matrix in each iteration of a loop. This is extremely inefficient and will cause major problems if your function is complex enough to have side effects. Store the return value in a variable once, and refer to that variable from then on.
Secondly, you do not need any loops here at all. The total number of nonzeros is given by the nnz function (short for number of non-zeros). The sum function accepts an optional dimension argument, so you can just tell it to sum along the columns instead of along the rows or the whole matrix.
m = fmu2(i,1)
averages = sum(m, 1) / nnz(m)
averages will be a 64-element array with an average for each column, since sum(m, 1) is a 64 element sum along each column and nnz(m) is a scalar.
One of the great things about MATLAB is that it provides vectorized implementations of just about everything. If you do it right, you should almost never have to use an explicit loop to do any mathematical operations at all.
If you want the column-wise mean of non-zero elements you can do the following
m = randi([0,5], 5, 5); % some data
avg = sum(m,1) ./ sum(m~=0,1);
This is a column-wise sum of values, divided by the column-wise number of elements not equal to 0. The result is a row vector where each element is the average of the corresponding column in m.
Note this is very flexible, you could use any condition in place of ~=0.
I want to initiate a mxn binary matrix. The summation of each column of the matrix equals to a given value s(j), for j=1..n. In addition, the summation of each row of the matrix should be within the range of given bounds: lhs is dl(i) and rhs is du(i), for i=1..m.
Now I can only randomly generate binary columns, in each of which the sum of ones equals to s(j) of that column, such as the following codes.
xij = zeros(m,n);
for j=1:n
randRows=randperm(m); %a row vector containing a random permutation of the integers from 1 to m inclusive.
rowsWithOne=randRows(1:sj(j)); %row indices having 1, and sum up to sj
xij(rowsWithOne,j)=1;
end
However, xij usually doesn't satisfy the horizontal constraints. I was thinking I should create a matrix first meets the row constraints lhs (lower bound) dl(i), then, column constraint s(j), and finally fill the offsets to meet rhs du(i), but I don't know how to implement it in Matlab. Is there any ideas to create xij?
Thanks in advance.
There are a couple things to first take into account, mainly if the problem can even be solved with the given constraints. First, you must check that the sum of s(j), sum(s) is greater than the sum of the lower bound constraints, sum(dl), and also less than the sum of the upper bound constraints, sum(du).
A simple if statement should be able to check this. After this has been done, the following code should be one solution to the problem, but given the nature of the problem, there will probably not be a unique solution.
%initialize counters
x = 1;
y = 1;
%make sure the lower bounds are first satisfied
for k = 1:nrows
while (dl(k) > 0)
if (s(y) > 0)
xij(k,y) = 1;
dl(k) = dl(k)-1;
du(k) = du(k)-1;
s(y) = s(y)-1;
end
y = y+1;
end
y = 1;
end
%make sure the columns all add to their specified values
for k = 1:ncols
while (s(k) > 0)
if (xij(x,k) == 0 && du(x) > 0)
xij(x,k) = 1;
du(x) = du(x)-1;
s(k) = s(k)-1;
end
x = x+1;
end
x = 1;
end
The first for loop adds 1s along the rows such that each row satisfies the minimum constraint. The next for loop adds 1s along the columns such that each column adds up to the desired value. The if statement in the first for loop ensures that a 1 isn't added if that column has already reached its desired value, and the if statement in the second for loop ensures that a 1 isn't added to a row that would put it over the maximum value of the row.
In order to keep track of the amount of ones, the du is lowered by 1 every time a 1 is added to the corresponding row, and likewise the desired value s(j) is subtracted by 1 every time a 1 is added to that column of the xij matrix.
%Create an initial xij matrix, each column of which consists of a random binary xij sequence, but sum of ones in each column equals to sj
Init_xij = zeros(nShift,nCombo);
for j=1:nCombo
randRows=randperm(nShift); %a row vector containing a random permutation of the integers from 1 to nShift inclusive.
rowsWithOne=randRows(1:sj(j)); %row indices having 1, and sum up to sj
Init_xij(rowsWithOne,j)=1;
end
%Adjust the initial xij matrix to make it feasible, satisfying horizontal
%LHS (dli) and RHS (dui) constraints
Init_xij_Feasible=Init_xij;
k=1;
while k
RowSum=sum(Init_xij_Feasible,2); %create a column vector containing the sum of each row
CheckLB=lt(RowSum,dli); %if RowSum <dli, true
CheckUB=gt(RowSum,dui); %if RowSum >dui, true
if ~any(CheckLB)&&~any(CheckUB) %if any element in CheckLB and CheckUB is zero
break,
else
[~,RowIdxMin]=min(RowSum);
[~,RowIdxMax]=max(RowSum);
ColIdx=find(Init_xij_Feasible(RowIdxMax,:) & ~Init_xij_Feasible(RowIdxMin,:),1); % returns the first 1 column index corresponding to the nonzero elements in row RowIdxMax and zero elements in row RowIdxMin.
%swap the min and max elements
[Init_xij_Feasible(RowIdxMin,ColIdx),Init_xij_Feasible(RowIdxMax,ColIdx)]=deal(Init_xij_Feasible(RowIdxMax,ColIdx),Init_xij_Feasible(RowIdxMin,ColIdx));
end
k=k+1;
end
Consider two n by n-1 matrix and an n by 1 vector (for example lets call them in order A, B and v). Elements of v are zero or one. If element v(m,1) is equal to one, I want to replace elements A(1:m-1,m-1) by B(1:m-1,m-1) and elements A(m+1:n,m) by B(m+1:n,m).
How can I do that? Could anyone help? To make the question more clear, consider below example.
example:
A=[1,2,3;4,5,6;7,8,9;12,13,14]
B=[3,4,5;6,7,8;9,10,11;6,5,3]
v=[0,1,0,1]
Result should be:
result= [3,2,5;4,5,8;7,10,11;12,5,14]
Here is an alternative, using logical indexing:
temp = A;
ind = 1:size(v,2);
for k = ind(v==1)
if k<=size(A,2)+1
A(1:k-1,k-1) = B(1:k-1,k-1);
B(1:k-1,k-1) = temp(1:k-1,k-1);
if k<size(A,2)
A(k+1:end,k) = B(k+1:end,k);
B(k+1:end,k) = temp(k+1:end,k);
end
end
end
You can acheive the desired result using find, which returns the indexes on non-zero elements, and a for-loop:
R = A; % assuming you've set A, B and v already.
n = size(A,1);
v1 = find(v);
for i=1:length(v1)
m=v1(i);
if m>1
R(1:m-1,m-1)=B(1:m-1,m-1);
end
if m<n
R(m+1:end,m)=B(m+1:end,m);
end
end
As I point out in a comment, v has to have length n-1 if v(n-1)=1 otherwise m+1:end is not a valid index range.
Edited to make second assignment optionally as per comments.
I need to find all possible combinations of numbers 1:8 such that sum of all elements is equal to 8
The combinations need to be arranged in an ascending order.
Eg
1 7
2 2 4
1 3 5
1 2 2 3
1 1 1 1 1 1 1 1
A number can repeat itself. But a combination must not..
i.e 1 2 2 3 and 2 1 2 3
I need the the solution in ascending order So there will be only one possibility of every combination
I tried a few codes online suggested on Find vector elements that sum up to specific number in MATLAB
VEC = [1:8];
NUM = 8;
n = length(VEC);
finans = zeros(2^n-1,NUM);
for i = 1:(2^n - 1)
ndx = dec2bin(i,n) == '1';
if sum(VEC(ndx)) == NUM
l = length(VEC(ndx));
VEC(ndx)
end
end
but they dont include the possibilities where the numbers repeat.
I found a better approach through recursion and it's more elegant (I like elegant) and faster than my previous attempt (0.00399705213 seconds on my computer).
EDIT: You will need my custom function stretchmat.m that stretches a vector to fit the size of another matrix. Kinda like repmat but stretching the first parameter (see help for details). Very useful!
script.m
% Define funciton to prepend a cell x with a variable i
cellprepend = #(x,i) {[i x]};
% Execute and time function
tic;
a = allcomb(cellprepend,1,8); % Solution in a
toc;
allcomb.m
function a = allcomb( cellprepend, m, n )
% Add entire block as a combination
a{1} = n;
% Exit recursion if block size 1
if n == 1
return;
end
% Recurse cutting blocks at different segments
for i = m:n/2
b = allcomb(cellprepend,i,n-i);
a = [a cellfun( cellprepend, b, num2cell( stretchmat( i, b ) ) )];
end
end
So the idea is simple, for solutions that add to 8 is exhaustive. If you look for only valid answers, you can do a depth first search by breaking up the problem into 2 blocks. This can be written recursively as I did above and is kinda similar to Merge Sort. The allcomb call takes the block size (n) and finds all the ways of breaking it up into smaller pieces.
We want non-zero pieces so we loop it from 1:n-1. It then prepends the first block to all the combinations of the second block. By only doing all comb on one of the blocks, we can ensure that all solutions are unique.
As for the sorting, I'm not quite sure what you mean by ascending. From what I see, you appear to be sorting from the last number in ascending order. Can you confirm? Any sort can be appended to the end of script.m.
EDIT 2/3 Notes
For the permutatively unique case, the code can be found here
Thanks to #Simon for helping me QA the code multiple times
EDIT: Look at my second more efficient answer!
The Naive approach! Where the cartprod.m function can be found here.
% Create all permutations
p(1:8) = {0:8};
M = fliplr( cartprod( p{:} ) );
% Check sums
r = sum( M, 2 ) == 8;
M = M(sum( M, 2 ) == 8,:); % Solution here
There are definitely more efficient solutions than this but if you just need a quick and dirty solution for small permutations, this will work. Please note that this made Matlab take 3.5 GB of RAM to temporarily store the permutations.
First save all combinations with repetitions in a cell array. In order to do that, just use nmultichoosek.
v = 1 : 8;
combs = cell(length(v),0);
for i = v
combs{i} = nmultichoosek(v,i);
end
In this way, each element of combs contains a matrix where each row is a combination. For instance, the i-th row of combs{4} is a combination of four numbers.
Now you need to check the sum. In order to do that to all the combinations, use cellfun
sums = cellfun(#(x)sum(x,2),combs,'UniformOutput',false);
sums contains the vectors with the sum of all combinations. For
instance, sums{4} has the sum of the number in combination combs{4}.
The next step is check for the fixed sum.
fixed_sum = 10;
indices = cellfun(#(x)x==fixed_sum,sums,'UniformOutput',false);
indices contains arrays of logical values, telling if the combination satisfies the fixed sum. For instance, indices{4}(1) tells you if the first combination with 4 numbers sums to fixed_sum.
Finally, retrieve all valid combinations in a new cell array, sorting them at the same time.
valid_combs = cell(length(v),0);
for i = v
idx = indices{i};
c = combs{i};
valid_combs{i} = sortrows(c(idx,:));
end
valid_combs is a cell similar to combs, but with only combinations that sum up to your desired value, and sorted by the number of numbers used: valid_combs{1} has all valid combinations with 1 number, valid_combs{2} with 2 numbers, and so on. Also, thanks to sortrows, combinations with the same amount of numbers are also sorted. For instance, if fixed_sum = 10 then valid_combs{8} is
1 1 1 1 1 1 1 3
1 1 1 1 1 1 2 2
This code is quite efficient, on my very old laptop I am able to run it in 0.016947 seconds.
I have a m x n matrix 'A' with a m x 1 vector of Weights corresponding to A. I have used 'unique' to find the unique matrix and to find IA and IC. How I can sum up the weights of equal rows in A in a faster way than using two 'for loops'? So far, I have
[Dis_Good_path,IA,IC]=unique(Good_path,'rows','stable');
for i=1:length(IA) % Summing up the weights corresponding to equal paths
Dis_R2(i)=0;
for j=1:length(IC)
if IA(i)==IC(j)
Dis_R2(i)= Dis_R2(i)+R2(j);
end
end
end
An answer with one loop less would look like this :)
[Dis_Good_path,ia,ic]=unique(Good_path,'rows','stable');
Dis_R2 = R2(ia);
rIA = setxor(ia,1:size(Good_path,1));
rIC = ic(rIA);
for i = 1:numel(rIC)
Dis_R2(rIC(i)) = Dis_R2(rIC(i)) + R2(rIA(i));
end