如何将枚举算法应用到问题中?

How to apply an enumeration algorithm into a problem?

我是 Whosebug 的新手,我想知道是否有合适的 method/algorithm 来解决我的示例问题,以便生成符合问题某些条件的所需解决方案。这是我的 json 文件(example1.json),您可以从这里找到:https://gitlab.com/Schrodinger168/practice/-/blob/master/example1.json

我将解释 csv 文件和我想要生成的内容如下:

对于资源有例如Ressources_XX这里可以是从 1 到 9 的值。所有 Ressource_X 都有它们的 minmax 值需要注意的是,对于所有 Ressources_X.

季节冬天夏天...并且每个数字对应于那些季节指的是天例如冬季定义从第1天到第13天,其他季节表示相同。

对于干预措施:是否必须计划任务。有Intervention_X,相应的工作量包含他们必须使用的Ressources_XDeltaIntervention_X 的持续时间,例如我们可以在 Intervention_625[=86 中看到=] 作为 Delta 列表中的第一个值(第 1 天)是 1.0 这意味着如果 Intervention_625 从第 1 天开始,它需要在同一天(第 1 天)结束,因为第 1 天的 Delta 值为 1.0 等等......每个 Intervention_X 有自己的 tmax 例如 Intervention_625 tmax 等于 17 这意味着它可以在我们希望生成的第 1 天和第 17 天之间开始,另一个 tmax的其他Intervention_X意思相同

对于排除有一些例外,例如:

"E141["Intervention_315","Intervention_456","summer"]表示Intervention_315Intervention_456不能在夏季同一天开始(夏季可以不同天开始),其他意思相同。

T这里是总时长T = 17表示整个作品的时长是17天

我想要生成的解决方案是干预的名称和每个干预的开始日期 (tx,ty..) 例如:

Intervention_X 发送

Intervention_Y ty

依此类推...直到所有干预都被使用,因为它们只会使用一次。

解决方案将确保它遵守每天使用资源(最小值、最大值)的条件排除 ,并且完成日期最多可以是 T + 1

如果需要,这是我的代码,用于访问我的 json 文件的每个节点和值:

class access_json:

    def __init__(self):
        self._resources = dict()
        self._seasons = dict()
        self._interventions = dict()
        self._exclusions = dict()
        
        self._NRESOURCES = 0
        self._NINTERVENTIONS = 0
        self._list_resources = list()
        self._list_interventions = list()
        
        self._T = 0
        
    def loadjson(self, fpath: str):
        js = dict()
        with open(fpath, 'r') as f:
            js = json.load(f)
        
        self._resources = js["Resources"]
        self._NRESOURCES = len(self._resources)
        self._list_resources = list(self._resources.keys())
        self._seasons = js["Seasons"]
        self._interventions = js["Interventions"]
        self._NINTERVENTIONS = len(self._interventions)
        self._list_interventions = list(self._interventions.keys())
        self._exclusions = js["Exclusions"]
        
        self._T = js["T"]

为了解决问题,我尝试每天使用排列来交换和替换那些干预措施,但这样做似乎很长,而且没有生成解决方案。任何有关代码的帮助,以及有关如何应用该算法解决此问题的建议,我们将不胜感激。先谢谢你!

这可以使用 backtracking algorithm 快速解决。特别是,使用规定的数据可以在毫秒内解决。

代码

def overlap(x, y):
    '''
        Checks if two time segments overlap
    '''
    a, b = x
    c, d = y
    return a <= d and b >= c

