Minizinc search 2d array with constraints - minizinc

How could I select the smallest number from each row of a 2d array while making sure the same column can at most be selected twice
(in the following case, for row 1, column 5 is chosen; for row 2, column 5 is chosen while for row 3, column 5 can no longer be selected, so column 2 is selected as the min):
(Besides, in java, it is often that ArrayList is used to add and remove elements but how is it possible to do this in Minizinc using constraints)?
int: m = 3;
int: n = 5;
array[1..m,1..n] of int: diffs = [|14,18,24,30,13
|10,12,18,24,7
| 8,7,12,18,6|]

Below is one approach which minimize the sum of the difference between the values of the selected columns and the "real" smallest values of each row.
include "globals.mzn";
int: m = 3;
int: n = 5;
array[1..m,1..n] of int: diffs = [|14,18,24,30,13
|10,12,18,24,7
| 8,7,12,18,6|];
% decision variables
array[1..m] of var 1..n: x; % which row to select
var int: z; % difference between the selected and smallest values
solve minimize z;
% solve satisfy;
% constraint 1: at_most 2 of the same column can be selected
constraint
% at most two rows can have the same column
forall(j in 1..n) (
at_most(2,x,j)
)
;
% constraint 2: calculate the least difference
constraint
% get smallest difference to the smallest value
z = sum(i in 1..m) (
% value of selected column - the smallest value of the row
diffs[i,x[i]]-min([diffs[i,j] | j in 1..n])
)
% /\ % for solve satisfy
% z = 1
;
output [
"z: \(z)\n",
"x: \(x) values:\([diffs[i,x[i]] | i in 1..m])\n"
];
For this problem instance there are two optimal solutions with z=1, i.e. the solution is 1 larger than the "real" optimal value (which would have been without the max 2 column constraint).
z: 1
x: [5, 5, 2] values:[13, 7, 7]
----------
z: 1
x: [1, 5, 5] values:[14, 7, 6]
The first solution mean that we pick the values from column 5 for the 2 first rows (i.e. the values of 13 and 7), and for the third row we pick the value from column 2 (i.e. 7). This happens to be the solution mentioned in the example.
There is an alternative approach where constraint 2 is replaced with the following constraint, i.e. which sums the selected values directly (and not the difference against minimum value of each row):
% constraint 2: calculate the least difference
constraint
z = sum([diffs[i,x[i]] | i in 1..m])
% /\ % for solve satisfy
% z = 27
;
It has - of course - the same solution of columns. The difference is only in the value of "z":
z: 27
x: [5, 5, 2] values:[13, 7, 7]
---------
z: 27
x: [1, 5, 5] values:[14, 7, 6]
Arguably this later variant is neater, but if the values in the "diffs" matrix are large then the first variant should probably be used since solvers tend to be happier to work with smaller values. (For matrices with large values it's recommended to use a restricted domain of "z" instead of "var int", but I'm a little lazy tonight. :-)

Related

How to find the "i"th most frequent element in a vector without using loops in MATLAB?

For the first most frequent element, we use mode. To find the ith most frequent element, I don't know of any other better way than to keep deleting the first most frequent element, second most frequent element, ..., up to the i-1th most frequent element from the dataset:
for n=1:size(data,1)
if n~=i
data = data( data(:,1) ~= mode(data(:,1)), :);
else
item = data( data(:,1) == i, :);
end
end
Can there be a better and faster way to do this using vectorization instead of looping?
You can utilize accumarray and sort with unique to generate the bin counts for the unique values in your data array.
For example:
function [val, count] = getnthmost(x, n)
% Get unique values in x (uniquevals) and their location (ic)
[uniquevals, ~, ic] = unique(x);
% Accumulate the indices and sort in descending order
[bincounts_sorted, idx] = sort(accumarray(ic, 1), 'descend');
% Get the nthmost value and its count
val = uniquevals(idx(n));
count = bincounts_sorted(n);
end
And a small example:
x = randi(5, 10, 1);
[val, count] = getnthmost(x, 2);
Which returns:
x =
2 5 1 2 2 1 3 4 3 4
val =
1
count =
2
Note that sort handles 'ties' in the order they appear in the array being sorted regardless of sort direction, so if we have [1, 2, 3, 2] our ascending sort indices will be [1, 2, 4, 3] and the descending sort indices will be [3, 2, 4, 1].
Walkthrough
We use unique here to find all of the unique values in our input array, x. We also store the optional third output, which is a mapping of the values of x to their index in the array of unique values. We can then use accumarray to accumulate the elements of 1 using the subscripts we got from unique. In other words, we're getting a count of each index. We sort this count in descending order and store the output indices so we can map the count back to the value in our array of unique values. We can then use n to pick and return the appropriate value and count.
Suppose we have a dataset contains this values:
data = [5 5 4 2 5 8 8 5 8 4 ];
In order to find most frequent item as you noted mode is the best method.
but to find ith most frequent item we study histogram of the data that shows how many each element repeated.
in Matlab the hist function is for computing the histogram. first argument of hist is the data and second argument is unique values of elements so in this example they are [2 4 5 8]
unqiue values of elements
unique_val = unique(data);
2 4 5 8
histogram computed
[count val] = hist(data, unique_val);
to plot the histogram you can use hist this way:
hist(data, unique_val);
so we have such a figure:
_
_ _
_ _ _
_ _ _ _
2 4 5 8
Visually we find that 5 is first most frequent item and 8 is second most frequent item....
But to numerically find the item we can sort the histogram in descending order to get such a figure:
_
_ _
_ _ _
_ _ _ _
5 8 4 2
so 5 is the first 8 is the second ....
In Matlab we concatenate count and val as freq
freq = [count; val].';
then sort freq based on the first column,count. (the minus sign is for descending sort and 1 is for the first column):
out = sortrows(freq , -1)
then out(i,2) is the ith most frequent item.
in short all of what explained leads to this:
%find count of data
[count val] = hist(data(:,1),unique(data(:,1)));
freq = [count; val].';
%sort counts descendingly
out = sortrows(freq,-1);
now out(i,2) is ith most frequent element
Here's a way:
x = [2 2 3 1 5 1 1 3 3 3 4 1 2 5 4 3 1 3 3 4]; % data
ii = 2; % find tthe second most frequent value
sx = sort(x); % sort x
ind = [true diff(sx)~=0]; % logical index of each new value in the sorted vector
counts = diff(find([ind true])); % count of each unique value
vals = sx(ind); % unique values
[~, is] = sort(counts, 'descend'); % counts in decreasing order
result = vals(is(ii)); % value with the ii-th largest count
In this example,
>> vals
vals =
1 2 3 4 5
>> counts
counts =
5 3 7 3 2
>> result
result =
1

