How to do a ~= vector operation in matlab - matlab

I'm trying to write my own program to sort vectors in matlab. I know you can use the sort(A) on a vector, but I'm trying to code this on my own. My goal is to also sort it in the fewest amount of swaps which is kept track of by the ctr variable. I find and sort the min and max elements first, and then have a loop that looks at the ii minimum vector value and swaps it accordingly.
This is where I start to run into problems, I'm trying to remove all the ii minimum values from my starting vector but I'm not sure how to use the ~= on a vector. Is there a way do this this with a loop? Thanks!
clc;
a = [8 9 13 3 2 8 74 3 1] %random vector, will be function a once I get this to work
[one, len] = size(a);
[mx, posmx] = max(a);
ctr = 0; %counter set to zero to start
%setting min and max at first and last elements
if a(1,len) ~= mx
b = mx;
c = a(1,len);
a(1,len) = b;
a(1,posmx) = c;
ctr = ctr + 1;
end
[mn, posmn] = min(a);
if a(1,1) ~= mn
b = mn;
c = a(1,1);
a(1,1) = b;
a(1,posmn) = c;
ctr = ctr + 1;
end
ii = 2; %starting at 2 since first element already sorted
mini = [mn];
posmini = [];
while a(1,ii) < mx
[mini(ii), posmini(ii - 1)] = min(a(a~=mini))
if a(1,ii) ~= mini(ii)
b = mini(ii)
c = a(1,ii)
a(1,ii) = b
a(1,ii) = c
ctr = ctr + 1;
end
ii = ii + 1;
end

Related

How can I do vectorization for this matlab "for loop"?

