How can I create a matrix where every combination of numbers between 0 and 1 (step x) is used and every row sums to one?

1 view (last 30 days)
Hello everybody,
I'm looking for a way to create a matrix where every row sums to one and every combination of numbers between zero and one is used given a specified step parameter.
Example: 3 Columns; Step 0.5:
Row 1:[0 0.5 0.5] Row 2:[0.5 0 0.5] Row 3:[0.5 0.5 0]
I'm using the following code (for 6 columns) which is too slow given a small step parameter:
i=1;
for a = 0:step:0.5
for b = 0:step:0.5
for c = 0:step:0.5
for d = 0:step:0.5
for e = 0:step:0.5
for f = 0:step:0.5
weights(i,:)=[a b c d e f];
i = i+1;
end
end
end
end
end
end
clear a b c d e f i
ind = sum(weights,2) == 1;
weights = weights(ind,:);
Thank you for your help!

Accepted Answer

Andrei Bobrov
Andrei Bobrov on 26 Jul 2012
Edited: Andrei Bobrov on 26 Jul 2012
for step stp = .5
stp = .5;
ii = cell(1,6);
[ii{:}] = ndgrid(0:stp:1);
out = cell2mat(cellfun(@(x)x(:),ii(:,end:-1:1),'un',0));
out = out(sum(out,2) == 1,:);
OR
x = 0:stp:1;
out = x(fliplr(fullfact(numel(x)*ones(1,6))));
out = out(sum(out,2) == 1,:);

More Answers (3)

Teja Muppirala
Teja Muppirala on 26 Jul 2012
Edited: Teja Muppirala on 26 Jul 2012
I like andrei's method, but it will will run out of memory quickly when step is small!
Here is a better implementation of the loop method. This runs very quickly and uses very little memory.
i=1;
weights = [];
step = .5/6;
t = 0.5 + 0.0000000001;
for a = 0:step:t
for b = 0:step:t-a
for c = 0:step:t-a-b
for d = 0:step:t-a-b-c
for e = 0:step:t-a-b-c-d
if a+b+c+d+e < t
weights(:,i)=[a; b; c; d; e; 0.5-a-b-c-d-e];
i = i+1;
end
end
end
end
end
end
weights = weights';
Notes:
1. Due to floating point arithmetic, sometimes the sum won't come out to exactly == 0.5, that's why you need some sort of tolerance. (For example, 0.1 + 0.2 ~= 0.3).
2. If you are going to grow matrices without preallocationg, grow them in the last dimension
tic
weights = [];
for n = 1:50000
weights(:,n) = [1; 1; 1; 1; 1; 1];
end
toc %Faster
tic
weights = [];
for n = 1:50000
weights(n,:) = [1 1 1 1 1 1];
end % Slower
toc % Slower

Christian Maschner
Christian Maschner on 26 Jul 2012
That's exactly what I was looking for! Thanks a lot!

Christian Maschner
Christian Maschner on 26 Jul 2012
Edited: Christian Maschner on 26 Jul 2012
It works for steps <0.02! That's very helpful! Thank you Teja!
But the rows do not sum to one.
  1 Comment
Teja Muppirala
Teja Muppirala on 26 Jul 2012
Oops. Yeah, I made them sum to 0.5. To make them sum up to 1, just change two places:
t = 0.5 + 0.0000000001;
to
t = 1.0 + 0.0000000001;
and
0.5-a-b-c-d-e
to
1-a-b-c-d-e

Sign in to comment.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!