OR-TOOLS - how to solve ordered allocation problem? - constraint-programming

I have 'n' tables and I have 'm' boxes.
The job is to stack all the boxes on tables.
Question:
what are the different possible combinations?
important note: all the boxes are ordered when put on a table, like stacks. I need to know the rank of each box in the stack.
how to implement that problem with ORTOOL constraint programming / SAT?
what is the best strategy? what variables / constraints?
(I dont expect code, but just advices... unless you are a fast developer :)
Thanks
from ortools.sat.python import cp_model
class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback):
def __init__(self, variables):
cp_model.CpSolverSolutionCallback.__init__(self)
self.__variables = variables
self.__solution_count = 0
def on_solution_callback(self):
self.__solution_count += 1
for v in self.__variables:
print('%s=%i' % (v, self.Value(v)))
print("")
def solution_count(self):
return self.__solution_count
def main():
variables = []
model = cp_model.CpModel()
#################################################
nb_tables = 3
nb_boxes = 4
for box in range(nb_boxes):
box_to_table = model.NewIntVar(0, nb_tables - 1, 'box_'+str(box)+'_to_table')
variables.append(box_to_table)
ranking_variables = []
for box in range(nb_boxes):
rank_of_box_on_its_table = model.NewIntVar(0, nb_boxes - 1, 'rank_of_box_'+str(box)+'_on_its_table')
variables.append(rank_of_box_on_its_table)
ranking_variables.append(rank_of_box_on_its_table)
# the next line is not good because the ranking is global
# and not local to each table. how to manage that?
model.AddAllDifferent(ranking_variables)
#################################################
solver = cp_model.CpSolver()
solution_printer = VarArraySolutionPrinter(variables)
status = solver.SearchForAllSolutions(model, solution_printer)
print('Status = %s' % solver.StatusName(status))
print('Number of solutions found: %i' % solution_printer.solution_count())
main()
And with the boolean version:
#################################################
nb_tables = 3
nb_boxes = 4
for box in range(nb_boxes):
this_box_vars = []
for table in range(nb_tables):
box_in_table = model.NewBoolVar('box_'+str(box)+'_in_table_' + str(table))
variables.append(box_in_table)
this_box_vars.append(box_in_table)
model.Add(sum(this_box_vars) == 1)
ranking_variables = []
for box in range(nb_boxes):
rank_of_box_on_its_table = model.NewIntVar(0, nb_boxes - 1, 'rank_of_box_'+str(box)+'_on_its_table')
variables.append(rank_of_box_on_its_table)
ranking_variables.append(rank_of_box_on_its_table)
# the next line is not good because the ranking is global
# and not local to each table. how to manage that?
model.AddAllDifferent(ranking_variables)
#################################################

Don't use integer variables.
Rule of the thumb:
if you see an AllDifferent constraint, remove it, and replace the integer variable by a list of Boolean variables.
Add Sum(bool_vars) == 1
x[i][j][k] is a Boolean variable indicating that box i is on table j at position k.
y[j][k] indicates if a box in on table j at position k.
each box appears exactly once:
forall i: Sum on j, k box[i][j][k] == 1
each position is occupied by at most one box:
forall j, k: sum on i box[i][k][k] <= 1
if a box is somewhere, it means this somewhere is occupied:
forall i, j, k: box[i][j][k] implies y[j][k]
if a position is occupied, there must be a box at this position:
forall j, k: bool_or([y[j][k].Not(), box[0][j][k], .., box[n - 1][j][k]])
positions must be densely occupied starting from 0 on a table:
forall j, k (except last position): y[j][k].Not() implies y[j][k + 1].Not()
If you want the rank of a box
forall i: rank[i] == sum over j, k box[i][j][k] * k

Related

Is there an efficient way to lower time complexity of this problem ? current T(n) = O(N^3)

