如何在python中找到一组不同列元素的最低总和?

Shi*_*il 6 python python-3.x pandas

问题陈述:有 5 个项目和 15 名员工,每列的数字表示每个员工对给定项目的兴趣。每个项目最多可以有 3 名员工。分数是从 1-5 1 是最高偏好,5 是最低偏好。我必须以最少的人不满意或最低分数的方式在项目中分配员工。请注意,我的算法创建了所有可能的组合,然后用 sum 升序对这些组合进行排序,并挑选出具有不同员工的前 5 个组合。

但这就是问题所在,例如,我的 sortedsum 矩阵是 [1,1,1,4,9,9,...] 现在这在算法上是正确的,但问题是如果我选择其中的前 5 个我的总和将是 16。但是如果我将 [2,1,1,4] 作为前四个,那么第五个项目团队的总和可能会代替 [1,1,1,4,9]到 3,这样最小值就会改变,这就是我的算法失败的地方。

我有一个 3nXn 矩阵,在本例中,我将其视为 15x5:所以矩阵看起来像这样(https://i.stack.imgur.com/omcq7.png):

df = pd.read_csv(io.StringIO("""

employee  proj_A  proj_B  proj_C  proj_D  proj_E
      A1       1       5       3       4       2
      B1       5       4       1       2       3
      C1       2       3       4       1       5
      A2       4       2       1       3       5
      B2       4       5       3       2       1
      C2       3       1       2       5       4
      A3       1       2       4       3       5
      B3       2       3       1       5       4
      C3       5       3       4       1       2
      A4       4       5       3       2       1
      B4       5       3       4       2       1
      C4       1       2       3       4       5
      A5       1       3       2       5       4
      B5       2       1       3       5       4
      C5       2       1       4       5       4

      """), sep=r"\s+")
Run Code Online (Sandbox Code Playgroud)

[格式化以方便粘贴到shell中]

我要解决的问题是在每列中选择三个不同的元素,从不同的行中选择不同的含义,以使所有 5 列的总和保持最少。

例如,这里如果我为 A 选择 A1、B1、C1,然后为 B 选择 A2、B2、C2 等等,那么总和为 A 为 1+5+2=8,B 为 2+5+1=8以此类推,即 8+8+... 应该是所有可能组合的最小总和。请注意,如果将 A1、B1 和 C1 分配给 A,则它们无法切换到 B 或任何其他下一列。

我尝试的是创建从 A1、B1、C1 到 A5、B5 和 C5 的所有可能组合,并计算它们的总和并按递增顺序对其进行排序,然后选择具有不同元素的前五个,如下所示:

我的代码的局限性: 1. 我正在优化的矩阵(这是一个 30x10 矩阵)花费了太多时间,因为组合太多了。2.它会忽略任何通过将初始元素的分数妥协到更高一点,我们可能会得到可以减少很多的中间分数的情况。

import pandas as pd
data=pd.read_csv("csvfile.csv")
teamsize=3
employes=data["Name"]
PScore=[]
for i in range(10):
    PScore.append(data[f"Project {i+1}"])
Scorings_combo=[]
for i in range(len(employes)):
    for j in range(len(employes)):
        for k in range(len(employes)):
            for l in range(10):
                if i==j or j==k or k==i:
                    break
                score=0
                score=score+PScore[l][i]+PScore[l][j]+PScore[l][k]
                Scorings_combo.append([i+1,j+1,k+1,l+1,score])
a=[Scorings_combo[i][4]for i in range(len(Scorings_combo))]
#b=sorted(a,reverse=True)
b=sorted(a)
emps=[]
sig=1
empl=[]
passigned=[]
countee=0
for i in range(len(b)):
    for j in range(3):
        if Scorings_combo[a.index(b[i])][j] in emps or Scorings_combo[a.index(b[i])][3] in passigned:
            a[a.index(b[i])]=-1
            sig=0
            break
    if sig!=0:
        print("New")
        for k in range(3):emps.append(Scorings_combo[a.index(b[i])][k])
        empl.append(Scorings_combo[a.index(b[i])])
        passigned.append(Scorings_combo[a.index(b[i])][3])
        countee=countee+1
        if count==8:
            break
    sig=1
    print(f"Iteration:{i}/{len(b)}")
Run Code Online (Sandbox Code Playgroud)

例如: 3,3,3,4,9 将是解决方案,即使以下是可能的: 4,4,4,4,4 因为它将寻找降序的不同元素,这给了我第一个解决方案。

如果您有任何想法,请帮助我。谢谢

这是数据的驱动器链接:https : //drive.google.com/file/d/1yaswBEi3RzrhQ743hJTnUeZFZNo-QBBR/view?usp=sharing

这是一个更简单的例子: Matrix=[[1,2],[2,1],[1,2],[1,2],[2,1],[1,2]] 现在最小可能的组合是:对于第一列: [1,1,1] 和 [1,1,2] 对于第二列。

Tod*_*odd 2

我想尝试遗传算法,这似乎是一个很好的优化类型问题,可以应用它。有 15 行,可以按任何顺序排列,总共 15 行!排列,或 1.0e+12。尝试所有排列的强力方法是不切实际的。

我有下面的函数来计算人口中个体的“适应度”。分数是平均值和标准差的组合。我的数学可能并不完全正确,而且我肯定会用 numpy 进行即兴发挥,但它似乎确实产生了很好的结果。

def calculate_fitness(population):
    fitness_scores = []

    for individual in population:
        # Group the rows in 3's according to the columns.
        proj_a = individual[  : 3,1]  # First 3 rows, column 1.
        proj_b = individual[ 3: 6,2]  # Next  3 rows, column 2, etc.
        proj_c = individual[ 6: 9,3]
        proj_d = individual[ 9:12,4]
        proj_e = individual[12:15,5]  # Bottom 3 rows, last column.

        arr = np.array([proj_a, proj_b, proj_c, proj_d, proj_e])

        mean = arr.mean()          # Mean.
        std  = np.abs(arr.std())   # Standard deviation.

        # We want both the lowest mean and lowest standard deviation.
        # For simplicity, let's just add them and use that as the score.
        fitness_scores.append(mean + std)

    # Invert and scale the values so they can be used as weights
    # for random selection.
    fitness_scores  = np.array(fitness_scores)
    fitness_scores  = (fitness_scores.max() + .3 ) - fitness_scores
    fitness_scores /= (fitness_scores.max() + .07)
    fitness_scores *= 100

    return fitness_scores
Run Code Online (Sandbox Code Playgroud)

输出 - 前 3 行属于 A,接下来的 3 行属于 B,依此类推:

employee proj_A proj_B proj_C proj_D proj_E
      A3      1      2      4      3      5
      C4      1      2      3      4      5
      A1      1      5      3      4      2
      C2      3      1      2      5      4
      B5      2      1      3      5      4
      C5      2      1      4      5      4
      A2      4      2      1      3      5
      A5      1      3      2      5      4
      B3      2      3      1      5      4
      B1      5      4      1      2      3
      C3      5      3      4      1      2
      C1      2      3      4      1      5
      B2      4      5      3      2      1
      B4      5      3      4      2      1
      A4      4      5      3      2      1
Run Code Online (Sandbox Code Playgroud)

在这个分组中,似乎每个人都非常高兴,这可能是最佳组合。

在这里,除了 A3 得到 3 之外,每个人都对全 1 感到非常满意。

employee proj_A proj_B proj_C proj_D proj_E
      C4      1      _      _      _      _
      A1      1      _      _      _      _
      A5      1      _      _      _      _
      B5      _      1      _      _      _
      C2      _      1      _      _      _
      C5      _      1      _      _      _
      A2      _      _      1      _      _
      B3      _      _      1      _      _
      B1      _      _      1      _      _
      C1      _      _      _      1      _
      A3      _      _      _      3      _
      C3      _      _      _      1      _
      A4      _      _      _      _      1
      B4      _      _      _      _      1
      B2      _      _      _      _      1
Run Code Online (Sandbox Code Playgroud)

我发现,针对高突变率进行调整,并保护前 5 个个体免受突变和死亡的影响,可以大大改善结果。

通过随机选取 4 个人,使用他们的健康得分作为权重来选择父母,以选择较高健康的父母。然后将 4 个中的顶部与任何其他不具有相同适应度分数的进行匹配,以尝试防止近亲繁殖并将种群多样性保持在良好的范围内。

每次迭代,一个个体死亡,两个父母被选择并产生一个孩子,并且以 50% 的概率选择一个个体并通过随机交换其几行来变异。

我发现最好的群体是 150 名成员,并且 1k 到 2k 次迭代似乎得到了一致的结果。