Search code examples
minizinc

minizinc: find element in arrray


I have two arrays(type: int) of different length. How could I find the closest number in array b for each number in array a(the following does not work though probably because of syntax error):

int: m;
int: n;
array [1..m] of int: a;
array [1..n] of int: b;
array[1..m] of int: results;
results = [abs(a[i] - b[j])| i in 1..m, j in 1..n];
solve minimize results;
output ["Solution: ", show(results)];

Solution

  • (It always helps with a complete model with as much information as possible, e.g. the values of "m" and "n" and other known/fixed values. Also, mention of the error messages helps in general.)

    There are a couple of unknown things in your model, so I have to guess a little.

    I guess that "results" really should be a single decision variable, and not an array as you defined it. Then you can write

    var int: results = sum([abs(a[i] - b[j])| i in 1..m, j in 1..n]);
    

    or

    var int: results;
    ...
    constraint results = sum([abs(a[i] - b[j])| i in 1..m, j in 1..n]);
    

    Also, as it stand the model is not especially interesting since it just define two constant arrays "a" and "b" (which must be filled with constant values). I assume that at least one of them are meant to be decision variables. An array of decision variable must be declared with "var int" (or better: something like "var 1..size" where 1..size is the domain of possible values in the array).

    Here is an example of a working model, which may or may not be something like what you have in mind:

    int: m = 10;
    int: n = 10;
    array [1..m] of int: a = [1,2,3,4,5,6,7,8,9,10];
    array [1..n] of var 1..10: b;
    var int: results = sum([abs(a[i] - b[j])| i in 1..m, j in 1..n]);
    
    solve minimize results;
    output [
      "Solution: ", show(results),"\n",
      "a: ", show(a), "\n",
      "b: ", show(b), "\n",
      ];
    

    Update 2015-11-19:

    I'm not sure I've understand the requirements completely, but here is a variant. Note that the sum loop don't use the "b" array at all, just "a" and "results". To ensure that the values in "results" are selected from "b" the domain of "results" are simply the set of the values in "b".

    int: m = 10;
    int: n = 10;
    array [1..m] of int: a = [1,2,3,4,5,6,7,8,9,10];
    array [1..n] of int: b = [5,6,13,14,15,16,17,18,19,20];
    
    % decision variables
    
    % values only from b
    array[1..m] of var {b[i] | i in 1..n}: results;
    var int: z; % to minimize
    
    constraint
       z >= 0 /\
       z = sum(i in 1..m) (
         sum(j in 1..m) (abs(a[i]-results[j])) 
         % (abs(a[i]-results[i])) % alternative interpretation (see below)
       )
    ;
    
    solve minimize z;
    
    output [
      "z: ", show(z), "\n",
      "results: ", show(results),"\n",
      "a: ", show(a), "\n",
      "b: ", show(b), "\n",
    ];
    

    Gecode has this for the optimal solution:

     z: 250
     results: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
     a: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
     b: [5, 6, 13, 14, 15, 16, 17, 18, 19, 20]
    

    Another solver (Opturion CPX) has a solution more similar to your variant:

     z: 250
     results: [6, 6, 5, 5, 5, 6, 6, 6, 5, 5]
     a: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
     b: [5, 6, 13, 14, 15, 16, 17, 18, 19, 20]
    

    Note that both solutions has the same optimal objective value ("z") of 250.

    There is, however, an alternative interpretation of the requirement (from your comment):

    for each element in a, select a corresponding value from b - this value has to be the closest in value to each element in a.

    where each value in "results" corresponds just the the value in "a" with the same index ("i") , i.e.

     % ...
     constraint
       z >= 0 /\
       z = sum(i in 1..m) (
          (abs(a[i]-results[i])) 
       )
    
     ;
    

    The solution then is (Gecode):

    z: 19
    results: [5, 5, 5, 5, 5, 6, 6, 6, 6, 13]
    a: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    b: [5, 6, 13, 14, 15, 16, 17, 18, 19, 20]
    

    The last value in "results" (13) is then chosen since it's nearer to 10 (last element in "a").

    Update 2 (2015-11-20)

    Regarding the second comment, about a 2D (not a 3D version you wrote), here's a model. It's based on the second interpretation of the model above. Extending it to larger dimensions is just a matter of changing the dimensions and adding the loop variables.

    Note that this assumes - perhaps contrary to your original question - that the dimensions of "a" and "results" are identical. If they are not, the the second interpretation can not be the one you intend. Also, I changed the values in "a" and "b" to make it more interesting. :-)

    int: m = 3;
    int: n = 3;
    array [1..m,1..n] of int: a = [|1,2,3|4,5,6|7,8,9|];
    array [1..m,1..n] of int: b = [|5,6,13|14,15,16,|7,18,19|];
    
    % decision variables
    
    % values only from b
    array[1..m,1..n] of var {b[i,j] | i in 1..m, j in 1..n}: results;
    var int: z;
    
    constraint
       z >= 0 /\
       z = sum(i in 1..m, j in 1..n) (
         (abs(a[i,j]-results[i,j]))
      )
    ;
    
    solve minimize z;
    
    output [  "z: ", show(z), "\n" ]
    ++["results:"]++
    [
      if j = 1 then "\n" else " " endif ++
        show_int(2,results[i,j])
      | i in 1..m, j in 1..n
    ]
    ++["\na:"]++
    [
       if j = 1 then "\n" else " " endif ++
          show_int(2,a[i,j])
       | i in 1..m, j in 1..n
    ]
    ++["\nb:"]++
    [
       if j = 1 then "\n" else " " endif ++
         show_int(2,b[i,j])
        | i in 1..m, j in 1..n
    ];
    

    One optimal solution of this is this:

    z: 13
    
    results:
     5  5  5
     5  5  6
     7  7  7
    
    a:
     1  2  3
     4  5  6
     7  8  9
    
    b:
     5  6 13
    14 15 16
     7 18 19