整数规划--指派问题

在生活中经常遇到这样的问题,某单位需完成n项任务,恰好有n个人可承担这些任务。由于每人的专长不同,各人完成任务不同(或 所费时间),效率也不同。于是产生应指派哪个人去完成哪项任务 ,使完成n项任务的总效率最高(或所需总时间最小)。

• 这类问题称为指派问题或分派问题(assignment problem)。

• 例:有一份中文说明书,需译成英、日、德、俄5种文字。分 别记作A、B、C、D、E。现有甲、乙、丙、丁、戊5人。他们将中文说明书翻译成不同语种的说明书所需时间如下表所示。问应指派何人去完成何工作,使所需总时间最少?
在这里插入图片描述

• 对应每个指派问题,需有类似上表那样的数表,称为效率矩阵或系数矩阵,其元素 𝑐𝑖𝑗>0(𝑖,𝑗 = 1,2,…,𝑛)表示指 派第𝑖人去完成第𝑗项任务时的效率(或时间、成本等)。

指派问题的解法:
第一步:使指派问题的系数矩阵经变换,在各行各列中都出现0 元素。
• (1) 从系数矩阵的每行元素减去该行的最小元素;
• (2) 再从所得系数矩阵的每列元素中减去该列的最小元素。
• 若某行(列)已有0元素,那就不必再减了。

第二步:进行试指派,以寻求最优解。为此,按以下步骤进行。
• 经第一步变换后,系数矩阵中每行每列都已有了0元素;但需找 出𝑛个独立的0元素。若能找出,就以这些独立0元素对应解矩阵 (𝑥𝑖𝑗)中的元素为1,其余为0,这就得到最优解。
• 当𝑛较小时,可用观察法、试探法去找出𝑛个独立0元素。
• 若𝑛较大时,就必须按一定的步骤去找,常用的步骤为:
• (1) 从只有一个0元素的行(列)开始,给这个0元素加圈,记作◎ 。这表示对这行所代表的人,只有一种任务可指派。然后划去◎ 所在列(行)的其他0元素,记作Φ。这表示这列所代表的任务已指派完,不必再考虑别人了。
• (2) 给只有一个0元素列(行)的0元素加圈,记作◎;然后划去◎ 所在行的0元素,记作Φ。
• (3) 反复进行(1),(2)两步,直到所有0元素都被圈出和划掉为止。
• (4) 若仍有没有划圈的0元素,且同行(列)的0元素至少有两个( 表示对这个可以从两项任务中指派其一)。这可用不同的方案去 试探。 • 从剩有0元素最少的行(列)开始,比较这行各0元素所在列中0元 素的数目,选择0元素少的那列的这个0元素加圈(表示选择性多 的要“礼让”选择性少的)。然后划掉同行同列的其他0元素。可反复进行,直到所有0元素都已圈出和划掉为止。
• (5) 若◎元素的数目𝑚等于矩阵的阶数𝑛,那么这指派问题的最优解已得到。若𝑚<𝑛,则转入下一步。

第三步(𝑚 < 𝑛时的处理办法):作最少的直线覆盖所有0元素,以 确定该系数矩阵中能找到最多的独立元素数。为此按以下步骤进 行:
• (1) 对没有◎的行打√号;
• (2) 对已打√号的行中所有含Φ元素的列打√号;
• (3) 再对打有√号的列中含◎元素的行打√号;
• (4) 重复(2),(3)直到得不出新的打√号的行、列为止。
• (5) 对没有打√号的行画一横线,有打√号的列画一纵线,这就 得到覆盖所有0元素的最少直线数。
• 令这直线数为𝑙。若𝑙<𝑛,说明必须再变换当前的系数矩阵,才能找到𝑛个独立的0元素,为此需要转第四步:若l=n,而m<n, 应回到第二步(4),另行试探。

第四步:
• 对矩阵进行变换的目的是增加0元素。
• 为此,在没有被直线覆盖的部分中找出最小元素,然后在打√行 各元素中都减去这最小元素,而在打√列的各元素都加上这最小 元素,以保证原来0元素不变。
• 这样得到新系数矩阵(它的最优解和原问题相同)。若得到𝑛个独 立的0元素,则已得最优解,否则回到第三步重复进行。

#指派问题
import numpy as np
from collections import Counter

def getSimMat(effMat):            #简化效率矩阵
    for i in range(effMat.shape[0]):
        if 0 in effMat[i]:
            continue
        else:
            matList = effMat[i].tolist()
            effMat[i] -= min(matList)

    for i in range(effMat.shape[1]):
        minVal = float("inf")
        for j in range(effMat.shape[0]):
            if effMat[j][i] < minVal:
                minVal = effMat[j][i]
                if minVal == 0:
                    break
        for k in range(effMat.shape[0]):
            effMat[k][i] -= minVal

