英文:
Linear programming solver ignore constraints
问题
I am working on a small project to learn linear programming.
I have formulated a problem where we want to assign P people to N projects.
Each person gives a preference, 2 (strongly desired, 1 desired, 0 not desired) for each of the N projects.
We define 'preferences' as a matrix of size 'PxN'.
Then, we create a boolean matrix 'D PxN' that represents the decision of which project each person is going to be.
And I'd like to maximize 'preferences * D' (where * is the element-wise multiplication).
I am defining some constraints:
-
A person can be assigned to only 1 project.
-
A project can't have only one person (but a project can have 0 people assigned to it, which means that the project is discarded).
To enforce the second constraint, I followed this suggestion:
https://math.stackexchange.com/questions/37075/how-can-not-equals-be-expressed-as-an-inequality-for-a-linear-programming-model.
So far everything seems to work, the result D that maximizes the function is a binary matrix that respects all the constraints.
The problems come when I try to penalize groups of size different from 3. To do so, I create a vector "Size penalty" (SP) and I try to enforce:
|Sum(D[i][j] for i in range(P)) - 3| <= SP[j]
Breaking down this modulo into two equations:
Sum(D[i][j] for i in range(P)) - 3 <= SP[j]
Sum(D[i][j] for i in range(P)) - 3 >= -SP[j]
When I add this, the model still works, but it somehow breaks the constraint of D being an integer between 0 and 1.
How can this be, considering that I defined D as Lp integers?
Full code:
# Your code here
(Note: This is a summary of the code you provided. If you need any specific assistance or have questions about the code, please let me know.)
英文:
I am working on a small project to learn linear programming.
I have formulated a problem where we want to assign P people to N projects.
Each person give a preference, 2 (strongly desired, 1 desired, 0 not desired) for each of the N project.
We define preferences
as a matrix of size PxN
.
then, we create a boolean matrix D PxN
that represent the decision, of in which project each person is going to be.
from pulp import *
import pandas as pd
preferences = [
[2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 2],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 2, 0, 2, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0]
]
N = len(preferences[0]) # Number of projects
P = len(preferences) # Number of people
D = [[LpVariable("D[%i,%i]" % (i, j), 0, 1, LpInteger) for j in range(N)] for i in range(P)]
And I'd like to maximize preferences * D
(where * is the element-wise multiplication).
I am defining some constraints:
- A person can be assigned to only 1 project
for i in range(P):
prob += lpSum(D[i][j] for j in range(N)) == 1
- A project can't have only one person, (but a project can have 0 people assigned to it, which means that the project is discarded).
To enforce the second constraint, I followed this suggestion.
https://math.stackexchange.com/questions/37075/how-can-not-equals-be-expressed-as-an-inequality-for-a-linear-programming-model.
B = 100 # large value
Z = [LpVariable("Z[%i]" % j, 0, 1, LpInteger) for j in range(N)] #Auxiliary boolean
for j in range(N):
prob += lpSum(D[i][j] for i in range(P)) <= B * Z[j]
prob += lpSum(D[i][j] for i in range(P)) >= 2-B+B*Z[j]
So far everything seems to work, the result D that maximize the function is a binary matrix that respect all the constraints.
Example output for D:
1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0
The problems come when I try to penalize groups of size different from 3, the more they get bigger or smaller than 3. (I'd actually like to do it for 3 and 4, starting from penalizing only at size 2 and size >=5, but better start simple).
To do so, I create a vector "Size penalty" (SP) and I try to enforce
|Sum(D[i][j] for i in range(P)) - 3| <= SP[j]
breaking down this modulo into two equations
Sum(D[i][j] for i in range(P)) - 3 <= SP[j] and
Sum(D[i][j] for i in range(P)) - 3 >= -SP[j]
SP1 = [LpVariable("SP[%i]" % j, 0, 1, LpInteger) for j in range(N)]
for j in range(N):
prob += lpSum(D[i][j] for i in range(P)) - 3 <= SP1[j]
prob += lpSum(D[i][j] for i in range(P)) - 3 >= -SP1[j]
and adding the Sum(SP) to the function to maximize
f = M * D - SUM(SP).
prob += lpSum(D[i][j] * preferences[i][j] for i in range(P) for j in range(N)) - lpSum(SP1[j] for j in range(N))
When I add this, the model still works, but it somehow breaks the constraint of D being an integer between 0 and 1.
Example of broken output:
1.0 -3.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0
1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 -1.0 0.0 0.0
0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 0.0
0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0 -2.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 -8.0 9.0
0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 1.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 1.0 0.0 -1.0 0.0 1.0 0.0 -13.0 0.0 0.0 1.0 0.0 12.0 0.0
0.0 1.0 -1.0 1.0 1.0 -3.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0
-1.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0 2.0 0.0 0.0 0.0 -2.0 0.0
1.0 0.0 1.0 1.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 3.0 1.0 -8.0
How can this be, considering that I defined D as Lp integers?
D = [[LpVariable("D[%i,%i]" % (i, j), 0, 1, LpInteger) for j in range(N)] for i in range(P)]
Full code:
from pulp import *
import pandas as pd
preferences = [
[2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 2],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 2, 0, 2, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0]
]
N = len(preferences[0]) # Number of projects
P = len(preferences) # Number of people
B = 100 # B large value
# Create the 'prob' variable to contain the problem data
prob = LpProblem("Project Assignment", LpMaximize)
# Variables
# D[i, j] = 1 if person i is assigned to project j and 0 otherwise. To be optimized
D = [[LpVariable("D[%i,%i]" % (i, j), 0, 1, LpInteger) for j in range(N)] for i in range(P)]
#Define auxiliary Z[N] for inequality
Z = [LpVariable("Z[%i]" % j, 0, 1, LpInteger) for j in range(N)]
#Define size penalty SP[N] variable as int for each project
SP1 = [LpVariable("SP[%i]" % j, 0, 1, LpInteger) for j in range(N)]
# Constraints
# Each person is assigned to exactly one project, use Add(sum(D[i, j]) == 1) for each person i
for i in range(P):
prob += lpSum(D[i][j] for j in range(N)) == 1
# Each project has not 1 person assigned. Use auxiliary variable z to express inequality
# https://math.stackexchange.com/questions/37075/how-can-not-equals-be-expressed-as-an-inequality-for-a-linear-programming-model
for j in range(N):
prob += lpSum(D[i][j] for i in range(P)) <= B * Z[j]
prob += lpSum(D[i][j] for i in range(P)) >= 2-B+B*Z[j]
#Add penalty the more the number of people is different from 3 for each project
for j in range(N):
prob += lpSum(D[i][j] for i in range(P)) - 3 <= SP1[j]
prob += lpSum(D[i][j] for i in range(P)) - 3 >= -SP1[j]
# Objective
# Maximize the total preference score
prob += lpSum(D[i][j] * preferences[i][j] for i in range(P) for j in range(N)) - lpSum(SP1[j] for j in range(N))
# Solve
status = prob.solve()
#print D.values()
for i in range(P):
for j in range(N):
print(D[i][j].value(), end=" ")
print()
#print Z.values()
print("Zeta values")
for j in range(N):
print(Z[j].value(), end=" ")
#Assert that sum(D[i][j]) over i is != 1 for each column j
for j in range(N):
assert sum(D[i][j].value() for i in range(P)) != 1
答案1
得分: 2
以下是翻译好的部分:
"Nice question, with examples and reproducible code!" -> "很好的问题,有示例和可重现的代码!"
"Your main issue is with the construction of the SP1
variable." -> "您主要的问题在于SP1
变量的构建。"
"But before we get to that, the main thing you are not doing is inspecting the solver status, which you must do before looking at results." -> "但在讨论这个问题之前,您主要没有做的事情是检查求解器的状态,这是在查看结果之前必须做的事情。"
"When you execute your model, the status returned is infeasible so whatever result the beast puts out are total junk and should be disregarded." -> "当您执行您的模型时,返回的状态是不可行的,因此无论这个结果是什么,都是垃圾,应该被忽略。"
"So why is it infeasible? Well, because you (unintentionally) limited SP1
to {0, 1} by defining limits, where in fact the 'delta' could be up to the number of projects. I changed that and it solves instantly." -> "那么为什么不可行呢?嗯,因为您(无意中)通过定义限制将SP1
限制为{0, 1},而实际上“delta”可以达到项目的数量。我改变了这一点,它立即解决了。"
"I also tweaked a couple of small things (see comments) and showed a few accelerants (where you can just use LpSum(<var>)
if you want all of it without specifying indices)." -> "我还微调了一些小细节(请参阅注释),并展示了一些加速因素(如果您想要所有内容而不指定索引,可以使用LpSum(<var>)
)。"
"I also added an additional binary indicator if a project is done by anyone so you can count the number of complete projects if that is of interest." -> "我还添加了一个额外的二进制指示器,用于指示项目是否由任何人完成,以便您可以计算完成的项目数量,如果您感兴趣的话。"
"And for giggles, showed an alternate objective function you might consider. If you go that route, be careful with the weighting... You need to do a little stubby-pencil math to make sure the bonus/penalty doesn't eclipse getting things done. Anyhow.... Fixed:" -> "而且为了好玩,我展示了一个您可能考虑的替代目标函数。如果您选择这条路线,请注意权重...您需要做一点点的数学计算,以确保奖金/惩罚不会超过完成任务。不管怎样...问题已解决:"
(以下为代码的翻译,请注意翻译的准确性。)
from pulp import *
preferences = [
[2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 2],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 2, 0, 2, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0]
]
N = len(preferences[0]) # 项目数量
P = len(preferences) # 人员数量
B = N + 1 # 这是足够大的值,并且随着模型的增长而增加
# 创建包含问题数据的'prob'变量
prob = LpProblem("项目分配", LpMaximize)
# 变量
# D[i, j] = 1表示人员i被分配到项目j,否则为0。需要优化
D = [[LpVariable("D[%i,%i]" % (i, j), cat=LpBinary) for j in range(N)] for i in range(P)]
# 项目完成的指示器
proj_complete = LpVariable.dicts('proj_complete', list(range(N)), cat=LpBinary)
# 定义不等式的辅助变量Z[N]
Z = [LpVariable("Z[%i]" % j, cat=LpBinary) for j in range(N)]
# 为每个
<details>
<summary>英文:</summary>
Nice question, with examples and reproducible code!
Your main issue is with the construction of the `SP1` variable. But before we get to that, the main thing you *are not doing* is inspecting the solver status, which **you must do** before looking at results. When you execute your model, the status returned is **infeasible** so whatever result the beast puts out are total junk and should be disregarded.
So why is it infeasible? Well, because you (unintentionally) limited `SP1` to {0, 1} by defining limits, where in fact the "delta" could be up to the number of projects. I changed that and it solves instantly.
I also tweaked a couple of small things (see comments) and showed a few accelerants (where you can just use `LpSum(<var>)` if you want all of it without specifying indices).
I also added an additional binary indicator if a project is done by anyone so you can count the number of complete projects if that is of interest. And for giggles, showed an alternate objective function you might consider. If you go that route, be careful with the weighting... You need to do a little stubby-pencil math to make sure the bonus/penalty doesn't eclipse getting things done. Anyhow.... Fixed:
#### Code:
from pulp import *
# import pandas as pd
preferences = [
[2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 2],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 2, 0, 2, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0]
]
N = len(preferences[0]) # Number of projects
P = len(preferences) # Number of people
B = N + 1 # <-- this is "big enough" and grows with the model
# Create the 'prob' variable to contain the problem data
prob = LpProblem("Project Assignment", LpMaximize)
# Variables
# D[i, j] = 1 if person i is assigned to project j and 0 otherwise. To be optimized
D = [[LpVariable("D[%i,%i]" % (i, j), cat=LpBinary) for j in range(N)] for i in range(P)] # <- LpBinary
# Indicator if project is complete
proj_complete = LpVariable.dicts('proj_complete', list(range(N)), cat=LpBinary) # <- I like this format for making vars, but yours works too.
#Define auxiliary Z[N] for inequality
Z = [LpVariable("Z[%i]" % j, cat=LpBinary) for j in range(N)] # <- LpBinary
#Define size penalty SP[N] variable as int for each project
SP1 = [LpVariable("SP[%i]" % j, 0, N, LpInteger) for j in range(N)]
# Constraints
# Each person is assigned to exactly one project, use Add(sum(D[i, j]) == 1) for each person i
for i in range(P):
prob += lpSum(D[i][j] for j in range(N)) == 1
# Each project has not 1 person assigned. Use auxiliary variable z to express inequality
# https://math.stackexchange.com/questions/37075/how-can-not-equals-be-expressed-as-an-inequality-for-a-linear-programming-model
for j in range(N):
prob += lpSum(D[i][j] for i in range(P)) <= B * Z[j]
prob += lpSum(D[i][j] for i in range(P)) >= 2 - B + B * Z[j]
#Add penalty the more the number of people is different from 3 for each project
for j in range(N):
prob += lpSum(D[i][j] for i in range(P)) - 3 <= SP1[j] # catch the "overage"
prob += lpSum(D[i][j] for i in range(P)) - 3 >= -SP1[j] # catch the "underage"
# link the assignment variable to project completion:
for project in range(N):
prob += proj_complete[project] <= sum(D[person][project] for person in range(P))
# Objective
# Maximize the total preference score
# prob += lpSum(D[i][j] * preferences[i][j] for i in range(P) for j in range(N)) - lpSum(SP1[j] for j in range(N))
# alternate objective: get as many projects done as possible, some bonus for preferences and penalties for over/under assign
proj_assigned, pref_bonus, staff_penalty = 1.0, 0.2, 0.1
prob += proj_assigned * lpSum(proj_complete) \
+ pref_bonus * lpSum(D[i][j] * preferences[i][j] for i in range(P) for j in range(N)) \
- staff_penalty * lpSum(SP1)
# Solve
status = prob.solve()
#print D.values()
for i in range(P):
for j in range(N):
print(D[i][j].value(), end=" ")
print()
#print Z.values()
print("Zeta values")
for j in range(N):
print(Z[j].value(), end=" ")
print()
print("deltas from 3 assigned people")
for j in range(N):
print(SP1[j].value(), end=" ")
print()
print(f'total projects completed: {value(lpSum(proj_complete))} of {N}')
#Assert that sum(D[i][j]) over i is != 1 for each column j
for j in range(N):
assert sum(D[i][j].value() for i in range(P)) != 1
#### Output:
Result - Optimal solution found
Objective value: 7.10000000
Enumerated nodes: 566
Total iterations: 9430
Time (CPU seconds): 1.73
Time (Wallclock seconds): 1.80
Option for printingOptions changed from normal to all
Total time (CPU seconds): 1.73 (Wallclock seconds): 1.81
1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0
Zeta values
1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 1.0 1.0 0.0 0.0 1.0 1.0
deltas from 3 assigned people
1.0 3.0 3.0 3.0 3.0 3.0 3.0 0.0 1.0 1.0 3.0 3.0 1.0 1.0
total projects completed: 6.0 of 14
[Finished in 1.9s]
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论