Add and remove element of vector at specific position

Let X = [1, 2, 3, 4, 5] and Y = [1, 2, 1, 0, 1] be vectors where X maps into Y.
Now I want to identify the maximum and minimum of Y, which is easy: [value_min, id_min] = min(Y) = [0, 4] and [value_max, id_max] = max(Y) = [2, 2].
Then I want to remove the element from X corresponding to the minimum in Y and expand evenly around the element in X corresponding to the maximum in Y, while keeping the number of points equal. For this example we remove X(4)=[]. Then we expand like X(2)=(X(2) - X(1))/2 and X(3)=(X(3) - X(2))/2 such that X looks like X = [1, 1.5, 2.5, 3, 5]. How can I achieve this? I think there is a general pattwern.
Solution
Now the following snipped should work for any vector of length N. Note that the first and final element are fixed.
[value_max, id_max] = max(Y(2:N-1));
X(id_max) = (X(id_max) - X(id_max-1))/2;
X(id_max+1) = (X(id_max+1) - X(id_max))/2;
[value_min, id_min] = min(Y(2:N-1));
X(id_min)=[];
Here is a solution to your problem but there are a few things you should take care of
% Any Vector should work
X=[1 2 3 4 5];
Y=[1 2 1 0 1];
%We dont need the actual min max
[~,MIN]=min(Y(2:end-1));
[~,MAX]=max(Y(2:end-1));
%you dont look at the first element so the index has to be increased by 1
MIN=MIN+1;
MAX=MAX+1;
X(MIN)=[];%taking out the smallest element
Xnew= [X(1:MAX) X(MAX:end)]; %Extend the vector by taking the MAX value twice
%the mean for 2 elements is A+B/2
Xnew(MAX)=mean(Xnew(MAX-1:MAX)); %the left one and the element next to it
Xnew(MAX+1)=mean(Xnew(MAX+1:MAX+2)); %the right one and the element next ot it
%rewrite X and clear Xnew
X=Xnew;
clear Xnew;
First of all this isnt very efficient, but if its just used to
modify some vectors and not get called a million times a day it will
do the trick.
In your text you say remove the minima then stretch
around the maxima, in your solution metacode it is the other way
around. this will influence the outcome when min and max are next to
each other, so please check which way you prefer.
Y isnt changed in this at all so it cant be performed multiple times on the same vector.
Is N (the length) of any importance later on? if not you can always just refer to "end"

vector of indices

I have a 10x10 matrix called A:
I have vector of column numbers:
C = [ 2, 6, 8 ];
I have a vector of row numbers:
R = [1; 3; 7];
The column numbers correspond to each row. i.e. For column 1 we are looking at row numbers given by R, for column 3 we are looking at row numbers given by R and so on.
I want to replace those exact locations in A with some other number 13.
i.e. for each of these locations in matrix A:
(1,2) (1,6) (1,8), (3,2), (3, 6), (3,8) I want to insert 13.
How do I achieve the above ?
you can do A(R,C) = 13 .......
As dlavila pointed out, you can do A(R,C) = 13 wich would be the best and easiest. Nevertheless I have written a longer code involving the eval function that you might find useful in the future:
for ii=1:length(C)
for jj =1:length(R)
eval(strcat('A(', num2str(C(ii)), ',',num2str(R(jj)),')=13;'))
end
end
Both give the same results.