def circleOneZero(effMat):
    while 0 in effMat:
        judge = False
        for i in range(effMat.shape[0]):
            zeroCount, zeroColumn = 0, -1
            for j in range(effMat.shape[1]):
                if effMat[i][j] == 0:
                    zeroCount += 1
                    zeroColumn = j
            if zeroCount == 1:
                effMat[i][zeroColumn] = -1
                for k in range(effMat.shape[0]):
                    if k != i:
                        if effMat[k][zeroColumn] == 0:
                            effMat[k][zeroColumn] = -1000
                judge = True
            else:
                continue

        for i in range(effMat.shape[1]):
            zeroCount, zeroRow = 0, -1
            for j in range(effMat.shape[0]):
                if effMat[j][i] == 0:
                    zeroCount += 1
                    zeroRow = j
            if zeroCount == 1:
                effMat[zeroRow][i] = -1
                for k in range(len(effMat[zeroRow])):
                    if k != i:
                        if effMat[zeroRow][k] == 0:
                            effMat[zeroRow][k] = -1000
                judge = True
            else:
                continue
        if judge is False:
            break
    return judge

def circleTwoZero(effMat):
    maxCount = float("inf")
    leastZeroRow = -1
    leastZeroColumn = -1
    zeroColumnList = []
    for i in range(effMat.shape[0]):
        count = Counter(effMat[i])
        zeroCount = count[0]
        if zeroCount != 0:
            if zeroCount < maxCount:
                maxCount = zeroCount
                leastZeroRow = i
    for i in range(effMat.shape[1]):
        if effMat[leastZeroRow][i] == 0:
            zeroColumnList.append(i)
    zeroCount = 0
    maxCount = float("inf")
    for j in zeroColumnList:
        for i in range(effMat.shape[0]):
            if effMat[i][j] == 0:
                zeroCount += 1
        if zeroCount < maxCount:
            maxCount = zeroCount
            leastZeroColumn = j
    effMat[leastZeroRow][leastZeroColumn] = -1
    for i in range(effMat.shape[1]):
        if effMat[leastZeroRow][i] == 0:
            effMat[leastZeroRow][i] = -1000
    for i in range(effMat.shape[0]):
        if effMat[i][leastZeroColumn] == 0:
            effMat[i][leastZeroColumn] = -1000

def circle(effMat):             #画圈
    while 0 in effMat:
        judge = circleOneZero(effMat)
        if judge is False:
            circleTwoZero(effMat)
    else:
        if np.sum(effMat == -1) == effMat.shape[0]:
            pass
        else:
            mark(effMat)

def mark(effMat):
    markRow = []
    markColumn = []
    for i in range(effMat.shape[0]):
        if -1 not in effMat[i]:
            markRow.append(i)
            # if -1000 in effMat[i]:
            #     markColumn.append(effMat[i].tolist().index(-1000))
    for markR in markRow:
        for j in range(effMat.shape[1]):
            if effMat[markR][j] == -1000:
                markColumn.append(j)
    for markC in markColumn:
        for i in range(effMat.shape[0]):
            if effMat[i][markC] == -1:
                markRow.append(i)

    l = effMat.shape[0] - len(markRow) + len(markColumn)

    if l < effMat.shape[0]:
        minVal = float("inf")
        for i in range(effMat.shape[0]):
            if i in markRow:
                for j in range(effMat.shape[1]):
                    if j not in markColumn:
                        if effMat[i][j] < minVal:
                            minVal = effMat[i][j]
            else:
                continue

        for mc in markRow:
            effMat[mc] -= minVal

        for j in range(effMat.shape[1]):
            if j in markColumn:
                for i in range(effMat.shape[0]):
                    effMat[i][j] += minVal

        effMat[effMat == -1] = 0
        effMat[effMat == -1000] = 0

        judgeNew = circleOneZero(effMat)
        circleTwoZero(effMat)

        x = []
        if 0 in effMat:
            for effMati in effMat:
                if 0 in effMati:
                    for i in range(len(effMati)):
                        if -1 in effMati:
                            if effMati[i] == 0:
                                effMati[i] = -1000
                        else:
                            if effMati[i] == 0:
                                for j in range(effMat.shape[0]):
                                    x.append(effMat[j][i])
                                if -1 in x:
                                    effMati[i] = -1000
                                else:
                                    effMati[i] = -1

        bol = effMat == -1
        num = effMat[bol]
        if num.size == effMat.shape[0]:
            pass
        else:
            circle(effMat)

if __name__ == '__main__':
    efficiencyMat = np.array([[12,7,9,7,9],[8,9,6,6,6],[7,17,12,14,9],[15,14,6,6,10],[4,10,7,10,9]])
    getSimMat(efficiencyMat)
    circle(efficiencyMat)
    mark(efficiencyMat)
    efficiencyMat[efficiencyMat != -1] = 0
    efficiencyMat[efficiencyMat == -1] = 1
    print(efficiencyMat)
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值