I'm following a course in constraint programming and the professor assigned me this job: Let us consider a n element vector of numbers v = [v_1, ... , v_n], where v_i ∈ N. For i, j ∈ {1, ... , n} a swap(i, j) move has the effect of swapping the values v[i] and v[j]. The problem is that of finding the plan of minimum length that sorts the vector.
int: n = 5;
array[1..n] of int: A = [2, 3, 4, 5, 1];
array[1..n, 1..n] of var int: A_prime;
array[1..n, 1..2] of var 1..n: swaps;
array[1..n, 1..n, 1..n] of var 0..1: lt;
array[1..n] of var int: sums;
var int: steps;
predicate swap(array[1..n, 1..n] of var int: A_prime, var int: i, var int: j, int: k) =
let {var int: temp = A_prime[k-1, i]}
in A_prime[k, i] = A_prime[k-1, j]
/\ A_prime[k, j] = temp
;
constraint steps > 0 /\ steps <= n;
constraint forall(i in 1..n)
(
A_prime[1, i] = A[i]
);
constraint forall(i, j in 1..n where i < j)
(
if A[i] <= A[j] then
lt[1, i, j] = 1
else
lt[1, i, j] = 0
endif
);
constraint forall(i in 1..n)
(
forall(j, k in 1..n where j >= k)
(
lt[i, j, k] = 0
)
);
constraint forall(i in 1..steps)
(
swaps[i, 1] < swaps[i, 2]
);
constraint forall(i, j in 1..steps where i < j)
(
(swaps[i, 1] != swaps[j, 1] /\ swaps[i, 2] == swaps[j, 2]) \/
(swaps[i, 1] == swaps[j, 1] /\ swaps[i, 2] != swaps[j, 2]) \/
(swaps[i, 1] != swaps[j, 1] /\ swaps[i, 2] != swaps[j, 2])
);
constraint forall(i in 2..steps)
(
swap(A_prime, swaps[i-1, 1], swaps[i-1, 2], i)
);
constraint forall(i in 2..steps)
(
forall(j in 1..n where j != swaps[i-1, 1] /\ j != swaps[i-1, 2])
(
A_prime[i, j] = A_prime[i-1, j]
)
);
constraint forall(i in 2..steps)
(
forall(j, k in 1..n where j < k)
(
if A_prime[i, j] <= A_prime[i, k] then
lt[i, j, k] = 1
else
lt[i, j, k] = 0
endif
)
);
constraint forall(i in 1..steps-1)
(
lt[i, swaps[i, 1], swaps[i, 2]] == 0
);
constraint forall(i in 1..steps)
(
sums[i] = sum(j, k in 1..n where j < k)(lt[i, j, k])
);
constraint forall(i, j in 1..steps where i < j)
(
sums[i] < sums[j]
);
constraint sums[steps] == ceil((n*(n-1))/2);
constraint forall(i in steps+1..n, j in 1..n)
(
A_prime[i, j] = 1
);
constraint forall(i in steps+1..n)
(
sums[i] = 0
);
constraint forall(i in steps+1..n)
(
swaps[i, 1] = 1 /\ swaps[i, 2] = 1
);
solve minimize steps;
Here's the solution I came up with. Let me break it down: Firstly, I generate all possible swaps for an n-element vector, then filter only those that arrange v[i] and v[j]. After each swap, I store the resulting array in the A_prime matrix, in the row corresponding to the step in which the swap is made. To determine the only useful swaps, I compute the lt matrix, an upper triangular 3D matrix, where lt[i, j] equals 0 if A_prime[k, i] is less than or equal to A_prime[k, j], and 1 otherwise. I consider the array ordered if the sum of elements in the final lt matrix equals (n*(n-1))/2.
The problem is that this solution is slow; it struggles even with arrays of just 5 elements. I attempted to assign default values to unused variables, like the lower triangular part of the lt matrices, to reduce the number of solutions, but it doesn't seem effective.
Do you have any hint to make this code perform faster? Thanks in advance!