need to choose the value such that the value of the equation abs('a' - 'b') + abs('b' - 'c') + abs('c' - 'a') is minimized.
def minimumValue(n: int, a: List[int], b: List[int], c: List[int]) -> int :
# Write your code here.
ans=10000000000000
for i in range (n):
for j in range (n):
for k in range (n):
ans = min(ans, abs(a[i] - b[j]) + abs(b[j] - c[k]) + abs(c[k] - a[i]))
return ans
Here is a O(nlogn) solution. You can sort the three lists, and then do this:
get the first value from each of the three lists
Repeat while we have three values:
sort these three values (and keep track of where they came from)
calculate the target expression with those three, and check if it improves on the best result so far
replace the least of these three values with the next value from the same list as this value came from. If there is no more next value in that list, return the result (quit)
Note also that the formula of the expression to evaluate is the same as doing (max(x,y,z)-min(x,y,z))*2, and this is easy to do when the values x, y and z are sorted, as then it becomes (z-x)*2. To find the minimum that this expression can take, we can leave out the *2 and only do that multiplication at the very end.
Here is the code for implementing that idea:
def minimumValue(n: int, a: List[int], b: List[int], c: List[int]) -> int:
queues = map(iter, map(sorted, (a, b, c)))
three = [[next(q), q] for q in queues]
least = float("inf")
while True:
three.sort()
least = min(least, three[2][0] - three[0][0])
try:
three[0][0] = next(three[0][1])
except:
return least*2
The time complexity for initially sorting the three input lists is O(nlogn). The loop will iterate 3n-2 times, which is O(n). Each of the actions in one loop iteration executes in constant time.
So the overall complexity is determined by the initial sorting: O(nlogn)
Without any further knowledge/assumption on the content of the 3 lists, and if you need to obtain the true minimum value (and not an approximate value), then there's no other choice than using brute force. Some optimisations are possible, but still with a N^3 complexity (and without any speed-up in the worst case).
for i in range (n):
for j in range (n):
v = abs(a[i] - b[j])
if v < ans:
for k in range (n):
ans = min(ans, v + abs(b[j] - c[k]) + abs(c[k] - a[i]))

inner function changing the variable value of outer function

def swap(i,r,c,mat):
for j in range(i+1,c):
if(abs(mat[j][j])>0):
mat[[j,i]] = mat[[i,j]]
break
return mat
def upper_triMat(matA,r,c):
np.set_printoptions(precision=4)
# forward elimination
for i in range(0,c-1):
if matA[i][i] == 0:
matA = swap(i,r,c,matA)
for j in range(i+1,r):
multiplier = matA[j][i]/matA[i][i]
for k in range(0,c):
matA[j][k] = matA[j][k] - multiplier*matA[i][k]
return matA
def dolittle(A):
A = np.array(A)
r,c = np.shape(A)
print(A)
U = upper_triMat(A,r,c) # Here the value of A is changed U.
print(A)
l = np.eye(r,c)
for i in range(0,r-1):
for j in range(i+1,r):
sum = 0
for k in range(0,r):
if i != k:
sum = sum + U[k][i]*l[j][k]
l[j][i] = (A[j][i]-sum)/U[i][i]
return l,U
A = [[3,-0.1,-0.2],
[0.1,7,-0.3],
[0.3,-0.2,10]]
dolittle(A)
When i call the upper_triMat function "A" changes in dolittle function. Why?? A is A and the upper_triMat function assigning it to U. But A is also getting the value of U. Using Jupyter Notebook. I am doing LU decomposition
upper_triMat mutates its parameter matA. And since matA is a reference to A, it's being modified.
Maybe you could fix it that way
U = upper_triMat(A.copy(),r,c) # pass a copy of the list instead of the reference of the original one.

How can I take max value from some list but with some conditions?

list1 = [140,232,857,273,405,374,1234,394,1803]
u = 0
b = 4
for i in list1[u:b]
u+= 4
u+= 4
print(max(i))
Now I wanna take the max value from that list but only from list1[0:4] and continue with that.
Now I want to do something like it on this code:
for im in images:
ww, hh = zip(*(im.size for im in images))
www, hhh = im.size
max_h = max(hh)
y_test = []
try:
new_im.paste(im, (x_offset,y))
with open('x.txt', 'a') as file:
file.write(str(x_offset) + "\n")
with open('y.txt', 'a') as file:
file.write(str(y) + "\n")
with open('w.txt', 'a') as file:
file.write(str(www) + "\n")
with open('h.txt', 'a') as file:
file.write(str(hhh) + "\n")
x_offset += im.size[0]
if x_offset > int(q_w) - www:
print(max(hh))
x_offset =0
y += max(hhh)
if hh < y:
y += hhh
if hh > y:
y -= hhh
else:
y += max_h
except:
continue
if x_offset > int(q_w) - www then I want to take the max value of hhh until here.
How can I do that?
Please understand that we apply max( ... ) to a sequence,
rather than to a single scalar value,
e.g. max([6, 7, 8]) rather than max(4).
That first example was unclear.
I think your intent is to run a window of size k=4 over the list
and display local maxima.
A more natural way to express that, without incrementing u inside the loop,
would be:
for i in range(len(list1) - k):
window = list1[i : i + k]
print(i, max(window))
A very similar approach would apply to your second example.
Phrase the for loop in this way, and slice off k elements:
for i, im in enumerate(images):
if i + k < len(images):
window = images[i : i + k]
...
After that you're on your own,
do something useful with window,
as your question was unclear on details of what you want.
You wrote this line within the loop:
ww, hh = zip(*(im.size for im in images))
It computes the same thing each time, so to make things quicker
it belongs outside the loop.
Additionally, it trashes the im iteration variable,
so for the rest of the loop im is a constant value,
it is always the last element of images.
This seems Bad, it's probably not what you wanted.
Similarly, this is a constant
which could be hoisted outside the loop:
max_h = max(hh)
Also, your except: continue is correct,
but except: pass would be the more usual idiom,
expressing the intent to simply ignore the exception.
No statements follow it in the code you posted,
so both would work out the same.
Understand that continue would skip to top-of-loop,
skipping the following statements if there were any.