def allocate(data, ans = None):
    '''
         Intervention schedule allocation based upon backtracking
    '''
    if ans is None:
        ans = {}
    
    if len(ans) == len(data["Interventions"]):
        # handled all the interventions
        yield ans
        
    # Check next intervention
    for intervention_name, intervention in data["Interventions"].items():
        if intervention_name not in ans:
            # will add items not in ans
            # last day intervention can be run
            last_day = min(data["T"], int(intervention["tmax"]))  
            
            for day in range(1, last_day+1):
                str_day = str(day)
                
                # different days intervention could be run
                delta = intervention["Delta"][day-1]  # number of days it takes to run
                
                if day + delta <= last_day + 1: 
                    # must finish within last day
                    # Check that there are sufficient resources
                    resource_usage = {}
                
                        
                    missing_resources = False
                    for resource_name, v in intervention["workload"].items():
                        if not str_day in v:
                            missing_resources = True
                            break
                            #raise Exception(f'{intervention_name} resources {k} missing day {str_day}')
                        usage_key = (resource_name, day)  # pair: (resource name, day) as key
                        resource_usage[usage_key] = v[str_day][str_day]
                    if missing_resources:
                        continue # one resource has no resources for a day
                    
                    # Resources needed by items in list for this day
                    for ans_name, ans_response in ans.items():
                        ans_day = ans_response["Day"]
                        ans_delta = ans_response["Delta"]
                        
                        if overlap([day, day+delta-1], [ans_day, ans_day+ans_delta-1]):
                            # An intervention in answer overlaps with current intervention
                            for resource_name, v_resource in ans_response["Resources"].items():
                                for overlap_day in range(day, day+int(delta)):
                                    # Get resource usage for each day of overlap
                                    usage_key = (resource_name, overlap_day)
                                    if not usage_key in resource_usage:
                                        resource_usage[usage_key] = 0
                                    resource_usage[usage_key] += v_resource
                    
                    # Check resource less than max
                    resources = data["Resources"]
                    for usage_key, value in resource_usage.items():
                        resource_name, overlap_day = usage_key  # values from tuple
                        if value > resources[resource_name]["max"][day-1]: # max value for resoure for this day
                            break
                    else:
                        # Resource didn't exceed max
                        # Check on Exclusion
                        winter = data["Seasons"]['winter']
                        summer = data["Seasons"]['summer']
                        isa = data["Seasons"]['is']
                        if str_day in winter:
                            season = "winter"
                        elif str_day in summer:
                            season = "summer"
                        elif str_day in isa:
                            season = "is"
                        else:
                            season = ""
                            
                        exclusions = data["Exclusions"]
                        bExclude = False
                        for k, v_lst in exclusions.items():
                            if season in v_lst and intervention_name in v_lst:
                                for intervention_k, ans_response in ans.items():
                                    if intervention_k in v_lst:
                                        ans_day = ans_response["Day"]
                                        ans_delta = ans_response["Delta"]
                                        if overlap([day, day+delta-1], [ans_day, ans_day+ans_delta-1]):
                                            bExclude = True
                                            break
                                if bExclude:
                                    break
                        if not bExclude:
                            # Resources used
                            response = {"Day": day, "Delta": intervention["Delta"][day-1]}
                            resources = {k:v[str_day][str_day] for k, v in intervention["workload"].items()}
                            response['Resources'] = resources
                            ans[intervention_name] = response
                            
                            yield from allocate(data, ans)
                            
                            # Remove last entry so we can try next iteration
                            del ans[intervention_name]   

                           

用法

使用发布的数据集

# Loading data from local file
with open('resource_allocation/example1.json', 'r') as f:
    data = json.load(f)                          
       
# Get first solution using from allocate generator                 
answer = next(allocate(data), None)   
if answer:
    for name, intervention in answer.items():
        print(f'Intervention {name}')
        print(f'\tStart {intervention["Day"]}, Duration: {intervention["Delta"]}')
        resources = intervention["Resources"]
        for k, v in resources.items():
            print(f'\tResource: {k} Usage: {v}')
        print()

输出

Intervention Intervention_625
    Start 1, Duration: 1.0
    Resource: Ressources_2 Usage: 0.14
    Resource: Ressources_4 Usage: 0.14
    Resource: Ressources_7 Usage: 0.14
    Resource: Ressources_8 Usage: 0.056
    Resource: Ressources_9 Usage: 0.014

Intervention Intervention_31
    Start 1, Duration: 2.0
    Resource: Ressources_3 Usage: 0.86
    Resource: Ressources_4 Usage: 0.86
    Resource: Ressources_9 Usage: 0.086

Intervention Intervention_40
    Start 1, Duration: 1.0
    Resource: Ressources_2 Usage: 0.29
    Resource: Ressources_7 Usage: 0.29
    Resource: Ressources_9 Usage: 0.029

Intervention Intervention_224
    Start 1, Duration: 1.0
    Resource: Ressources_1 Usage: 0.71
    Resource: Ressources_9 Usage: 0.071

Intervention Intervention_702
    Start 1, Duration: 1.0
    Resource: Ressources_2 Usage: 0.43
    Resource: Ressources_9 Usage: 0.043

Intervention Intervention_19
    Start 1, Duration: 2.0
    Resource: Ressources_4 Usage: 0.86
    Resource: Ressources_8 Usage: 0.344
    Resource: Ressources_9 Usage: 0.086

Intervention Intervention_672
    Start 1, Duration: 1.0
    Resource: Ressources_3 Usage: 0.43
    Resource: Ressources_9 Usage: 0.043

Intervention Intervention_579
    Start 1, Duration: 1.0
    Resource: Ressources_8 Usage: 0.028
    Resource: Ressources_9 Usage: 0.014

