有许多参数的问题,其中一些被指出(4-8工具),但他们中的一些不(有多少个站点,在那里只有一个锤)。例如,可以通过将“工具”定义为“间隔= X和角度= Z的锤子”来简化。
我会从制作某种逆向地图开始。将工具集转换为作业或将工作站/工具转换为作业。
工具集到工作映射将允许您说“对于给定的工具集,我可以处理哪些工作”。添加一个函数来说明两个工具集是否兼容(即除了没有工具的工作站相同),另一个用于生成组合工具集作为两个兼容工具集的总和。在您的示例中,作业1和5具有兼容的工具集,就像作业2和3以及总和和4一样。然后您可以通过安装兼容工具集的组合来处理兼容工具集集合中的所有作业。
groups = {}
for job in jobs:
x = ['________' for i in range(num_stations)]
for tool in jobs[job]:
x[jobs[job][tool][station]] = tool
y = ','.join(x)
if y not in groups:
groups[y] = [job]
else:
groups[y].append(job)
站到工具的工作映射将允许你说“这个站,如果我安装该工具哪些工作将与帮助。”
stations = []
for station in range(num_stat):
stations.append({})
for job_name in sorted(jobs.keys()):
for tool in sorted(jobs[job_name].keys()):
pos = jobs[job_name][tool][station]
if tool not in stations[pos]:
stations[pos][tool] = set()
stations[pos][tool].add(job_name)
总之,这里是一个黑客位在一起的:
#!/usr/bin/env python
# vim: tabstop=8 softtabstop=4 shiftwidth=4 nocindent smartindent
import random
import traceback, sys
Verbose = False
random.seed(4)
num_loops = 500 # Number of times to run the job-scheduling loop
num_stat = 6 # Number of stations where tool can be loaded
min_tools = 2 # lowest tool count for a job
max_tools = 4 # highest tool count for a job
num_posts = 3 # Number of stations a tool can be mounted
num_jobs = 5000 # Number of simulated jobs
num_tools = 6 # Number of different tool/offset/angle combinations
# Setup the tools database
# tool_names is a list of the names of the available tools (with offset and angle)
# tools is a map giving a list of positions where the tool can be loaded
tool_names = sorted(['tool%03d' % idx for idx in range(num_tools)])
tool_fmap = {}
tool_rmap = {}
for tool in tool_names:
tool_fmap[tool] = sorted(random.sample(xrange(num_stat), num_posts))
for pos in tool_fmap[tool]:
if pos not in tool_rmap:
tool_rmap[pos] = set()
tool_rmap[pos].add(tool)
for tool in sorted(tool_fmap.keys()):
print "%-8s" % tool, sorted(tool_fmap[tool])
for pos in sorted(tool_rmap.keys()):
print "%2d" % pos, sorted(tool_rmap[pos])
# Build the jobs and stations database
def make_jobs_database():
jobs = {}
distro = [0 for i in range(num_stat + 1)]
short_count = [0 for i in range(num_stat + 1)]
for job_num in range(num_jobs):
job_name = "job%04d" % job_num
jobs[job_name] = {}
num_tools_in_job = random.randrange(min_tools, max_tools + 1)
t_list = random.sample(tool_names, num_stat)
available_positions = range(num_stat)
num_posns_allocated = 0
for tool in t_list:
possible_positions = [x for x in tool_fmap[tool] if x in available_positions]
if len(possible_positions) == 0:
continue
jobs[job_name][tool] = random.choice(possible_positions)
available_positions.remove(jobs[job_name][tool])
num_posns_allocated += 1
if num_posns_allocated >= num_tools_in_job:
break
if len(jobs[job_name].keys()) < num_tools_in_job:
short_count[len(jobs[job_name].keys())] += 1
distro[len(jobs[job_name].keys())] += 1
print "Shorts:", short_count
for idx, cnt in enumerate(distro):
print "Jobs with %d tools = %d" % (idx, distro[idx])
return jobs
def make_station_database(jobs):
stations = []
for station in range(num_stat):
stations.append({})
for job_name in sorted(jobs.keys()):
for tool in sorted(jobs[job_name].keys()):
pos = jobs[job_name][tool]
if tool not in stations[pos]:
stations[pos][tool] = set()
stations[pos][tool].add(job_name)
return stations
def make_group_database(jobs):
groups = {}
for job_name in sorted(jobs.keys()):
x = ['_______' for i in range(num_stat)]
for tool in sorted(jobs[job_name].keys()):
pos = jobs[job_name][tool]
x[pos] = tool
z = ",".join(x)
if z not in groups:
groups[z] = []
groups[z].append(job_name)
return groups
def show_jobs_database(jobs):
print "Jobs Database:"
for job_name in sorted(jobs.keys()):
x = ['_______' for i in range(num_stat)]
for tool in sorted(jobs[job_name].keys()):
pos = jobs[job_name][tool]
x[pos] = tool
z = ",".join(x)
print job_name, z
def show_station_database(stations):
print "Station Database:"
for idx, stat in enumerate(stations):
print idx
for tool in sorted(stat.keys()):
print ' ', tool, sorted(stat[tool])
def show_group_database(groups):
print "Groups Database:"
for group in sorted(groups.keys()):
print group, groups[group]
def prune_station_database(stations, done_jobs):
for pos in range(num_stat):
empty_tools = []
for tool in stations[pos]:
stations[pos][tool].difference_update(done_jobs)
if len(stations[pos][tool]) == 0:
empty_tools.append(tool)
for tool in empty_tools:
del stations[pos][tool]
def make_selection(stations, all_possible_jobs):
global tools_used, posns_used
selections = []
tools_used = {}
posns_used = {}
possible_jobs = all_possible_jobs.copy()
for i in range(num_stat): # select a station to populate
target_posn = None
target_tool = None
target_len = 0
for pos in range(num_stat): # inspect each station
if pos in posns_used:
continue
key_len = 0
key_pos = None
key_tool = None
for tool in stations[pos]: # inspect each tool at this station
if tool in tools_used:
continue
new_len = len(possible_jobs.intersection(stations[pos][tool]))
if new_len > key_len:
key_len = new_len
key_pos = pos
key_tool = tool
if key_len > target_len:
target_len = key_len
target_posn = key_pos
target_tool = key_tool
if target_tool is not None:
tools_used[target_tool] = target_posn
if target_posn is not None:
posns_used[target_posn] = target_tool
possible_jobs.intersection_update(stations[target_posn][target_tool])
selections.append((target_posn, target_tool))
if Verbose:
print "Jobs:", len(possible_jobs), sorted(list(possible_jobs))
if not Verbose: # print it at least once
print "Jobs:", len(possible_jobs), sorted(list(possible_jobs))
# We have all the tools we need, see if there are more we can use
for pos in range(num_stat):
extra_jobs = 0
best_tool = None
if pos not in posns_used:
new_posns = {pos:None}
for p in posns_used:
new_posns[p] = posns_used[p]
for tool in stations[pos]:
if tool in tools_used:
continue
key_count = 0
new_posns[pos] = tool
for job in stations[pos][tool]:
if job_is_ok(job, None, new_posns):
key_count += 1
if key_count > extra_jobs:
extra_jobs = key_count
best_tool = tool
tools_used[best_tool] = pos
posns_used[pos] = best_tool
selections.append((pos, best_tool))
print "Tools:", tools_used
print "Stations:", posns_used
print "Selections:", selections
def toolset_compatible(x, y):
if len(x) != len(y):
return False
for idx in range(len(x)):
if x[idx] == y[idx]:
continue
if x[idx] == '' and y[idx] not in x:
continue
if y[idx] == '' and x[idx] not in y:
continue
return False
if Verbose:
print "Compatible:"
print x
print y
return True
def toolset_combine(x, y):
z = []
for idx, xval in enumerate(x):
if xval == '':
z.append(y[idx])
else:
z.append(xval)
if Verbose:
print "Combined:"
print x
print y
print z
return z
def job_is_ok(job, tools_used, posns_used):
for tool in jobs[job]:
pos = jobs[job][tool]
if pos not in posns_used:
pass
elif posns_used[pos] != tool:
return False
return True
def job_is_on(job, tools_used):
for tool in jobs[job]:
pos = jobs[job][tool]
if tool not in tools_used:
return False
if tools_used[tool] != pos:
return False
return True
def use_stations(stations):
all_possible_jobs = set(jobs.keys())
try:
for run in range(num_loops):
make_selection(stations, all_possible_jobs)
jobs_on = set()
for job in all_possible_jobs:
if job_is_on(job, tools_used):
jobs_on.add(job)
print "JobsOn:", len(jobs_on), sorted(jobs_on)
prune_station_database(stations, jobs_on)
count_before = len(all_possible_jobs)
all_possible_jobs.difference_update(set(jobs_on))
count_after = len(all_possible_jobs)
print "Counts: run = %d, before = %d, after = %d" % (run + 1, count_before, count_after)
if count_before == count_after:
break
if count_after == 0:
break
except Exception, err:
print traceback.format_exc()
print "Tools:", tools_used
print "Stations:", posns_used
show_station_database(stations)
def use_groups(groups):
all_possible_jobs = set(jobs.keys())
try:
for run in range(num_loops):
selected_group = None
group_len = 0
for group in groups.keys():
if len(groups[group]) > group_len:
group_len = len(groups[group])
selected_group = group
if Verbose:
print "Selected:", selected_group, groups[selected_group]
selected_groups = [selected_group]
toolset = [x.strip('_') for x in selected_group.split(',')]
for group in groups.keys():
if group in selected_groups:
continue
new_toolset = [x.strip('_') for x in group.split(',')]
if toolset_compatible(toolset, new_toolset):
toolset = toolset_combine(toolset, new_toolset)
selected_groups.append(group)
jobs_on = set()
tools_used = {}
for idx, tool in enumerate(toolset):
if tool != '':
tools_used[tool] = idx
print "Tools:", tools_used
for job in all_possible_jobs:
if job_is_on(job, tools_used):
jobs_on.add(job)
print "JobsOn:", len(jobs_on), sorted(jobs_on)
for group in selected_groups:
if group in groups:
del groups[group]
count_before = len(all_possible_jobs)
all_possible_jobs.difference_update(set(jobs_on))
count_after = len(all_possible_jobs)
print "Counts: run = %d, before = %d, after = %d" % (run + 1, count_before, count_after)
if count_before == count_after:
break
if count_after == 0:
break
except Exception, err:
print traceback.format_exc()
print "Tools:", tools_used
print "Selected:", selected_groups
show_group_database(groups)
def main_program():
global jobs, stations, groups
jobs = make_jobs_database()
if Verbose:
show_jobs_database(jobs)
groups = make_group_database(jobs)
if Verbose:
show_group_database(groups)
stations = make_station_database(jobs)
if Verbose:
show_station_database(stations)
if True:
use_groups(groups)
else:
use_stations(stations)
if __name__ == "__main__":
import cProfile
cProfile.run('main_program()')
您可以尝试通过改变真到假的main_program底部的两种可供选择的方法。您可以调整顶部的主要参数。您可以通过在顶部将verbose设置为true来打印大量输出。运行需要几秒钟,其中很大一部分是建立作业数据库。
我对这个有趣的问题没有现成的答案,但我的第一个想法是查看**聚类**;也许可以根据所使用的工具来定义作业之间的某种距离/相似度,并使用聚类规则进行操作,直到满足您的需求为止? –
为指针欢呼。我会看看集群:) – ashgetstazered
我一直在盯着你的工作分组示例的最后10分钟,你分组他们的方式似乎不符合上面解释的规则集......它是可能我错过了这一观点,但也许是因为你对这个问题的熟悉程度,你忽略了一些用于隔离工作的规则/专业知识? –