OR-TOOLS - 如何解决有序分配问题?

问题描述

我有“n”张桌子和“m”个盒子。 任务是把所有的盒子堆在桌子上。

问题: 有哪些不同的可能组合? 重要提示:所有的盒子放在桌子上时都是有序的,就像堆叠一样。我需要知道堆栈中每个盒子的排名。

如何使用 ORTOOL 约束编程 / SAT 来实现该问题? 什么是最好的策略?什么变量/约束?

(我不期待代码,但只是建议......除非你是一个快速的开发者:) 谢谢

from ortools.sat.python import cp_model

class VararraySolutionPrinter(cp_model.cpsolverSolutionCallback):

    def __init__(self,variables):
        cp_model.cpsolverSolutionCallback.__init__(self)
        self.__variables = variables
        self.__solution_count = 0

    def on_solution_callback(self):
        self.__solution_count += 1
        for v in self.__variables:
            print('%s=%i' % (v,self.Value(v)))

        print("")

    def solution_count(self):
        return self.__solution_count


def main():

    variables = []
    model = cp_model.CpModel()

    #################################################
    
    nb_tables = 3
    nb_Boxes = 4

    for Box in range(nb_Boxes):
        Box_to_table = model.NewIntvar(0,nb_tables - 1,'Box_'+str(Box)+'_to_table')
        variables.append(Box_to_table)

    ranking_variables = []

    for Box in range(nb_Boxes):
        rank_of_Box_on_its_table = model.NewIntvar(0,nb_Boxes - 1,'rank_of_Box_'+str(Box)+'_on_its_table')
        variables.append(rank_of_Box_on_its_table)
        ranking_variables.append(rank_of_Box_on_its_table)

    # the next line is not good because the ranking is global
    # and not local to each table. how to manage that?
    model.AddAllDifferent(ranking_variables)

    #################################################

    solver = cp_model.cpsolver()
    solution_printer = VararraySolutionPrinter(variables)
    status = solver.SearchForAllSolutions(model,solution_printer)

    print('Status = %s' % solver.StatusName(status))
    print('Number of solutions found: %i' % solution_printer.solution_count())


main()

还有布尔版本:

#################################################
    
    nb_tables = 3
    nb_Boxes = 4

    for Box in range(nb_Boxes):

        this_Box_vars = []

        for table in range(nb_tables):
            Box_in_table = model.NewBoolVar('Box_'+str(Box)+'_in_table_' + str(table))
            variables.append(Box_in_table)
            this_Box_vars.append(Box_in_table)

        model.Add(sum(this_Box_vars) == 1)

    ranking_variables = []

    for Box in range(nb_Boxes):
        rank_of_Box_on_its_table = model.NewIntvar(0,'rank_of_Box_'+str(Box)+'_on_its_table')
        variables.append(rank_of_Box_on_its_table)
        ranking_variables.append(rank_of_Box_on_its_table)

    # the next line is not good because the ranking is global
    # and not local to each table. how to manage that?
    model.AddAllDifferent(ranking_variables)

    #################################################

解决方法

不要使用整数变量。

经验法则:

  • 如果您看到 AllDifferent 约束,请将其删除,并用布尔变量列表替换整数变量。
  • 加总和(bool_vars) == 1

x[i][j][k] 是一个布尔变量,表示框 i 在表 j 上的位置 k。

y[j][k] 表示表 j​​ 中位置 k 处是否有一个框。

每个框只出现一次:

forall i: Sum on j,k box[i][j][k] == 1

每个位置最多被一个盒子占据:

forall j,k: sum on i box[i][k][k] <= 1

如果某个盒子在某处,则表示该某处已被占用:

forall i,j,k: box[i][j][k] implies y[j][k]

如果一个位置被占用,这个位置一定有一个框:

forall j,k: bool_or([y[j][k].Not(),box[0][j][k],..,box[n - 1][j][k]])

表上的位置必须从 0 开始密集占用:

forall j,k (except last position): y[j][k].Not() implies y[j][k + 1].Not()

如果你想要一个盒子的排名

forall i: rank[i] == sum over j,k box[i][j][k] * k