亲爱的,我在下面的代码上遇到性能问题。我知道预测435个变量具有挑战性,但是如果我发现以下用于运行大量变量的模型的方法,对我来说将是非常棒的,否则,如果您可以提出其他替代方法,我将不胜感激。这里的代码:
from __future__ import division
from __future__ import print_function
from ortools.sat.python import cp_model
from ortools.sat.python.cp_model import IntVar
import time
import datetime
def cp_sat_program():
# We have 2 products Gold, Silver each of them has a profit and a bad debt value.
# We have a dataset with a list of customers each one with a PD value.
# We want to maximize the profit of the portfolio of customers keeping the expected loss under a threshold
# and the number of reject less than 55%
time_start = time.time()
date_start = datetime.datetime.fromtimestamp(time_start).strftime('%Y-%m-%d %H:%M:%S')
# Creates the constant variables.
scaling = 1000 # i'm scaling since the algorithm works with integer only
profit_gold = int(0.09 * scaling)
profit_silver = int(0.023 * scaling)
profit_reject = int(0 * scaling)
customers_pd = [0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24,
25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94,
0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24, 0.01,
25.26, 0.01, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94,
0.01, 0.24, 25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01,
25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94,
0.24, 0.01,0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24,
25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94,
0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24
]
num_customers = len(customers_pd)
all_customers_ids = range(num_customers)
customers_pd_scaled = {}
# I'm scaling the PD of the customers because the system works with integers
for i in all_customers_ids:
customers_pd_scaled[i] = int(customers_pd[i] * scaling)
# Bad Debt for each product:
bad_debt_gold = int(profit_gold*0.63*scaling)
bad_debt_silver = int(profit_silver*0.62*scaling)
bad_debt_reject = int(0 * scaling)
# Creates the model.
model = cp_model.CpModel()
# Creates the model variables
suggested_products = {}
for p in all_customers_ids:
suggested_products[p] = [model.NewBoolVar('c' + str(p) + 'Gold'), model.NewBoolVar('c' + str(p) + 'Silver'),
model.NewBoolVar('c' + str(p) + 'Reject')]
scaled_r = model.NewIntVar(0, scaling * 1000, 'rejects')
denom = model.NewIntVar(1, 3 * 1000, 'total Requests')
division = model.NewIntVar(0, scaling * 1000, 'reject/total Requests')
num_products = range(len(suggested_products[0]))
# Creates the constraints.
for i in all_customers_ids:
model.Add(sum(suggested_products[i][b] for b in num_products) == 1)
num_rejects = sum(suggested_products[i][2] for i in all_customers_ids)
model.Add(scaled_r == (num_rejects * scaling)) # i'm scaling since the algorithm works with integer only
model.Add(denom == num_customers)
model.AddDivisionEquality(division, scaled_r, denom) # calculate the division
model.Add(division <= int(0.55 * scaling)) # percentage of rejects less than 55%
# expected loss less or equal than 250.000:
model.Add(sum(suggested_products[i][0] * customers_pd_scaled[i] * bad_debt_gold for i in all_customers_ids) +
sum(suggested_products[i][1] * customers_pd_scaled[i] * bad_debt_silver for i in all_customers_ids) +
sum(suggested_products[i][2] * customers_pd_scaled[i] * bad_debt_reject for i in all_customers_ids)
<= int(250000 * scaling))
# goal
model.Maximize(sum(suggested_products[i][0] * profit_gold for i in all_customers_ids) +
sum(suggested_products[i][1] * profit_silver for i in all_customers_ids) +
sum(suggested_products[i][2] * profit_reject for i in all_customers_ids)
)
# Creates a solver and solves the model.
solver = cp_model.CpSolver()
solver.parameters.num_search_workers = 8
status = solver.Solve(model)
goal = solver.ObjectiveValue()
if status != cp_model.INFEASIBLE:
for i in all_customers_ids:
print('CUSTOMER NUMBER ' + str(i) + ': Gold =' + str(solver.Value(suggested_products[i][0]))
+ ' Silver =' + str(solver.Value(suggested_products[i][1]))
+ ' Reject =' + str(solver.Value(suggested_products[i][2])))
print('Goal = %i' % goal)
print("status = %s" % solver.StatusName(status))
else:
print("status = %s" % solver.StatusName(status))
time_end = time.time()
date_end = datetime.datetime.fromtimestamp(time_end).strftime('%Y-%m-%d %H:%M:%S')
print('Date Start: ' + date_start)
print('Date End: ' + date_end)
cp_sat_program()
如果以下约束的极限值为330000,则非常快;如果极限为250000,则一小时后将永远不会响应:
model.Add(sum(suggested_products[i][0] * customers_pd_scaled[i] * bad_debt_gold for i in all_customers_ids) +
sum(suggested_products[i][1] * customers_pd_scaled[i] * bad_debt_silver for i in all_customers_ids) +
sum(suggested_products[i][2] * customers_pd_scaled[i] * bad_debt_reject for i in all_customers_ids)
<= int(330000 * scaling))
答案 0 :(得分:1)
我相信您可以制定出接近最佳的解决方案。
第一个获利金>>获利银,而bad_debt_silver〜= bad_debt_gold。因此,您可以假设您只会分配黄金。
因此,现在的问题是分配最大目标数,同时保持在250000以下的限制。为此,请按customer_pd对客户进行排序,然后依次选择1比1,直到达到限制为止。
答案 1 :(得分:0)
如果您真的想使用求解器进行求解,这里是一个清理版本
from __future__ import division
from __future__ import print_function
from ortools.sat.python import cp_model
from ortools.sat.python.cp_model import IntVar
import time
import datetime
def cp_sat_program():
# We have 2 products Gold, Silver each of them has a profit and a bad debt value.
# We have a dataset with a list of customers each one with a PD value.
# We want to maximize the profit of the portfolio of customers keeping the expected loss under a threshold
# and the number of reject less than 55%
time_start = time.time()
date_start = datetime.datetime.fromtimestamp(time_start).strftime('%Y-%m-%d %H:%M:%S')
# Creates the constant variables.
scaling = 1000 # i'm scaling since the algorithm works with integer only
profit_gold = int(0.09 * scaling)
profit_silver = int(0.023 * scaling)
profit_reject = int(0 * scaling)
customers_pd = [0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24,
25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94,
0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24, 0.01,
25.26, 0.01, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94,
0.01, 0.24, 25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01,
25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94,
0.24, 0.01,0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24,
25.26, 5.94, 0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94,
0.24, 0.01, 25.26, 0.01, 25.26, 5.94, 0.01, 0.24, 0.01, 25.26, 5.94, 0.01, 25.26, 5.94, 0.24
]
num_customers = len(customers_pd)
all_customers_ids = range(num_customers)
customers_pd_scaled = [int(pd * scaling) for pd in customers_pd]
# Bad Debt for each product:
bad_debt_gold = int(profit_gold*0.63*scaling)
bad_debt_silver = int(profit_silver*0.62*scaling)
bad_debt_reject = int(0 * scaling)
# Creates the model.
model = cp_model.CpModel()
# Creates the model variables
suggested_products = {}
for p in all_customers_ids:
suggested_products[p] = (model.NewBoolVar('c' + str(p) + 'Gold'), model.NewBoolVar('c' + str(p) + 'Silver'),
model.NewBoolVar('c' + str(p) + 'Reject'))
num_products = range(len(suggested_products[0]))
# Creates the constraints.
for i in all_customers_ids:
model.Add(sum(suggested_products[i][b] for b in num_products) == 1)
model.Add(suggested_products[i][1] == 0)
# At most 55% of rejects
model.Add(sum(suggested_products[i][2] for i in all_customers_ids) <= int(num_customers * 0.55))
# expected loss less or equal than 250.000:
model.Add(sum(suggested_products[i][0] * customers_pd_scaled[i] * bad_debt_gold for i in all_customers_ids) +
sum(suggested_products[i][1] * customers_pd_scaled[i] * bad_debt_silver for i in all_customers_ids)
<= int(250000 * scaling))
# goal
model.Maximize(sum(suggested_products[i][0] * profit_gold for i in all_customers_ids) +
sum(suggested_products[i][1] * profit_silver for i in all_customers_ids))
# Creates a solver and solves the model.
solver = cp_model.CpSolver()
solver.parameters.log_search_progress = True
status = solver.Solve(model)
goal = solver.ObjectiveValue()
if status != cp_model.INFEASIBLE:
for i in all_customers_ids:
print('CUSTOMER NUMBER ' + str(i) + ': Gold =' + str(solver.Value(suggested_products[i][0]))
+ ' Silver =' + str(solver.Value(suggested_products[i][1]))
+ ' Reject =' + str(solver.Value(suggested_products[i][2])))
print('Goal = %i' % goal)
print("status = %s" % solver.StatusName(status))
else:
print("status = %s" % solver.StatusName(status))
time_end = time.time()
date_end = datetime.datetime.fromtimestamp(time_end).strftime('%Y-%m-%d %H:%M:%S')
print('Date Start: ' + date_start)
print('Date End: ' + date_end)
cp_sat_program()
它立即返回一个值为6030的赋值。