Get even/odd indices of a matrix - MATLAB

I have following problem:
I have a given matrix of let's say 4x4.
How can I get the indices of the following combinations:
row odd and column odd
row odd and column even
row even and column odd
row even and column even
For example if I have the matrix:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
'row odd and column odd' would be the indices of 1, 3, 9, 11...
'row odd and column even' would be the indices of 2, 4, 10, 12...
'row even and column odd' would be the indices of 5, 7, 13, 15...
and 'row even and column even' would be indices of 6, 8, 14, 16...
Also, is it possible to combine those operations so e.g. I get the indices for 'row odd and column odd' and 'row even and column even'?
Thank you!
It's pretty easy to do with indexing:
Odd rows and odd columns
B = A(1:2:end, 1:2:end);
Odd rows and even columns
B = A(1:2:end, 2:2:end);
Even rows and odd columns
B = A(2:2:end, 1:2:end);
Even rows and even columns
B = A(2:2:end, 2:2:end);
The above assumes that you want the actual matrix values themselves. It's a bit confusing as your matrix elements are the same as linear indexing values themselves. If you want to determine the actual column major indices to access the matrix, you can generate a vector from 1 to N where N is the total number of elements in your matrix, then reshape this matrix into the desired size that you want. After, use the same logic above to get the actual linear indices:
N = numel(A);
B = reshape(1:N, size(A,1), size(A,2));
ind = B(1:2:end, 1:2:end); %// For odd rows, odd columns
%// Repeat for the other ones...
Now, given your comment, you want to create a new matrix that will store only these extracted matrix values while making all of the other elements zero. If you want to do this, simply pre-allocate a matrix of zeroes, then copy over those values to extract using the computed indices into the new matrix. In other words:
N = numel(A);
B = reshape(1:N, size(A,1), size(A,2));
ind = B(1:2:end, 1:2:end); %// For odd rows, odd columns - Change to suit your tastes
out = zeros(size(A));
out(ind(:)) = A(ind(:));
If you want to combine the indices like having odd row - odd column, and even row - even column, just compute two sets of indices, concatenate them into a single vector and do the same syntax like before. Therefore:
N = numel(A);
B = reshape(1:N, size(A,1), size(A,2));
ind = B(1:2:end, 1:2:end); %// For odd rows, odd columns
ind2 = B(2:2:end, 2:2:end); %// For even rows, even columns
ind = [ind(:); ind2(:)];
out = zeros(size(A));
out(ind) = A(ind);
Code
N = size(A,1); %// Get size of input matrix A
case1_ind = bsxfun(#plus,[1:2:N]',(0:N/2-1)*2*N)
case2_ind = case1_ind + N
case3_ind = case1_ind + 1
case4_ind = case3_ind + N
Note: These outputs are indices. So, to get the actual outputs, use these as indices.
To combine indices for case 1 and case 4, just concatenate -
case14comb_ind = [case1_ind ; case4_ind]
Edit :
%// To copy onto some other matrix of the same size as A, do this for case 1
new_matrix = zeros(size(A))
new_matrix(case1_ind(:)) = A(case1_ind(:))
Repeat this for the other cases too.

How to find all index pairs of unequal elements in vector (Matlab)

Lets say I have the following vector in Matlab:
V = [4, 5, 5, 7];
How can I list (in a n-by-2 matrix for example) all the index pairs corresponding to unequal elements in the vector. For example for this particular vector the index pairs would be:
index pairs (1, 2) and (1, 3) corresponding to element pair (4,5)
index pair (1, 4) corresponding to element pair (4,7)
index pairs (2, 4) and (3, 4) corresponding to element pair (5,7)
The reason I need this is because I have a cost-function which takes a vector such as V as input and produces a cost-value.
I want to see how does the random swapping of two differing elements in the vector affect the cost value (using this for steepest descent hill climbing).
The order of the index pairs doesn't also matter. For my purposes (1,2) is the same as (2,1).
For example if my cost-function was evalCost(), then I could have V = [4, 5, 5, 7] and
evalCost(V) = 14
whereas for W = [4, 7, 5, 5] the cost could be:
evalCost(W) = 10
How to get the list of "swapping" pair indexes in Matlab. Hope my question is clear =)
I don't understand the cost function part, but the first part is simple:
[a,b]=unique(V)
C = combnk(b,2)
C contains the indices, and V(C) the values:
C = combnk(b,2)
C =
1 2
1 4
2 4
V(C)
ans =
4 5
4 7
5 7
Use bsxfun and then the two-ouput version of find to get the pairs. triu is applied to the output of bsxfun to consider only one of the two possible orders.
[ii jj] = find(triu(bsxfun(#ne, V, V.')));
pairs = [ii jj];