I have some matlab code as follow, constructing KNN similarity weight matrix.
[D,I] = pdist2(X, X, 'squaredeuclidean', 'Smallest', k+1);
D = D < threshold;
W = zeros(n, n);
for i=1:size(I,2)
W(I(:,i), i) = D(:,i);
W(i, I(:,i)) = D(:,i)';
end
I want to vectorize the for loop. I have tried
W(I) = D;
but failed to get the correct value.
I add test case here:
n = 5;
D = [
1 1 1 1 1
0 1 1 1 1
0 0 0 0 0
];
I = [
1 2 3 4 5
5 4 5 2 3
3 1 1 1 1
];
There are some undefined variables that makes it hard to check what it is doing, but this should do the same as your for loop:
D,I] = pdist2(X, X, 'squaredeuclidean', 'Smallest', k+1);
D = D < threshold;
W = zeros(n);
% set the diagonal values
W(sub2ind(size(X), I(1, :), I(1, :))) = D(1,:);
% set the other values
W(sub2ind(size(W), I(2, :), 1:size(I, 2))) = D(2, :);
W(sub2ind(size(W), 1:size(I, 2), I(2, :))) = D(2, :).';
I splited the directions, it works now with your test case.
A possible solution:
idx1 = reshape(1:n*n,n,n).';
idx2 = bsxfun(#plus,I,0:n:n*size(I,2)-1);
W=zeros(n,n);
W(idx2) = D;
W(idx1(idx2)) = D;
Here assumed that you repeatedly want to compute D and I so compute idx only one time and use it repeatedly.
n = 5;
idx1 = reshape(1:n*n,n,n).';
%for k = 1 : 1000
%[D,I] = pdist2(X, X, 'squaredeuclidean', 'Smallest', k+1);
%D = D < threshold;
idx2 = bsxfun(#plus,I,0:n:n*size(I,2)-1);
W=zeros(n,n);
W(idx2) = D;
W(idx1(idx2)) = D;
%end
But if n isn't constant and it varies in each iteration it is better to change the way idx1 is computed:
n = 5;
%for k = 1 : 1000
%n = randi([2 10]);%n isn't constant
%[D,I] = pdist2(X, X, 'squaredeuclidean', 'Smallest', k+1);
%D = D < threshold;
idx1 = bsxfun(#plus,(0:n:n^2-1).',1:size(I,2));
idx2 = bsxfun(#plus,I,0:n:n*size(I,2)-1);
W=zeros(n,n);
W(idx2) = D;
W(idx1(idx2)) = D;
%end
You can cut some corners with linear indices but if your matrices are big then you should only take the nonzero components of D. Following copies all values of D
W = zeros(n);
W(reshape(sub2ind([n,n],I,[1;1;1]*[1:n]),1,[])) = reshape(D,1,[]);

Vectorization in Matlab to speed up expensive loop

How can I speed up the following MATLAB code, using vectorization? Right now the single line in the loop is taking hours to run for the case upper = 1e7.
Here is the commented code with sample output:
p = 8;
lower = 1;
upper = 1e1;
n = setdiff(lower:upper,primes(upper)); % contains composite numbers between lower + upper
x = ones(length(n),p); % Preallocated 2-D array of ones
% This loop stores the unique prime factors of each composite
% number from 1 to n, in each row of x. Since the rows will have
% varying lengths, the rows are padded with ones at the end.
for i = 1:length(n)
x(i,:) = [unique(factor(n(i))) ones(1,p-length(unique(factor(n(i)))))];
end
output:
x =
1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 1
2 3 1 1 1 1 1 1
2 1 1 1 1 1 1 1
3 1 1 1 1 1 1 1
2 5 1 1 1 1 1 1
For example, the last row contains the prime factors of 10, if we ignore the ones. I have made the matrix 8 columns wide to account for the many prime factors of numbers up to 10 million.
Thanks for any help!
This is not vectorization, but this version of the loop will save about half of the time:
for k = 1:numel(n)
tmp = unique(factor(n(k)));
x(k,1:numel(tmp)) = tmp;
end
Here is a quick benchmark for this:
function t = getPrimeTime
lower = 1;
upper = 2.^(1:8);
t = zeros(numel(upper),2);
for k = 1:numel(upper)
n = setdiff(lower:upper(k),primes(upper(k))); % contains composite numbers between lower to upper
t(k,1) = timeit(#() getPrime1(n));
t(k,2) = timeit(#() getPrime2(n));
disp(k)
end
p = plot(log2(upper),log10(t));
p(1).Marker = 'o';
p(2).Marker = '*';
xlabel('log_2(range of numbers)')
ylabel('log(time (sec))')
legend({'getPrime1','getPrime2'})
end
function x = getPrime1(n) % the originel function
p = 8;
x = ones(length(n),p); % Preallocated 2-D array of ones
for k = 1:length(n)
x(k,:) = [unique(factor(n(k))) ones(1,p-length(unique(factor(n(k)))))];
end
end
function x = getPrime2(n)
p = 8;
x = ones(numel(n),p); % Preallocated 2-D array of ones
for k = 1:numel(n)
tmp = unique(factor(n(k)));
x(k,1:numel(tmp)) = tmp;
end
end
Here's another approach:
p = 8;
lower = 1;
upper = 1e1;
p = 8;
q = primes(upper);
n = setdiff(lower:upper, q);
x = bsxfun(#times, q, ~bsxfun(#mod, n(:), q));
x(~x) = inf;
x = sort(x,2);
x(isinf(x)) = 1;
x = [x ones(size(x,1), p-size(x,2))];
This seems to be faster than the other two options (but is uses more memory). Borrowing EBH's benchmarking code:
function t = getPrimeTime
lower = 1;
upper = 2.^(1:12);
t = zeros(numel(upper),3);
for k = 1:numel(upper)
n = setdiff(lower:upper(k),primes(upper(k)));
t(k,1) = timeit(#() getPrime1(n));
t(k,2) = timeit(#() getPrime2(n));
t(k,3) = timeit(#() getPrime3(n));
disp(k)
end
p = plot(log2(upper),log10(t));
p(1).Marker = 'o';
p(2).Marker = '*';
p(3).Marker = '^';
xlabel('log_2(range of numbers)')
ylabel('log(time (sec))')
legend({'getPrime1','getPrime2','getPrime3'})
grid on
end
function x = getPrime1(n) % the originel function
p = 8;
x = ones(length(n),p); % Preallocated 2-D array of ones
for k = 1:length(n)
x(k,:) = [unique(factor(n(k))) ones(1,p-length(unique(factor(n(k)))))];
end
end
function x = getPrime2(n)
p = 8;
x = ones(numel(n),p); % Preallocated 2-D array of ones
for k = 1:numel(n)
tmp = unique(factor(n(k)));
x(k,1:numel(tmp)) = tmp;
end
end
function x = getPrime3(n) % Approach in this answer
p = 8;
q = primes(max(n));
x = bsxfun(#times, q, ~bsxfun(#mod, n(:), q));
x(~x) = inf;
x = sort(x,2);
x(isinf(x)) = 1;
x = [x ones(size(x,1), p-size(x,2))];
end

Have to convert Integer to binary

I'm writing a user-defined function to convert integers to binary. The largest number that could be converted with the function should be a binary number with
16 1 s. If a larger number is entered as d, the function should display an error
message. With my code, I'm trying to add the numbers 0 or 1 to my vector x based on the remainder, then I want to reverse my final vector to display a number in binary. Here's what I have:
function [b] = bina(d)
% Bina is a function that converts integers to binary
x = [];
y = 2;
in = d/2;
if d >=(2^16 -1)
fprintf('This number is too big')
else
while in > 1
if in >= 1
r = rem(in,y);
x = [x r]
end
end
end
end
As you insist on a loop:
x = [];
y = 2;
in = d;
if d >=(2^16 -1)
fprintf('This number is too big')
else
ii = 1;
while in > 0
r = logical(rem(in,y^ii));
x = [r x];
in = in - r*2^(ii-1);
ii = ii+1;
end
end
b = x;
You had the right ideas, but you need to update the variables in your while-loop with every iteration. This is mainly in, where you need to subtract the remainder. And just store the binary remainders in your variable x.
You can check your result with
x = double( dec2bin(d, 16) ) - 48
You could also use a for loop, by pre-calculating the number of iterations with
find( d < 2.^(1:16),1)
and then
if d >=(2^16 -1)
fprintf('This number is too big')
else
for ii = 1:find( d < 2.^(1:16),1)
r = logical(rem(in,y^ii));
x = [r x];
in = in - r*2^(ii-1)
end
end

Multiplying a vector by random numbers while keeping the sum the same (MATLAB)

I'm trying to multiply (element wise) a vector V of length N by a randomly generated number in the range (a,b), while keeping the sum of the vector equal to a total amount, E. I want to do this in MATLAB, but I'm not sure how. Getting random numbers between a certain range I know how to do:
minrand = 0;
maxrand = 1;
randfac = (maxrand-minrand).*rand(1,N) + minrand;
But yeah, beyond that I'm pretty clueless. I guess the random numbers can't really be generated like this, because if we call the random numbers the vector R, then I want that
R_1*V1 + R_2*V2 .... + R_N*V_N = E. So I guess it's a big equation. Is there any way to solve it, while putting constraints on the max and min values of R?
You can pick pairs of two elements (in all combinations) and add and subtract an equal random number.
% Make up a random vector
N=10;
randfac = 10*rand(1,N);
%OP Answer here: Given randfac with sum E re-randomize it
E = sum(randfac);
minrand = 0;
maxrand = 2;
disp(randfac)
% v = [6.4685 2.9652 6.6567 1.6153 7.3581 0.0237 7.1025
% 3.2381 1.9176 1.3561]
disp(sum(randfac))
% E = 38.7019
r = minrand + (maxrand-minrand)*rand(N*N,1);
k = 1;
for i=1:N
for j=1:N
randfac(i) = randfac(i)-r(k);
randfac(j) = randfac(j)+r(k);
k = k + 1;
end
end
disp(randfac)
% v = [5.4905 0.7051 4.7646 1.3479 9.3722 -1.4222 7.9275
% 7.5777 1.7549 1.1836]
disp(sum(randfac))
% E = 38.7019
Just divide the vector with the sum and multiply with the target E.
randfac = (maxrand-minrand).*rand(1,N) + minrand;
randfac = E*randfac/sum(randfac);
as long as the operator is linear, the result is going to retain it's randomness. Below is some sample code:
minrand = 0;
maxrand = 1;
N = 1000; %size
v = (maxrand-minrand).*rand(1,N) + minrand;
E = 100; %Target sum
A = sum(v);
randfac = (E/A)*v;
disp(sum(randfac))
% 100.0000
First of all with random numbers in the interval of [a b] you can't guarantee that you will have the same summation (same E). For example if [a b]=[1 2] of course the E will increase.
Here is an idea, I don't know how random is this!
For even N I randomize V then divide it in two rows and multiply one of them with random numbers in [a b] but the second column will be multiplied to a vector to hold the summation fixed.
N = 10;
V = randi(100,[1 N]);
E = sum(V);
idx = randperm(N);
Vr = V(idx);
[~,ridx] = sort(idx);
Vr = reshape(Vr,[2 N/2]);
a = 1;
b = 3;
r1 = (b - a).*rand(1,N/2) + a;
r2 = (sum(Vr) - r1.*Vr(1,:))./Vr(2,:);
r = reshape([r1;r2],1,[]);
r = r(ridx);
Enew = sum(V.*r);
The example results are,
V = [12 82 25 51 81 51 31 87 6 74];
r = [2.8018 0.7363 1.9281 0.5451 1.9387 -0.4909 1.3076 0.8904 2.9236 0.8440];
with E = 500 as well as Enew.
I'm simply assigning one random number to a pair (It can be considered as half random!).
Okay, I have found a way to somewhat do this, but it is not elegant and there are probably better solutions. Starting with an initial vector e, for which sum(e) = E, I can randomize its values and end up with an e for which sum(e) is in the range [(1-threshold)E,(1+thresholdE)]. It is computationally expensive, and not pretty.
The idea is to first multiply e by random numbers in a certain range. Then, I will check what the sum is. If it is too big, I will decrease the value of the random numbers smaller than half of the range until the sum is no longer too big. If it is too small, I do the converse, and iterate until the sum is within the desired range.
e = somepredefinedvector
minrand = 0;
maxrand = 2;
randfac = (maxrand-minrand).*rand(1,N) + minrand;
e = randfac.*e;
threshold = 0.001;
while sum(e) < (1-threshold)*E || sum(e) > (1+threshold)*E
if sum(e) > (1+threshold)*E
for j = 1:N
if randfac(j) > (maxrand-minrand)/2
e(j) = e(j)/randfac(j);
randfac(j) = ((maxrand-minrand)/2-minrand).*rand(1,1) + minrand;
e(j) = randfac(j)*e(j);
end
if sum(e) > (1-threshold)*E && sum(e) < (1+threshold)*E
break
end
end
elseif sum(e) < (1-threshold)*E
for j = 1:N
if randfac(j) < (maxrand-minrand)/2
e(j) = e(j)/randfac(j);
randfac(j) = (maxrand-(maxrand-minrand)/2).*rand(1,1) + (maxrand-minrand)/2;
e(j) = randfac(j)*e(j);
end
if sum(e) > (1-threshold)*E && sum(e) < (1+threshold)*E
break
end
end
end
end

For loop output as an array

I wrote a code in Matlab which I predefine the variable "a" and then set up a for loop of 5 iterations where the variable "a" goes through some basic operations. However, the for loop output only saves the fifth iteration of "a." How do I save all 5 iterations in a 1x5 array?
The code is as follows:
a = 10;
k = 0.5;
n = 2;
for m = 1:5
a = a + (a*k) + n;
end
Edit:
I just found it that I have to create a new variable.
a = 10;
k = 0.5;
n = 2;
a_n = zeros(1,5);
for m = 1:5
a = a + (a*k) + n;
a_n(m) = a;
end
You may need to store value of a after each iteration into an another variable x
a = 10;
k = 0.5;
n = 2;
for m = 1:5
a = a + (a*k) + n;
x(m) = a;
end
x
Output:
x =
17.000 27.500 43.250 66.875 102.312
You would need to use a different variable to store the 5 iterations as an array.
Code would look something like this:
a = 10;
k = 0.5;
n = 2;
b = [];
for m = 1:5
a = (a + (a*k) + n)
b = [b a];
end
You can now print b for all 5 iteration values.
Here is an alternate way to update values into the 1-D matrix.