Intervention Intervention_627
    Start 1, Duration: 2.0
    Resource: Ressources_2 Usage: 0.86
    Resource: Ressources_7 Usage: 0.86
    Resource: Ressources_8 Usage: 0.344
    Resource: Ressources_9 Usage: 0.086

Intervention Intervention_456
    Start 1, Duration: 1.0
    Resource: Ressources_4 Usage: 0.14
    Resource: Ressources_9 Usage: 0.014

Intervention Intervention_90
    Start 1, Duration: 1.0
    Resource: Ressources_2 Usage: 0.14
    Resource: Ressources_9 Usage: 0.014

Intervention Intervention_315
    Start 2, Duration: 1.0
    Resource: Ressources_4 Usage: 0.14
    Resource: Ressources_6 Usage: 0.14
    Resource: Ressources_7 Usage: 0.14
    Resource: Ressources_9 Usage: 0.014

Intervention Intervention_356
    Start 1, Duration: 1.0
    Resource: Ressources_7 Usage: 0.57
    Resource: Ressources_9 Usage: 0.057

Intervention Intervention_535
    Start 1, Duration: 1.0
    Resource: Ressources_3 Usage: 0.29
    Resource: Ressources_7 Usage: 0.29
    Resource: Ressources_9 Usage: 0.029

Intervention Intervention_595
    Start 1, Duration: 1.0
    Resource: Ressources_2 Usage: 0.14
    Resource: Ressources_9 Usage: 0.014

Intervention Intervention_592
    Start 1, Duration: 1.0
    Resource: Ressources_2 Usage: 0.14
    Resource: Ressources_8 Usage: 0.056
    Resource: Ressources_9 Usage: 0.014

Intervention Intervention_536
    Start 1, Duration: 1.0
    Resource: Ressources_3 Usage: 0.14
    Resource: Ressources_7 Usage: 0.14
    Resource: Ressources_9 Usage: 0.014

Intervention Intervention_150
    Start 1, Duration: 1.0
    Resource: Ressources_2 Usage: 0.14
    Resource: Ressources_6 Usage: 0.14
    Resource: Ressources_9 Usage: 0.014

说明

伪代码

def allocate(data, ans = None):
    # initize answer if not initialized
    if ans is None:
        ans = {} # dictionary of interventions with name, day and resources used
        
    # Base case (check if solution found)
    if len(ans) == len(data["Interventions"]):
        yield ans  # let me know if you're not familiar with generators
        
    # Try remaining interventions (i.e. ones not in answer ans)
    #   Loop over all interventions
    for intervention_name, intervention in data["Interventions"].items():
        if intervention_name not in ans:
            # This intervention not in answer ans
            # Found out what day to run the intervention
            # last day to run
            last_day = min(data["T"], int(intervention["tmax"]))  
            
            for day in range(1, last_day+1):
                # Range of days to try running the intervention
                # make sure intervention finishes before last day
                delta = intervention["Delta"][day-1]  # number of days it takes to run
                if day + delta <= last_day + 1: 
                    # Check if intervention satisfies constraints if we add it on this day
                    
                    # 1--check sufficient resources
                    # logic for checking if suffient resources
                    # set sufficient_resources to True if sufficient, False otherwise
                    if sufficient_resources:
                        # 2--check if intervention compatible with others in list
                        #     i.e. can run on same day as others in ans
                        # set compatible to True if not excluded by others in list, False otherwise
                        if compatible:
                            # add intervention to ans for this day with the resources it uses on this day
                            # we call this response
                            
                            # Add intervention for this day
                            ans[intervention_name] = response
                            
                            # now recursive try other interventions
                            # This recursives tries other interventions
                            # by now successively trying the next intervention which is not in ans on a compatibl day
                            yield from allocate(data, and)

                            del ans[intervention_name] # remove last entry
        

伪代码--non-generator函数(但只求第一个解)

def allocate(data, ans = None):
    # initize answer if not initialized
    if ans is None:
        ans = {} # dictionary of interventions with name, day and resources used
        
    # Base case (check if solution found)
    if len(ans) == len(data["Interventions"]):
        return True  # let me know if you're not familiar with generators
        
    # Try remaining interventions (i.e. ones not in answer ans)
    #   Loop over all interventions
    for intervention_name, intervention in data["Interventions"].items():
        if intervention_name not in ans:
            # This intervention not in answer ans
            # Found out what day to run the intervention
            # last day to run
            last_day = min(data["T"], int(intervention["tmax"]))  
            
            for day in range(1, last_day+1):
                # Range of days to try running the intervention
                # make sure intervention finishes before last day
                delta = intervention["Delta"][day-1]  # number of days it takes to run
                if day + delta <= last_day + 1: 
                    # Check if intervention satisfies constraints if we add it on this day
                    
                    # 1--check sufficient resources
                    # logic for checking if suffient resources
                    # set sufficient_resources to True if sufficient, False otherwise
                    if sufficient_resources:
                        # 2--check if intervention compatible with others in list
                        #     i.e. can run on same day as others in ans
                        # set compatible to True if not excluded by others in list, False otherwise
                        if compatible:
                            # add intervention to ans for this day with the resources it uses on this day
                            # we call this response
                            
                            # Add intervention for this day
                            ans[intervention_name] = response
                            
                            # now recursive try other interventions
                            # This recursives tries other interventions
                            # by now successively trying the next intervention which is not in ans on a compatibl day
                            if allocate(data, ans):
                                # Path forward found a solution
                                return True
                            else:
                                # Backtrack--this day won't work
                                # remove intervention since won't work for this day
                                del ans[intervention_name]   

