Selecting elements closest to zero - arrays

Let's say I have a vector A = [-3 -2 -1 1 2].
I want to pick out the positive element closest to zero and the negative element closest to zero while retaining their index value in the vector A.
So I want another vector with [-1, 4; 1,5] (i.e. [-ve to 0, index ; +ve to 0, index]).
I've tried creating separate arrays with the positive and negative components and selecting the min/max of these, but this loses the index in A. E.g.
Ap = A(A>0)
An = A(A<0)
[ap,idxp]=sort(Ap)
[an,idxn]=sort(An)
I'm sure there must be a simple way to do this. Any help greatly appreciated.

Invert the vector
B = 1 ./ A;
and find min
[~, idxn] = min(B);
an = A(idxn);
and max
[~, idxp] = max(B);
ap = A(idxp);

You can keep the index with
Ap = A;
Ap(Ap < 0) = NaN;
[ap,idxp] = min(Ap);
An = A;
An(An > 0) = NaN;
[an,idxn] = max(An);

You can sort the absolute values, then pick out the indices and values of the first value where A is greater than or less than zero:
A = [-3 -2 -1 1 2];
[sA,idx] = sort(abs(A)); % Sort absolute values to get closest to 0 order
idxP = idx( find( A(idx) > 0, 1 ) ); % First index where sorted A is positive
idxN = idx( find( A(idx) < 0, 1 ) ); % First index where sorted A is negative
out = [A(idxP), idxP; A(idxN), idxN]; % Formatted output as requested: [1, 4; -1, 3] for example

Related

Algorithm: Given an array, find the maximum sum after rearrangement

You are given an array A, of size N, containing numbers from 0-N. For each sub-array starting from 0th index, lets say Si, we say Bi is the smallest non negative number that is not present in Si.
We need to find the maximum possible sum of all Bi of this array.
We can rearrange the array to obtain the maximum sum.
For example:
A = 1, 2, 0 , N = 3
then lets say we rearranged it as A= 0, 1, 2
S1 = 0, B1= 1
S2 = 0,1 B2= 2
S3 = 0,1,2 B3= 3
Hence the sum is 6
Whatever examples I have tried, I have seen that sorted array will give the maximum sum. Am I correct or missing something here.
Please help to find the correct logic for this problem. I am not looking for optimal solution but just the correct logic.
Yes, sorting the array maximizes the sum of 𝐵𝑖
As the input size is 𝑛, it does not include every number in the range {0, ..., 𝑛}, as that is a set of 𝑛 + 1 numbers. Let's say it only lacks value 𝑘, then 𝐵𝑖 is 𝑘 for all 𝑖 >= 𝑘. If there are other numbers that are missing, but greater than 𝑘, there is no impact on any 𝐵𝑖.
Thus we need to find out the minimum missing value 𝑘 in the range {0, ..., 𝑛}. And then the maximised sum is 1 + 2 + ... + 𝑘 + (𝑛−𝑘)𝑘. This is 𝑘(𝑘+1)/2 + (𝑛−𝑘)𝑘 = 𝑘(1 + 2𝑛 − 𝑘)/2
To find the value of 𝑘, create a boolean array of size 𝑛 + 1, and set the entry at index 𝑣 to true when 𝑣 is encountered in the input. 𝑘 is then the first index at which that boolean array still has a false value.
Here is a little implementation in a JavaScript snippet:
function maxSum(arr) {
const n = arr.length;
const isUsed = Array(n + 1).fill(false);
for (const value of arr) {
isUsed[value] = true;
}
const k = isUsed.indexOf(false);
return k * (1 + 2*n - k) / 2;
}
console.log(maxSum([0, 1, 2])); // 6
console.log(maxSum([0, 2, 2])); // 3
console.log(maxSum([1, 0, 1])); // 5

Generate a matrix of combinations (permutation) without repetition (array exceeds maximum array size preference)

