Python Z3 – 类别分配,如何为类别分配教师的约束

huangapple go评论57阅读模式
英文:

Python Z3 - Class allocation, how to assign a teacher to a class constraint

问题

我有一个看起来像这样的矩阵

```python
[[5, 0, 4, 1, 1, 2, 5, 1, 2, 5, 4],
 [1, 2, 5, 3, 3, 3, 1, 0, 2, 0, 2]]

每一行代表一个老师,数字表示他们对教授该课程的信心。应该添加的约束是老师只能教授一个班级。

我尝试过以下方法:

def 分配(matrix, class, teacher, k):
  解决器 = Solver()
  teacherConfidence = {}
  for i in range(class):
    teacher = f"教师{i+1}"
    teacherConfidence[teacher] = matrix[i]
  print (teacherConfidence)

但我不知道如何告诉解决器说'每个老师只能教授一个班级'。


<details>
<summary>英文:</summary>

I have a matrix that looks like

[[5, 0, 4, 1, 1, 2, 5, 1, 2, 5, 4],
[1, 2, 5, 3, 3, 3, 1, 0, 2, 0, 2]]


Each row is a teacher and the numbers represent how confident they are in teaching the class. The constraint that should be added is that the teacher should only be able to teach one class.

I have tried doing it like so:

def Allocation(matrix, class, teacher, k):
solver = Solver()
teacherConfidence = {}
for i in range(class):
teacher = f"Teacher{i+1}"
teacherConfidence[teacher] = matrix[i]
print (teacherConfidence)


But I have no clue how to tell the solver to say &#39;each teacher should only teach one class&#39;

</details>


# 答案1
**得分**: 2

以下是您要翻译的代码部分:

```python
from z3 import *
for i in range( 1, 2+1 ):
    for j in range( 1, 11+1 ):
        exec( f"x{i}_{j} = Bool( 'x{i}_{j}' ) )
s = Solver()
for i in range( 1, 2+1 ):
    exec( "s.add( Or( " + ", ".join( f"And( x{i}_{j}, " + ", ".join( f"Not( x{i}_{k} )" for k in range( 1, 11+1 ) if k != j ) + " )" for j in range( 1, 11+1 ) ) + " ) )" )
print( f"The problem is {s.check()}. " )
if s.check() == sat:
    print( f"An answer is {s.model()}")
from z3 import *

M = [ [ 5, 0, 4, 1, 1, 2, 5, 1, 2, 5, 4 ], [ 1, 2, 5, 3, 3, 3, 1, 0, 2, 0, 2 ] ]

for i in range( 1, 2+1 ):
    for j in range( 1, 11+1 ):
        exec( f"x{i}_{j} = Bool( 'x{i}_{j}' ) )
o = Optimize()
exec( "o.maximize( " + " + ".join( " + ".join( f"{M[ i-1 ][ j-1 ]} * x{i}_{j}" for j in range( 1, 11+1 ) ) for i in range( 1, 2+1 ) ) + " )" )
for i in range( 1, 2+1 ):
    exec( "o.add( Or( " + ", ".join( f"And( x{i}_{j}, " + ", ".join( f"Not( x{i}_{k} )" for k in range( 1, 11+1 ) if k != j ) + " )" for j in range( 1, 11+1 ) ) + " ) )" )
print( f"The problem is {o.check()}. " )
if o.check() == sat:
    print( f"An answer is {o.model()}")
from scipy.optimize import linprog

M = [ [ 5, 0, 4, 1, 1, 2, 5, 1, 2, 5, 4 ], [ 1, 2, 5, 3, 3, 3, 1, 0, 2, 0, 2 ] ]

target = []
for i in range( 2 ):
    target.extend( [ - M[ i ][ j ] for j in range( 11 ) ] )
A = [ [ 0 for j in range( 2 * 11 ) ] for i in range( 2 ) ]
for i in range( 2 ):
    for j in range( 11 ):
        A[ i ][ i * 11 + j ] = 1
b = [ 1 for i in range( 2 ) ]
problem = linprog( c = target, A_eq = A, b_eq = b, integrality = [ 1 for i in range( 2 * 11 ) ], bounds = [ ( 0, 1 ) for i in range( 2 * 11 ) ], method = 'highs' )
print( f"The maximum is {problem.fun} for the assignment {list( problem.x )}.")
英文:

Please ensure that your questions or texts are complete and comprehensive. Take a few extra seconds to provide all necessary details. In this particular case, you provided an integer-valued matrix with two rows and 11 columns. While you explained the meaning of each row, you neglected to mention the meaning of the columns. Presumably, the columns represent 11 different classes, but the absence of clarifying information leaves room for alternative interpretations. Additionally, you stated that each teacher should teach exactly one class, but you did not elaborate on the objective beyond assigning classes to teachers. Consequently, the confidence coefficients of the teachers seem irrelevant and unnecessary. Based solely on the information provided, one could simply assign any of the classes to the first teacher and a different class to the second teacher. Probably you missed a sentence like "to optimize the assignment and ensure that teachers are teaching the classes they are most confident in, given the available options". Also with the current setup, it implies that at least nine classes would remain unattended by any teacher. I said at least because your text doesn't exclude the possibility of both teachers teach the same class Python Z3 – 类别分配,如何为类别分配教师的约束

Ok, let's go for the two problems closer to your text. 1- Just assigning a class to each teacher. 2- Assigning a class to a teacher with a good outcome.

The first case can be formulated as a simple SAT (satisfiability) problem. Let xi_j a Boolean variable that is True if teacher i is assigned to class j. To say that for each i=1,2, only one xi_j can be True you can consider the following formula.

Python Z3 – 类别分配,如何为类别分配教师的约束

The inner part of the disjunction, just says that for a fixed i and j, only xi_j is True and for all other k different from j, xi_k is False. Here is one way to write a code in Python to ask Z3 to solve this. It might not be the best way, but I used list comprehension and parsing string.

from z3 import *
for i in range( 1, 2+1 ):
    for j in range( 1, 11+1 ):
        exec( f&quot;x{i}_{j} = Bool( &#39;x{i}_{j}&#39; )&quot; )
s = Solver()
for i in range( 1, 2+1 ):
    exec( &quot;s.add( Or( &quot; + &quot;, &quot;.join( f&quot;And( x{i}_{j}, &quot; + &quot;, &quot;.join( f&quot;Not( x{i}_{k} )&quot; for k in range( 1, 11+1 ) if k != j ) + &quot; )&quot; for j in range( 1, 11+1 ) ) + &quot; ) )&quot; )
print( f&quot;The problem is {s.check()}. &quot; )
if s.check() == sat:
    print( f&quot;An answer is {s.model()}&quot; )

The result is sat and the model has all variables False except x1_11 and x2_11 which means z3 just let both teachers choose class 11. There's nothing to be surprised, the condition that each teacher only have 1 class is satisfied.

The second case is not just a SAT (satisfiability) problem, it becomes an optimization problem. Denote your matrix with M. The confidence of teacher 1 for all of the classes he teaches is the summation of the product of the confidence coefficients to the Boolean variables x1_j where they are treated as binary instead of True/False. Well, you can use the average instead of summation, but they are scalar multiple of each other so maximizing one of them is maximizing the other one as well. So your two target functions are the followings.

Python Z3 – 类别分配,如何为类别分配教师的约束

But note none of the two functions obtain negative values so let's combine them into one target function. One way is to just simply add the two target functions.

Instead of calling Solver, we will call Optimize. In addition to .add for giving the constraints, we will use .maximize for giving the target function.

from z3 import *

M = [ [ 5, 0, 4, 1, 1, 2, 5, 1, 2, 5, 4 ], [ 1, 2, 5, 3, 3, 3, 1, 0, 2, 0, 2 ] ]

for i in range( 1, 2+1 ):
    for j in range( 1, 11+1 ):
        exec( f&quot;x{i}_{j} = Bool( &#39;x{i}_{j}&#39; )&quot; )
o = Optimize()
exec( &quot;o.maximize( &quot; + &quot; + &quot;.join( &quot; + &quot;.join( f&quot;{M[ i-1 ][ j-1 ]} * x{i}_{j}&quot; for j in range( 1, 11+1 ) ) for i in range( 1, 2+1 ) ) + &quot; )&quot; )
for i in range( 1, 2+1 ):
    exec( &quot;o.add( Or( &quot; + &quot;, &quot;.join( f&quot;And( x{i}_{j}, &quot; + &quot;, &quot;.join( f&quot;Not( x{i}_{k} )&quot; for k in range( 1, 11+1 ) if k != j ) + &quot; )&quot; for j in range( 1, 11+1 ) ) + &quot; ) )&quot; )
print( f&quot;The problem is {o.check()}. &quot; )
if o.check() == sat:
    print( f&quot;An answer is {o.model()}&quot; )

It is sat and asks only x1_7 and x2_3 to be True where the confidence coefficient of both teachers are 5 (max). To treat the question as an optimization with multiple objectives, you can add the target functions separately, see the following link.

https://theory.stanford.edu/~nikolaj/programmingz3.html#sec-multiple-objectives

If you are not familiar with how to use z3, other than the above link you can watch the following short video.

https://youtu.be/rTVNvZx83IE

But as an optimization problem you don't need to necessarily use z3. You can treat xi_js as binary not only in the target function, but also in the constraints, then having exactly one of x1_js equal to 1 is equivalent with x1_1+x1_2+...+x1_11=1. So you are dealing with a linear binary optimization as the following.

Python Z3 – 类别分配,如何为类别分配教师的约束

You can use any optimization package that supports linear binary optimization such as scipy. Not that the scipy.optimize.linprog assumes the problem is a minimization, so you need to multiply the coefficients of your target function to minus one, also remember that you have 2*11=22 variables.

from scipy.optimize import linprog

M = [ [ 5, 0, 4, 1, 1, 2, 5, 1, 2, 5, 4 ], [ 1, 2, 5, 3, 3, 3, 1, 0, 2, 0, 2 ] ]

target = []
for i in range( 2 ):
    target.extend( [ - M[ i ][ j ] for j in range( 11 ) ] )
A = [ [ 0 for j in range( 2 * 11 ) ] for i in range( 2 ) ]
for i in range( 2 ):
    for j in range( 11 ):
        A[ i ][ i * 11 + j ] = 1
b = [ 1 for i in range( 2 ) ]
problem = linprog( c = target, A_eq = A, b_eq = b, integrality = [ 1 for i in range( 2 * 11 ) ], bounds = [ ( 0, 1 ) for i in range( 2 * 11 ) ], method = &#39;highs&#39; )
print( f&quot;The maximum is {problem.fun} for the assignment {list( problem.x )}.&quot; )

The result says teacher 1 teaches class 1 and teacher 2 teaches class 3. Again both got classes where they have confidence coefficient 5.

If you are not familiar with scipy.optimize.linprog, the following short video is a good place to check.

https://youtu.be/GN3N3MEn_ps

huangapple
  • 本文由 发表于 2023年4月4日 04:30:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75923545.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定