Where should I modify my breadth first search algo for finding the shortest path between 2 nodes?

I am taking a graph algo course, i am stuck with this problem of finding the shortest path between 2 vertices.
The problem statement : Given an un-directed graph with n vertices and m edges and two vertices u and v, compute the length of the shortest path between u and v. Output the minimum number of edges in a path from u to v, or −1 if there is no path.
My code is passing some test-cases, but few of them are failing, and i can't really see where am i going wrong, so any kind of insight would really be helpful.
def explore(arr, start, end, vis):
vis[start] = 0; q = [start] # queue for storing the node for exploring
while len(q) != 0: # iterates till queue isn't empty
u = q.pop()
for i in arr[u]: # checks for all nodes connected to uth node
if vis[i] == -1: # if the node is unvisited
q.insert(0, i)
vis[i] = vis[u] + 1
elif vis[i] > vis[u] + 1: # if the visited node has shorter path
q.insert(0, i)
vis[i] = vis[u] + 1
return vis[end]
if True:
n, m = map(int, input().split()) # n : vertices, m : edges
arr = {} # stores edges
for i in range(m): # accepts edges as inputs
a, b = map(int, input().split()) # (a,b) >(0,0)
if a-1 in arr.keys():
arr[a-1].append(b-1)
else:
arr[a-1] = [b-1]
if b-1 in arr.keys():
arr[b-1].append(a-1)
else:
arr[b-1] = [a-1]
if m > 0:
start, end = map(int, input().split()) # start : source node, end = dest node
vis = [-1 for i in range(n)] # will store shortest path for each node
print(explore(arr, start-1, end-1, vis))
else:
print(-1)
You have issues with your code due to problems with indexes. You use indexes started from 1 here: q = [start] but later you use indexes started from 0: for i in arr[u] (note, no -1) and so on. I strictly recommend to use indexing from 0 everywhere - it's definitely more readable and helps to avoid possible errors with indexes. Also, you don't really need your elif case if you add new item to the end of q (you insert it to start of q for some reason). Corrected code (warning - indexes in input parameters starts from 0 everywhere!):
def explore(arr, start, end, vis):
vis[start] = 0
q = [start] # queue for storing the node for exploring
while len(q): # iterates till queue isn't empty
u = q.pop()
for i in arr[u]: # checks for all nodes connected to uth node
if vis[i] == -1: # if the node is unvisited
q.append(i)
vis[i] = vis[u] + 1
return vis[end]

power (a, n) in PYTHON

POwer in Python. How to write code to display a ^ n using funсtion?
why doesn't this code working?
a = int(input())
n = int(input())
def power(a, n):
for i in range (n):
a=1
a *= n
print(power (a, n))
Few errors:
Changing a will lose your power parameter, use result (or something else).
Move setting result = 1 outside your loop to do so once.
Multiply by a not by n.
Use return to return a value from the function
def power(a, n):
result = 1 # 1 + 2
for _ in range (n):
result *= a # 3
return result # 4
Style notes:
Setting/mutating a parameter is considered bad practice (unless explicitly needed), even if it is immutable as is here.
If you're not going to use the loop variable, you can let the reader know by using the conventional _ to indicate it (_ is a legal variable name, but it is conventional to use it when not needing the variable).
Tip: you can simple use a**n
It doesn't work because your function doesn't return the end value. Add return a to the end of the function.
ALSO:
That is not how a to the power of n is is calculated.
A proper solution:
def power(a,n):
pow_a = a
if n is 0:
return 1
for _ in range(n-1): # Substracting 1 from the input variable n
pow_a *= a # because n==2 means a*a already.
return pow_a
and if you want to be really cool, recursion is the way:
def power_recursive(a,n):
if n is 0:
return 1
elif n is 1:
return a
else:
a *= power_recursive(a,n-1)
return a

Resources