I am trying to generate a matrix, that has all unique combinations of [0 0 1 1], I wrote this code for this:
v1 = [0 0 1 1];
M1 = unique(perms([0 0 1 1]),'rows');
• This isn't ideal, because perms() is seeing each vector element as unique and doing:
4! = 4 * 3 * 2 * 1 = 24 combinations.
• With unique() I tried to delete all the repetitive entries so I end up with the combination matrix M1 →
only [4!/ 2! * (4-2)!] = 6 combinations!
Now, when I try to do something very simple like:
n = 15;
i = 1;
v1 = [zeros(1,n-i) ones(1,i)];
M = unique(perms(vec_1),'rows');
• Instead of getting [15!/ 1! * (15-1)!] = 15 combinations, the perms() function is trying to do
15! = 1.3077e+12 combinations and it's interrupted.
• How would you go about doing in a much better way? Thanks in advance!
You can use nchoosek to return the indicies which should be 1, I think in your heart you knew this must be possible because you were using the definition of nchoosek to determine the expected final number of permutations! So we can use:
idx = nchoosek( 1:N, k );
Where N is the number of elements in your array v1, and k is the number of elements which have the value 1. Then it's simply a case of creating the zeros array and populating the ones.
v1 = [0, 0, 1, 1];
N = numel(v1); % number of elements in array
k = nnz(v1); % number of non-zero elements in array
colidx = nchoosek( 1:N, k ); % column index for ones
rowidx = repmat( 1:size(colidx,1), k, 1 ).'; % row index for ones
M = zeros( size(colidx,1), N ); % create output
M( rowidx(:) + size(M,1) * (colidx(:)-1) ) = 1;
This works for both of your examples without the need for a huge intermediate matrix.
Aside: since you'd have the indicies using this approach, you could instead create a sparse matrix, but whether that's a good idea or not would depend what you're doing after this point.

Summing partitions of an array in matlab

