Search code examples
sortingminizinc

Minizinc array sorting


Lets say I have an array declaration looking like this

array[1..5] of int: temp = [1,0,5,0,3];

Is there a way to initiate a new array looking the same as temp but without the 0's? The result would look like the following

[1,5,3]

or sort the array in such a way that the 0's would be either in the beginning or in the end of the array, which would be

[0,0,1,5,3]

or

[1,5,3,0,0]

Thanks


Solution

  • Even though Axel has answered this, I'll show another approach which - in my book is a little neater.

    • Case 1: the array ("temp") is a constant array. Then one can simply write

      array[int] of int: temp2 = [temp[i] | i in index_set(temp) where temp[i] != 0];

    MiniZinc 2 (in contrast to version 1.*) don't need the size declaration if it can be calculated; it suffices to just use "array[int]". Also, "index_set" is used to be a little more general, e.g. to handle cases where the indices are from 0..4 (see the commented line).

    • Case 2: the array ("s") is an array of decision variables

    If the array to handle is decision variables, we don't know (per definition) how many 0's there are and must rely on the alternative variant, namely to sort the array. One can then use the "sort" function, as shown in the model.

     include "globals.mzn";
    
     % constant
     array[1..5] of int: temp = [1,0,5,0,3];
     % array[0..4] of int: temp = array1d(0..4, [1,0,5,0,3]);
     array[int] of int: temp2 = [temp[i] | i in index_set(temp) where temp[i] != 0];
    
     % decision variables
     array[1..5] of var int: s;
     array[1..5] of var int: s2 = sort(s); % NOT CORRECT, see model below
    
     solve satisfy;
    
     constraint
        s = [1,0,5,0,3]
     ;
    
     %  show our variables
     output 
     [
        "temp: \(temp)\n",
        "temp2: \(temp2)\n",
    
        "s: \(s)\n",
        "s2: \(s2)\n",
     ];
    

    Update

    For the stable version of decision variables, this works what I can see. It calculating the position where to place this number depending on if "s[i]" is 0 or not. Not very pretty though.

     int: n = 5;
     array[1..n] of var 0..5: s;
     array[1..n] of var lb_array(s)..ub_array(s): s2;
    
     solve satisfy;
    
     constraint
       s = [1,0,5,0,3] /\
       forall(i in 1..n) (
          if s[i] != 0 then
            s2[sum([s[j]!=0 | j in 1..i-1])+1] = s[i]
          else 
            s2[sum([s[j]!=0 | j in 1..n]) + sum([s[j]=0 | j in 1..i-1])+1 ] = 0
          endif
       )
    ;
    
    output 
    [ 
      "s: \(s)\n",
      "s2: \(s2)\n",
    ]
    ;
    

    The output is

    s: [1, 0, 5, 0, 3]
    s2: [1, 5, 3, 0, 0]