Non-Generator版本

def overlap(x, y):
    '''
        Checks if two time segments overlap
    '''
    a, b = x
    c, d = y
    return a <= d and b >= c

def allocate2(data, ans = None):
    '''
         Intervention schedule allocation based upon backtracking
    '''
    if ans is None:
        ans = {}
    
    if len(ans) == len(data["Interventions"]):
        # handled all the interventions
        return ans
        
    # Check next intervention
    for intervention_name, intervention in data["Interventions"].items():
        if intervention_name not in ans:
            # will add items not in ans
            # last day intervention can be run
            last_day = min(data["T"], int(intervention["tmax"]))  
            
            for day in range(1, last_day+1):
                str_day = str(day)
                
                # different days intervention could be run
                delta = intervention["Delta"][day-1]  # number of days it takes to run
                
                if day + delta <= last_day + 1: 
                    # must finish within last day
                    # Check that there are sufficient resources
                    resource_usage = {}
                
                        
                    missing_resources = False
                    for resource_name, v in intervention["workload"].items():
                        if not str_day in v:
                            missing_resources = True
                            break
                            #raise Exception(f'{intervention_name} resources {k} missing day {str_day}')
                        usage_key = (resource_name, day)  # pair: (resource name, day) as key
                        resource_usage[usage_key] = v[str_day][str_day]
                    if missing_resources:
                        continue # one resource has no resources for a day
                    
                    # Resources needed by items in list for this day
                    for ans_name, ans_response in ans.items():
                        ans_day = ans_response["Day"]
                        ans_delta = ans_response["Delta"]
                        
                        if overlap([day, day+delta-1], [ans_day, ans_day+ans_delta-1]):
                            # An intervention in answer overlaps with current intervention
                            for resource_name, v_resource in ans_response["Resources"].items():
                                for overlap_day in range(day, day+int(delta)):
                                    # Get resource usage for each day of overlap
                                    usage_key = (resource_name, overlap_day)
                                    if not usage_key in resource_usage:
                                        resource_usage[usage_key] = 0
                                    resource_usage[usage_key] += v_resource
                    
                    # Check resource less than max
                    resources = data["Resources"]
                    for usage_key, value in resource_usage.items():
                        resource_name, overlap_day = usage_key  # values from tuple
                        if value > resources[resource_name]["max"][day-1]: # max value for resoure for this day
                            break
                    else:
                        # Resource didn't exceed max
                        # Check on Exclusion
                        winter = data["Seasons"]['winter']
                        summer = data["Seasons"]['summer']
                        isa = data["Seasons"]['is']
                        if str_day in winter:
                            season = "winter"
                        elif str_day in summer:
                            season = "summer"
                        elif str_day in isa:
                            season = "is"
                        else:
                            season = ""
                            
                        exclusions = data["Exclusions"]
                        bExclude = False
                        for k, v_lst in exclusions.items():
                            if season in v_lst and intervention_name in v_lst:
                                for intervention_k, ans_response in ans.items():
                                    if intervention_k in v_lst:
                                        ans_day = ans_response["Day"]
                                        ans_delta = ans_response["Delta"]
                                        if overlap([day, day+delta-1], [ans_day, ans_day+ans_delta-1]):
                                            bExclude = True
                                            break
                                if bExclude:
                                    break
                        if not bExclude:
                            # Resources used
                            response = {"Day": day, "Delta": intervention["Delta"][day-1]}
                            resources = {k:v[str_day][str_day] for k, v in intervention["workload"].items()}
                            response['Resources'] = resources
                            ans[intervention_name] = response
                            
                            result = allocate2(data, ans)
                            if result:
                                # was able to find an answer going forward using intervention
                                # on this day,so return the result
                                return result
                            # Remove last entry so we can try next iteration
                            del ans[intervention_name]
    

用法

answer = allocate2(data)