Suppose I have an array of length 15
x = randi([0 5], 1,15);
I want to sum every 3 elements of x together and put each sum in a new array called y, as in the following:
y = [y1 y2 y3 y4 y5];
Please help me in doing that in Matlab using for loops.
Here's a vectorized approach that automatically deals with a possible smaller last chunk:
x = randi([0 5], 1, 15); % example data
N = 3; % chunk size
y = accumarray(ceil((1:numel(x))/N).', x(:));
you can use reshape as follows:
y = sum(reshape(x,3,[]))
This reshapes your vector x to an array 3 by whatever is left, then sum along right dimension...
For the case the # of elements you want to sum doesnt add up to the total length of the vector, you can pad with zeros or NaN at the end to make it work. Here's I chose adding zeros:
x = randi([0 5], 1,15);
n = 4 ; % sum every n elements (which is the number of rows in the reshape)
try
y = sum(reshape(x, n, []));
catch
disp('added trailing zeros!')
x(numel(x) + (n - mod(numel(x), n))) = 0;
y = sum(reshape(x, n, []));
end
(you can do this with an if condition instead, I just like try catch more here)
Using for loops:
y = zeros(1,5);
for i = 1:5
idx = (i-1)*3 + 1:(i-1)*3 + 3;
y(i) = sum(x(idx));
end
Using a reference variable Target that is used to indicate the start position of each partition the loop below can be achieved. If you would only like to use only loops an alternative inner loop can be done. This method works almost on the same premise as windowing.
Method 1: Single For-Loop with Indexing
x = randi([0 5], 1,15);
y = zeros(1,length(x)/3);
Index = 1;
for Target = 1: +3: 15
Partition = x(Target:Target+2);
y(1,Index) = sum(Partition);
Index = Index + 1;
end
Method 2: Outer and Inner For-Loops
x = randi([0 5], 1,15);
y = zeros(1,length(x)/3);
Partition = zeros(1,3);
Index = 1;
for Target = 1: +3: 15
for Column = 1: +1: 3
Partition(1,Column) = x(1,Target+Column-1);
end
y(1,Index) = sum(Partition);
Index = Index + 1;
end

What is the difference between enumerate and range(len(list)) in python?

Can someone tell me why the two code below give me different results? I was under the impression that the enumerate and range(len(list)) both are iterable.
Code: Trying to find the equilibrium index of an array
def equisum (arr):
tsum = sum(arr)
lsum = 0
num = len(arr)
for i in range(num):
tsum -= num
if lsum == tsum:
return i
lsum += num
return -1
arr = [-7, 1, 5, 2, -4, 3, 0]
print (equisum (arr))
Result : -1
def equisum (arr):
tsum = sum(arr)
lsum = 0
for i, num in enumerate(arr):
tsum -= num
if lsum == tsum:
return i
lsum += num
return -1
arr = [-7, 1, 5, 2, -4, 3, 0]
print (equisum (arr))
Result : 3 (the correct answer)
range(num) will give the values 0, 1, 2....
enumerate(arr) will give the values (0, -7), (1, 1), (2, 5)... where the first value is the index and the second value is value of arr at that index.
Your second function should give the correct answer because it subtracts the value of arr[i] rather than just i.
range() is used for getting the sequence of numbers. For example if you give range(10), it will give numbers from 0 to 9.
Here, as you are using range, you are calculating the numbers wrong and finally the default -1 is being returned.
While in enumerate(), it returns both index and actual element from the list.
by the way, if you are using VS Code for programming, make use of the debug feature. It will help to identify such issues much faster.

Vectorizing a code that requires to complement some elements of a binary array

I have a matrix A of dimension m-by-n composed of zeros and ones, and a matrix J of dimension m-by-1 reporting some integers from [1,...,n].
I want to construct a matrix B of dimension m-by-n such that for i = 1,...,m
B(i,j) = A(i,j) for j=1,...,n-1
B(i,n) = abs(A(i,n)-1)
If sum(B(i,:)) is odd then B(i,J(i)) = abs(B(i,J(i))-1)
This code does what I want:
m = 4;
n = 5;
A = [1 1 1 1 1; ...
0 0 1 0 0; ...
1 0 1 0 1; ...
0 1 0 0 1];
J = [1;2;1;4];
B = zeros(m,n);
for i = 1:m
B(i,n) = abs(A(i,n)-1);
for j = 1:n-1
B(i,j) = A(i,j);
end
if mod(sum(B(i,:)),2)~=0
B(i,J(i)) = abs(B(i,J(i))-1);
end
end
Can you suggest more efficient algorithms, that do not use the nested loop?
No for loops are required for your question. It just needs an effective use of the colon operator and logical-indexing as follows:
% First initialize B to all zeros
B = zeros(size(A));
% Assign all but last columns of A to B
B(:, 1:end-1) = A(:, 1:end-1);
% Assign the last column of B based on the last column of A
B(:, end) = abs(A(:, end) - 1);
% Set all cells to required value
% Original code which does not work: B(oddRow, J(oddRow)) = abs(B(oddRow, J(oddRow)) - 1);
% Correct code:
% Find all rows in B with an odd sum
oddRow = find(mod(sum(B, 2), 2) ~= 0);
for ii = 1:numel(oddRow)
B(oddRow(ii), J(oddRow(ii))) = abs(B(oddRow(ii), J(oddRow(ii))) - 1);
end
I guess for the last part it is best to use a for loop.
Edit: See the neat trick by EBH to do the last part without a for loop
Just to add to #ammportal good answer, also the last part can be done without a loop with the use of linear indices. For that, sub2ind is useful. So adopting the last part of the previous answer, this can be done:
% Find all rows in B with an odd sum
oddRow = find(mod(sum(B, 2), 2) ~= 0);
% convert the locations to linear indices
ind = sub2ind(size(B),oddRow,J(oddRow));
B(ind) = abs(B(ind)- 1);

Resources