最近忙建模比赛去了,所以其他事情有些搁置
下面会分享 A 题前两问的思路,代码和最终结果
第三问的模型我自己不太满意,便不分享出来了
最终结果出来啦,现在的思路方向是正确的(一等奖),可以安心参考
忽略常数项 C:
max M = ∑ i = 1 100 ∑ j = 1 10 ( x i ∗ y i , j ∗ ( R ∗ t i , j ∗ ( 1 − h i , j ) − t i , j ∗ h i , j ) ) − P ∗ ( ∑ i = 1 100 x i − 1 ) 2 − P ∗ ∑ i = 1 100 ( ∑ j = 1 10 y i , j − x i ) 2 \max M = \sum_{i=1}^{100} \sum_{j=1}^{10} (x_i * y_{i,j} * (R * t_{i,j} * (1 - h_{i,j}) - t_{i,j} * h_{i,j})) - P * (\sum_{i=1}^{100} x_i - 1)^2 - P * \sum_{i=1}^{100} (\sum_{j=1}^{10} y_{i,j} - x_i)^2 maxM=∑i=1100∑j=110(xi∗yi,j∗(R∗ti,j∗(1−hi,j)−ti,j∗hi,j))−P∗(∑i=1100xi−1)2−P∗∑i=1100(∑j=110yi,j−xi)2
还可以进行一些简化:
因为仅选择一张卡,所以目标函数中的 x i x_i xi是可以省去的,也就是说只使用 y i , j y_{i,j} yi,j来表示是否选择第i张卡的第j个阈值,此时决策变量从1100变为了1000。如果 y i , j = 1 y_{i,j}=1 yi,j=1,那么表示选择了第i张卡的第j个阈值,现在我们可以将约束条件整合成 ∑ i = 1 100 ∑ j = 1 10 y i , j = 1 \sum_{i=1}^{100}\sum_{j=1}^{10} y_{i,j} = 1 ∑i=1100∑j=110yi,j=1 就行
max M = ∑ i = 1 100 ∑ j = 1 10 ( y i , j ∗ ( R ∗ t i , j ∗ ( 1 − h i , j ) − t i , j ∗ h i , j ) ) − P ∗ ( ∑ i = 1 100 ∑ j = 1 10 y i , j − 1 ) 2 \max M = \sum_{i=1}^{100} \sum_{j=1}^{10} (y_{i,j} * (R * t_{i,j} * (1 - h_{i,j}) - t_{i,j} * h_{i,j})) - P * (\sum_{i=1}^{100}\sum_{j=1}^{10} y_{i,j} - 1)^2 maxM=∑i=1100∑j=110(yi,j∗(R∗ti,j∗(1−hi,j)−ti,j∗hi,j))−P∗(∑i=1100∑j=110yi,j−1)2
按以下规则拼接 y i , j y_{i,j} yi,j为 z k z_k zk,以卡号的顺序排列:
k = 10 ( i − 1 ) + j k = 10(i-1)+j k=10(i−1)+j
则有 max M = ∑ i = 1 100 ∑ j = 1 10 ( z 10 ( i − 1 ) + j ∗ ( R ∗ t i , j ∗ ( 1 − h i , j ) − t i , j ∗ h i , j ) ) − P ∗ ( ∑ i = 1 100 ∑ j = 1 10 z 10 ( i − 1 ) + j − 1 ) 2 \max M = \sum_{i=1}^{100} \sum_{j=1}^{10} (z_{10(i-1)+j} * (R * t_{i,j} * (1 - h_{i,j}) - t_{i,j} * h_{i,j})) - P * (\sum_{i=1}^{100}\sum_{j=1}^{10} z_{10(i-1)+j} - 1)^2 maxM=∑i=1100∑j=110(z10(i−1)+j∗(R∗ti,j∗(1−hi,j)−ti,j∗hi,j))−P∗(∑i=1100∑j=110z10(i−1)+j−1)2
将i,j也表示为k的形式:
j = ( k + 1 ) m o d 10 , i = ( k − 1 ) / / 10 + 1 j = (k+1) mod 10, i = (k-1)//10+1 j=(k+1)mod10,i=(k−1)//10+1
t k = t k / / 10 , ( k + 1 ) m o d 10 t_k = t_{k//10,(k+1)mod10} tk=tk//10,(k+1)mod10
max M = ∑ k = 1 1000 ( z k ∗ ( R ∗ t k ∗ ( 1 − h k ) − t k ∗ h k ) ) − P ∗ ( ∑ k = 1 1000 z k − 1 ) 2 \max M = \sum_{k=1}^{1000} (z_k * (R * t_k * (1 - h_k) - t_k * h_k)) - P * (\sum_{k=1}^{1000} z_k - 1)^2 maxM=∑k=11000(zk∗(R∗tk∗(1−hk)−tk∗hk))−P∗(∑k=11000zk−1)2
为了将目标函数化为 QUBO 形式,需要将所有的一次项和常数项都转化为二次项。可以利用以下的等式来实现这一目的:
将目标函数化为 :
max M = ∑ k = 1 1000 z k 2 ( R ∗ t k ∗ ( 1 − h k ) − t k ∗ h k + 2 P ) − P ∗ ( ( ∑ k = 1 1000 z k ) 2 + 1 ) \max M = \sum_{k=1}^{1000} z_k^2(R * t_k * (1 - h_k) - t_k * h_k + 2P)- P*((\sum_{k=1}^{1000} z_k)^2 + 1) maxM=∑k=11000zk2(R∗tk∗(1−hk)−tk∗hk+2P)−P∗((∑k=11000zk)2+1).
最后将其转化为最小化,忽略常数项P:
min M = − ∑ k = 1 1000 z k 2 ( R ∗ t k ∗ ( 1 − h k ) − t k ∗ h k + 2 P ) + P ∗ ( ( ∑ k = 1 1000 z k ) 2 + 1 ) \min M = -\sum_{k=1}^{1000} z_k^2(R * t_k * (1 - h_k) - t_k * h_k + 2P)+ P*((\sum_{k=1}^{1000} z_k)^2 + 1) minM=−∑k=11000zk2(R∗tk∗(1−hk)−tk∗hk+2P)+P∗((∑k=11000zk)2+1).
此时可以用QUBO矩阵Q表示,有:
min M = z T Q z \min M = z^T Q z minM=zTQz
其中 Q Q Q 的元素为:
Q k , l = { − ( R ∗ t k ∗ ( 1 − h k ) − t k ∗ h k ) ) − P k = l P k ≠ l Q_{k,l} = \begin{cases} - (R * t_k * (1 - h_k) - t_k * h_k)) - P & k = l \\\ P & k \ne l \end{cases} Qk,l={−(R∗tk∗(1−hk)−tk∗hk))−P Pk=lk=l
最终的结果是选择第 49 张卡的第 1 个阈值,对应的收入为 61172.0
这一部分的求解有很多种方式,比如:你可以使用 D-wave 提供的 API 在量子计算机上真正的进行求解,也可以使用OpenJij库的函数 SQASampler() 模拟量子退火求解。
# 数据处理
import numpy as np
import pandas as pd
# 导入 PyQUBO 库
from pyqubo import Array, Binary, Constraint
# 模型求解
from dwave.system import LeapHybridSampler
from openjij import SQASampler
# 读取信用卡数据
data = pd.read_csv("附件1:data_100.csv", header=0)
t = data.iloc[:, ::2].values.T # 通过率
h = data.iloc[:, 1::2].values.T # 坏账率
print(t.shape, h.shape) # 行对应阈值,列对应卡
import re
def ret_result(solution):
'''
返回索引和结果
'''
i,j = 0,0
pattern_idx = re.compile(r"[0-9]+")
for key,value in solution.items():
if value == 1:
print(key, value)
idx = pattern_idx.findall(key)
if len(idx)==1:
i = int(idx[0])
print(i)
else:
i, j = int(idx[0]), int(idx[1])
print(i, j)
result = C * R * t[i, j] * (1 - h[i, j]) - C * t[i, j] * h[i, j]
return i, j, result
# 定义常数和变量
C = 1000000 # 总贷款资金
R = 0.08 # 利率
n = 100 # 信用评分卡数量
P = C # 惩罚系数
# 定义目标函数
H = 0
for i in range(100):
for j in range(10):
H -= x[i] * y[i, j] * (C * R * t[i, j] * (1 - h[i, j]) - C * t[i, j] * h[i, j])
# 定义约束条件
C1 = P * (sum(x) - 1)**2
C2 = P * sum((sum(y[i]) - x[i])**2 for i in range(100))
# 将目标函数和约束条件相加
model = (H + C1 + C2).compile()
# 利用to_qubo()得到矩阵Q
Q, _ = model.to_qubo()
# 求解 QUBO 模型
sampler = LeapHybridSampler(num_reads=3) # 使用 LeapHybridSampler
response = sampler.sample_qubo(Q) # 采样 QUBO
solution = response.first.sample # 获取最优解
i, j, result = ret_result(solution)
print(f"第{i+1}张卡选择第{j+1}个阈值的收入是: {result}")
z = [Binary(f"z_{k}") for k in range(n*10+100)]
# 定义目标函数
H = 0
for i in range(100):
for j in range(10):
H -= z[i] * z[10 * i + j + 100] * (C * R * t[i, j] * (1 - h[i, j]) - C * t[i, j] * h[i, j])
# 定义约束条件
C1 = P * (sum(z[:100]) - 1)**2
C2 = P * sum((sum(z[i * 10 + 100: i*10+110]) - z[i])**2 for i in range(100))
# 将目标函数和约束条件相加
model = (H + C1 + C2).compile()
# 利用to_qubo()得到矩阵Q
Q, _ = model.to_qubo()
# 求解 QUBO 模型
sampler = LeapHybridSampler() # 使用 LeapHybridSampler
response = sampler.sample_qubo(Q) # 采样 QUBO
solution = response.first.sample # 获取最优解
# 打印对应的索引
pattern_idx = re.compile(r"[0-9]+")
for key,value in solution.items():
if value == 1:
print(key, value)
idx = int(pattern_idx.findall(key)[0])
print(idx)
因为已经给定了三张信用评分卡,只需要选择每张信用评分卡的阈值。
可以用三个二值向量 y 1 , i y_{1,i} y1,i, y 2 , j y_{2,j} y2,j, y 3 , k y_{3,k} y3,k 来表示三张信用评分卡的阈值,每个变量有 10 种取值,分别对应 10 个阈值。
目标函数是最大化银行收入减去坏账损失,约束条件是每个变量只能取一个值。
定义决策变量: y i , j y_{i,j} yi,j 为一个二值变量,表示是否选择第 i 张信用评分卡的第 j 个阈值,其中 i = 1, 2, 3,j = 1, 2, …, 10。
定义目标函数:我们的目标是最大化银行的最终收入,根据题目给出的公式,我们可以写出目标函数为:
max M = ∑ i = 1 10 ∑ j = 1 10 ∑ k = 1 10 ( y 1 , i ∗ y 2 , j ∗ y 3 , k ∗ t 1 , i ∗ t 2 , j ∗ t 3 , k ) ∗ ( C ∗ R ∗ ( 1 − 1 3 ( h 1 , i + h 2 , j + h 3 , k ) ) − C ∗ 1 3 ( h 1 , i + h 2 , j + h 3 , k ) ) \max M = \sum_{i=1}^{10} \sum_{j=1}^{10}\sum_{k=1}^{10} (y_{1,i}*y_{2,j}*y_{3,k} * t_{1,i}*t_{2,j}*t_{3,k}) \\ * (C * R * (1 - \frac{1}{3} (h_{1,i}+h_{2,j}+h_{3,k})) - C * \frac{1}{3} (h_{1,i}+h_{2,j}+h_{3,k})) maxM=∑i=110∑j=110∑k=110(y1,i∗y2,j∗y3,k∗t1,i∗t2,j∗t3,k)∗(C∗R∗(1−31(h1,i+h2,j+h3,k))−C∗31(h1,i+h2,j+h3,k))
因为该题规定了选择前三张卡,所以我们可以简化总通过率和总坏账率的表达形式为:
T i , j , k = t 1 , i ∗ t 2 , j ∗ t 3 , k T_{i,j,k} = t_{1,i}*t_{2,j}*t_{3,k} Ti,j,k=t1,i∗t2,j∗t3,k
H i , j , k = 1 3 ( h 1 , i + h 2 , j + h 3 , k ) H_{i,j,k} = \frac{1}{3} (h_{1,i}+h_{2,j}+h_{3,k}) Hi,j,k=31(h1,i+h2,j+h3,k)
忽略常数项做简化,有:
max M = ∑ i = 1 10 ∑ j = 1 10 ∑ k = 1 10 ( y 1 , i ∗ y 2 , j ∗ y 3 , k ) ∗ ( T i , j , k ∗ ( R ∗ ( 1 − H i , j , k ) − H i , j , k ) ) \max M = \sum_{i=1}^{10} \sum_{j=1}^{10}\sum_{k=1}^{10} (y_{1,i}*y_{2,j}*y_{3,k} ) * (T_{i,j,k} * (R * (1 - H_{i,j,k}) -H_{i,j,k})) maxM=∑i=110∑j=110∑k=110(y1,i∗y2,j∗y3,k)∗(Ti,j,k∗(R∗(1−Hi,j,k)−Hi,j,k))
定义约束条件:每张信用评分卡只能选择一个阈值,即: ∑ j = 1 10 y i , j = 1 \sum_{j=1}^{10} y_{i,j} = 1 ∑j=110yi,j=1,其中 i = 1, 2, 3。
将约束条件加入目标函数中,并引入一个惩罚项系数 P,得到:
max M = ∑ i = 1 10 ∑ j = 1 10 ∑ k = 1 10 ( y 1 , i ∗ y 2 , j ∗ y 3 , k ) ∗ ( T i , j , k ∗ ( R ∗ ( 1 − H i , j , k ) − H i , j , k ) ) − P ∗ ∑ i = 1 3 ( ∑ j = 1 10 y i , j − 1 ) 2 \max M = \sum_{i=1}^{10} \sum_{j=1}^{10}\sum_{k=1}^{10} (y_{1,i}*y_{2,j}*y_{3,k} ) * (T_{i,j,k} * (R * (1 - H_{i,j,k}) -H_{i,j,k}))- P * \sum_{i=1}^{3} (\sum_{j=1}^{10} y_{i,j} - 1)^2 maxM=∑i=110∑j=110∑k=110(y1,i∗y2,j∗y3,k)∗(Ti,j,k∗(R∗(1−Hi,j,k)−Hi,j,k))−P∗∑i=13(∑j=110yi,j−1)2
这个目标函数是三次的,我们需要将其转为二次,这里设 y 2 , j ∗ y 3 , k = z j , k y_{2,j}*y_{3,k} = z_{j,k} y2,j∗y3,k=zj,k,这一操作对应的约束条件为:
(1) z j , k ≤ y 2 , j z_{j,k} \leq y_{2,j} zj,k≤y2,j
(2) z j , k ≤ y 3 , k z_{j,k} \leq y_{3,k} zj,k≤y3,k
(3) z j , k ≥ y 2 , j + y 3 , k − 1 → y 2 , j + y 3 , k + ( − z j , k ) ≤ 1 z_{j,k} \geq y_{2,j} + y_{3,k} - 1 \rightarrow y_{2,j} + y_{3,k} + (-z_{j,k}) \le 1 zj,k≥y2,j+y3,k−1→y2,j+y3,k+(−zj,k)≤1
这些约束条件可以保证当且仅当 y 2 , j = 1 y_{2,j} = 1 y2,j=1 且 y 3 , k = 1 y_{3,k} = 1 y3,k=1 时, z j , k = 1 z_{j,k} = 1 zj,k=1。
根据参考文献(A Tutorial on Formulating and Using QUBO Models)中的
可以得到对应的惩罚项:
(1) P ∗ ( z j , k − y 2 , j ∗ z j , k ) P*(z_{j,k} - y_{2,j} * z_{j,k}) P∗(zj,k−y2,j∗zj,k)
(2) P ∗ ( z j , k − y 3 , k ∗ z j , k ) P*(z_{j,k} - y_{3,k} * z_{j,k}) P∗(zj,k−y3,k∗zj,k)
(3) P ∗ ( y 2 , j y 3 , k − y 2 , j ∗ z j , k − y 3 , k ∗ z j , k ) P*(y_{2,j}y_{3,k}-y_{2,j} * z_{j,k}-y_{3,k} * z_{j,k}) P∗(y2,jy3,k−y2,j∗zj,k−y3,k∗zj,k) = P ∗ ( z j , k − y 2 , j ∗ z j , k − y 3 , k ∗ z j , k ) P*(z_{j,k}-y_{2,j} * z_{j,k}-y_{3,k} * z_{j,k}) P∗(zj,k−y2,j∗zj,k−y3,k∗zj,k)
汇总即有:
P ∗ ( 3 z j , k + y 2 , j y 3 , k − 2 y 3 , k z j , k − 2 y 2 , j z j , k ) P*(3z_{j,k} + y_{2,j}y_{3,k}-2y_{3,k}z_{j,k}-2y_{2,j}z_{j,k}) P∗(3zj,k+y2,jy3,k−2y3,kzj,k−2y2,jzj,k)
将惩罚项加入目标函数:
max M = ∑ i = 1 10 ∑ j = 1 10 ∑ k = 1 10 ( y 1 , i ∗ z j , k ) ∗ ( T i , j , k ∗ ( R ∗ ( 1 − H i , j , k ) − H i , j , k ) ) − P ∗ ∑ i = 1 3 ( ∑ j = 1 10 y i , j − 1 ) 2 − P ∗ ∑ j = 1 10 ∑ k = 1 10 ( 3 z j , k + y 2 , j y 3 , k − 2 y 3 , k z j , k − 2 y 2 , j z j , k ) \max M = \sum_{i=1}^{10} \sum_{j=1}^{10}\sum_{k=1}^{10} (y_{1,i}*z_{j,k} ) * (T_{i,j,k} * (R * (1 - H_{i,j,k}) -H_{i,j,k}))\\- P * \sum_{i=1}^{3} (\sum_{j=1}^{10} y_{i,j} - 1)^2-P*\sum_{j=1}^{10}\sum_{k=1}^{10}(3z_{j,k} + y_{2,j}y_{3,k}-2y_{3,k}z_{j,k}-2y_{2,j}z_{j,k}) maxM=∑i=110∑j=110∑k=110(y1,i∗zj,k)∗(Ti,j,k∗(R∗(1−Hi,j,k)−Hi,j,k))−P∗∑i=13(∑j=110yi,j−1)2−P∗∑j=110∑k=110(3zj,k+y2,jy3,k−2y3,kzj,k−2y2,jzj,k)
打断一下,下面的拼接只是为了写出 QUBO 矩阵,其实能弄明白第一题的怎么写就行,不需要太花费时间,这里只是一个简单的二次型矩阵表达,但因为拼接后看起来会很复杂。
现在,我们需要将 y 1 , i , y 2 , j , y 3 , k y_{1,i},y_{2,j},y_{3,k} y1,i,y2,j,y3,k和 z j , k z_{j,k} zj,k合并成一个向量 L m L_{m} Lm,拼接规则如下:
L m = { y 1 , m 1 ≤ m ≤ 10 y 2 , m − 10 11 ≤ m ≤ 20 y 3 , m − 20 21 ≤ m ≤ 30 z ( m − 31 ) / / 10 + 1 , ( m − 31 ) % 10 + 1 31 ≤ m ≤ 130 L_{m} = \begin{cases} y_{1,m} & 1 \leq m \leq 10 \\y_{2,m-10} & 11 \leq m \leq 20 \\y_{3,m-20} &21 \leq m \leq 30 \\ z_{(m-31)//10+1,(m-31)\%10+1} & 31 \leq m \leq 130\end{cases} Lm=⎩ ⎨ ⎧y1,my2,m−10y3,m−20z(m−31)//10+1,(m−31)%10+11≤m≤1011≤m≤2021≤m≤3031≤m≤130
此时,对应的约束条件转化为:
∑ j = 1 10 y i , j = 1 → ∑ j = 1 10 L ( i − 1 ) ∗ 10 + j = 1 \sum_{j=1}^{10} y_{i,j} = 1 \rightarrow \sum_{j=1}^{10} L_{(i-1)*10+j} = 1 ∑j=110yi,j=1→∑j=110L(i−1)∗10+j=1
z j , k ≤ y 2 , j → L ( j − 1 ) ∗ 10 + k + 30 ≤ L j + 10 z_{j,k} \leq y_{2,j} \rightarrow L_{(j-1)*10+k+30} \leq L_{j+10} zj,k≤y2,j→L(j−1)∗10+k+30≤Lj+10
z j , k ≤ y 3 , k → L ( j − 1 ) ∗ 10 + k + 30 ≤ L k + 20 z_{j,k} \leq y_{3,k} \rightarrow L_{(j-1)*10+k+30} \leq L_{k+20} zj,k≤y3,k→L(j−1)∗10+k+30≤Lk+20
y 2 , j + y 3 , k + ( − z j , k ) ≤ 1 → L j + 10 + L k + 20 + ( − L ( j − 1 ) ∗ 10 + k + 30 ) ≤ 1 y_{2,j} + y_{3,k} + (-z_{j,k}) \le 1 \rightarrow L_{j+10} + L_{k+20} + (-L_{(j-1)*10+k+30}) \leq 1 y2,j+y3,k+(−zj,k)≤1→Lj+10+Lk+20+(−L(j−1)∗10+k+30)≤1
目标函数:
max M = ∑ i = 1 10 ∑ j = 1 10 ∑ k = 1 10 ( L i ∗ L ( j − 1 ) ∗ 10 + k + 30 ) ∗ ( T i , j , k ∗ ( R ∗ ( 1 − H i , j , k ) − H i , j , k ) ) − P ∗ ∑ i = 1 3 ( ∑ j = 1 10 L ( i − 1 ) ∗ 10 + j − 1 ) 2 − P ∗ ∑ j = 1 10 ∑ k = 1 10 ( 3 L ( j − 1 ) ∗ 10 + k + 30 + L j + 10 L k + 20 − 2 L k + 20 L ( j − 1 ) ∗ 10 + k + 30 − 2 L j + 10 L ( j − 1 ) ∗ 10 + k + 30 ) \max M = \sum_{i=1}^{10} \sum_{j=1}^{10}\sum_{k=1}^{10} (L_i*L_{(j-1)*10+k+30} ) * (T_{i,j,k} * (R * (1 - H_{i,j,k}) -H_{i,j,k})) - P * \sum_{i=1}^{3} (\sum_{j=1}^{10} L_{(i-1)*10+j} - 1)^2-P*\sum_{j=1}^{10}\sum_{k=1}^{10}(3L_{(j-1)*10+k+30} + L_{j+10}L_{k+20}-2L_{k+20}L_{(j-1)*10+k+30}-2L_{j+10}L_{(j-1)*10+k+30}) maxM=∑i=110∑j=110∑k=110(Li∗L(j−1)∗10+k+30)∗(Ti,j,k∗(R∗(1−Hi,j,k)−Hi,j,k))−P∗∑i=13(∑j=110L(i−1)∗10+j−1)2−P∗∑j=110∑k=110(3L(j−1)∗10+k+30+Lj+10Lk+20−2Lk+20L(j−1)∗10+k+30−2Lj+10L(j−1)∗10+k+30)
因为 ( ∑ j = 1 10 L ( i − 1 ) ∗ 10 + j − 1 ) 2 = ( ∑ j = 1 10 L ( i − 1 ) ∗ 10 + j ) 2 − 2 ∑ j = 1 10 L ( i − 1 ) ∗ 10 + j + 1 (\sum_{j=1}^{10} L_{(i-1)*10+j} - 1)^2 = (\sum_{j=1}^{10} L_{(i-1)*10+j})^2 - 2 \sum_{j=1}^{10} L_{(i-1)*10+j} + 1 (∑j=110L(i−1)∗10+j−1)2=(∑j=110L(i−1)∗10+j)2−2∑j=110L(i−1)∗10+j+1
可以将目标函数转换为以下形式:
min M = − ∑ i = 1 10 ∑ j = 1 10 ∑ k = 1 10 ( L i ∗ L ( j − 1 ) ∗ 10 + k + 30 ) ∗ ( T i , j , k ∗ ( R ∗ ( 1 − H i , j , k ) − H i , j , k ) ) + P ∗ ∑ i = 1 3 ( ( ∑ j = 1 10 L ( i − 1 ) ∗ 10 + j ) 2 − 2 ∑ j = 1 10 L ( i − 1 ) ∗ 10 + j + 1 ) + P ∗ ∑ j = 1 10 ∑ k = 1 10 ( 3 L ( j − 1 ) ∗ 10 + k + 30 + L j + 10 L k + 20 − 2 L k + 20 L ( j − 1 ) ∗ 10 + k + 30 − 2 L j + 10 L ( j − 1 ) ∗ 10 + k + 30 ) \min M = -\sum_{i=1}^{10} \sum_{j=1}^{10}\sum_{k=1}^{10} (L_i*L_{(j-1)*10+k+30} ) * (T_{i,j,k} * (R * (1 - H_{i,j,k}) -H_{i,j,k})) + P * \sum_{i=1}^{3} ((\sum_{j=1}^{10} L_{(i-1)*10+j})^2 - 2 \sum_{j=1}^{10} L_{(i-1)*10+j} + 1)+P*\sum_{j=1}^{10}\sum_{k=1}^{10}(3L_{(j-1)*10+k+30} + L_{j+10}L_{k+20}-2L_{k+20}L_{(j-1)*10+k+30}-2L_{j+10}L_{(j-1)*10+k+30}) minM=−∑i=110∑j=110∑k=110(Li∗L(j−1)∗10+k+30)∗(Ti,j,k∗(R∗(1−Hi,j,k)−Hi,j,k))+P∗∑i=13((∑j=110L(i−1)∗10+j)2−2∑j=110L(i−1)∗10+j+1)+P∗∑j=110∑k=110(3L(j−1)∗10+k+30+Lj+10Lk+20−2Lk+20L(j−1)∗10+k+30−2Lj+10L(j−1)∗10+k+30)
然后,可以将上面的表达式转换为 QUBO 矩阵的形式。具体来说,可以定义一个 130 × 130 130 \times 130 130×130 的矩阵 Q Q Q,其中 Q Q Q 的元素为:
最终的结果是分别选择前三张卡的第 8,1,2 个阈值,最终的收入为27914.8。
# 导入需要的库
import re
import numpy as np
import pandas as pd
from openjij import SQASampler
# from dwave.system import DWaveSampler, LeapHybridSampler, EmbeddingComposite
from pyqubo import Array, Binary
# 读取信用卡数据
data = pd.read_csv("附件1:data_100.csv", header=0)
t = data.iloc[:, ::2].values.T # 通过率
h = data.iloc[:, 1::2].values.T # 坏账率
print(t.shape, h.shape) # 行对应卡,列对应阈值
def get_result(solution):
'''
返回结果并打印索引及结果
'''
# x, y 用于存储下标
x, y = [], []
i = 0
pattern_idx = re.compile(r"[0-9]+")
for key, value in solution.items():
if len(key) < 10:
if value == 1 and i < 3:
print(key, value)
x_idx, y_idx = pattern_idx.findall(key)
print(f"第{int(x_idx)+1}张卡选择第{int(y_idx)+1}个阈值")
x.append(int(x_idx))
y.append(int(y_idx))
i += 1
# T, H 分别表示总通过率和总坏账率
T = t[x[0], y[0]] * t[x[1], y[1]] * t[x[2], y[2]]
H = 1 / 3 * (h[x[0], y[0]] + h[x[1], y[1]] + h[x[2], y[2]])
result = C * T * (R * (1 - H) - H)
print(f"result\n")
return result
# 定义常数和变量
C = 1000000 # 总贷款资金
R = 0.08 # 利率
n = 100 # 信用评分卡数量
P = C # 惩罚系数
# 定义目标函数
y = Array.create('y', shape=(3, 10), vartype='BINARY')
z = Array.create('z', shape=(10, 10), vartype='BINARY')
Q = 0
for i in range(10):
for j in range(10):
for k in range(10):
T_ijk = t[0, i] * t[1, j] * t[2, k]
H_ijk = (h[0, i] + h[1, j] + h[2, k]) / 3
Q += y[0, i] * z[j, k] * T_ijk * (C * R * (1 - H_ijk) - C * H_ijk)
c1 = P * sum((sum(y[i]) - 1)**2 for i in range(3))
c2 = 0
for j in range(10):
for k in range(10):
c2 += P * (3 * z[j, k] - 2 * z[j, k] * y[1, j] -
2 * z[j, k] * y[2, k] + y[1, j] * y[2, k])
model = -Q + c1 + c2
model = model.compile()
qubo, offset = model.to_qubo()
best_result = 0
results = []
print("因为输出不稳定,所以写了个循环代码进行结果的验证,你可以修改SQASampler来得到更稳定的结果(每次运行大概需要一分半)\n")
for k in range(20):
# sampler = LeapHybridSampler() # 使用 LeapHybridSampler,如果没有api的话就用下面那行
sampler = SQASampler(
num_sweeps=3000,
num_reads=5000) # num_sweeps每个温度的扫描次数,num_reads指重复模拟退火算法的次数
response = sampler.sample_qubo(qubo, ) # 采样 QUBO
solution = response.first.sample
print(f"第{k+1}次运行结果:")
result = get_result(solution)
results.append(result)
if result > best_result:
best_result = result
best_solution = solution
print("最优解:")
get_result(best_solution)
# 单次运行结果(注释上面循环部分,再取消注释下面)
# sampler = SQASampler(num_sweeps=3000, num_reads=5000) # num_sweeps每个温度的扫描次数,num_reads指重复模拟退火算法的次数
# response = sampler.sample_qubo(qubo, ) # 采样 QUBO
# solution = response.first.sample
# get_result(solution)
需要注意的是,代码中模拟量子退火的解是不稳定的,在我最近一次的试运行中,10 次中输出最优解的次数为 7 次。