我有一个问题要解决。
问题陈述:
我已经设置了大约5000家商店,其中包含geo-coordinates.Every商店必须访问一次,以便从商店收集订单收件人。
这将由销售员完成。
我想生成N个优化的旅程计划,这样每个推销员都能够覆盖最大数量。 8小时内的商店。
即在旅行计划中,访问所有商店所需的时间不应超过8小时。
并且没有两个推销员应该访问同一家商店。
即一旦访问了商店,就不应该再次访问另一个旅程计划。
在这种情况下,产生的旅程计划数量间接等于否。需要推销员。
需要最终结果:
尽量减少号码。旅程计划涵盖所有商店
我有什么:
商店到商店的距离矩阵。每个商店之间的距离覆盖和时间
Challange : 我没有关于推销员的任何信息(我没有他们的地理位置),因此很难选择每个旅程计划的起点。
我最初的想法:
我正在考虑通过群集将商店划分到不同的区域。然后为每个群集形成旅程计划(针对优化路线)。
我将在python中开发它。
关于尝试和处理此类问题的最佳方法的任何想法。
答案 0 :(得分:0)
这是一个启发式的想法:
s1
和s2
之间最短的未覆盖链接,时间成本为t
。a1
和a2
,使他们的路径在s1
和s2
开始或结束。如果您找不到这样的对(因为商店已经在现有路径的中间,或者s1
和s2
是一个推销员路径的开始和结束),请丢弃该链接并转到回到2。t_sum
为a1
花费的时间总和,a2
和t
所花费的时间。如果t_sum
小于每个推销员可以花费的最长时间T
(8小时),请丢弃该链接并返回2. a1
和a2
合并为一名推销员。这个过程会根据具体情况稍微改变一下,但它应该是相当微不足道的。例如,如果a1
在s1
结束而a2
从s2
开始,则您可以连接其路径,但如果a1
从s1
开始并且a2
从s2
开始,然后您必须(例如)反转a1
的路径,然后将它们连接起来。新推销员的花费时间为t_sum
。这是基本的想法。有一些空间可以决定使用哪些数据结构,影响您查找下一个最短链接的方式,查找a1
和a2
的方式以及如何跟踪已覆盖/丢弃的链接。您可能还包括许多改进或优化:
a1
和a2
后,您可以放弃s1
和s2
的所有链接,除非它们是a1
路径中的唯一元素}或a2
。例如,如果我查看商店1
和2
之间的链接,我正在合并路径[1, 3, 4]
和[2]
(导致[2, 1, 3, 4]
或{ {1}}),我可以删除商店[4, 3, 1, 2]
的所有剩余链接,因为它现在位于路径的中间,但不是来自1
,因为它仍然位于合并的一端路径,因此可以连接。2
和a1
时,如果a2
,那么此推销员将无法再进一步合并(因为每个剩余链接的成本至少为T - t_sum < t
}),所以你可以&#34;关闭&#34;路径,意味着你可以丢弃每端的链接,如果这可以节省你的时间(取决于具体的实现),你可以在下一步的执行中搜索t
和a1
3。a2
与任何当前&#34;的剩余时间之间的差异。活性&#34;代理小于上一个值T
,&#34; close&#34;如果是这样的话。编辑:
这是一个Python概念证明。我避免使用NumPy或任何其他外部软件包,虽然因为算法大多是迭代的,所以我不确定你能不能更快地使用它。
t
该函数返回一个元组列表,其中包含商店ID的路径和旅程的总时间。这是一个测试:
def make_journeys(dists, max_dist):
n = len(dists)
ds = sorted((dists[i][j], i, j) for i in range(n) for j in range(i + 1, n))
starts = list(range(n))
ends = list(range(n))
salesmen = [([i], 0) for i in range(n)]
for d, i, j in ds:
if starts[i] is not None and starts[j] is not None:
si, sj = starts[i], starts[j]
reverse_i, reverse_j = True, False
elif starts[i] is not None and ends[j] is not None:
si, sj = starts[i], ends[j]
reverse_i, reverse_j = True, True
elif ends[i] is not None and starts[j] is not None:
si, sj = ends[i], starts[j]
reverse_i, reverse_j = False, False
elif ends[i] is not None and ends[j] is not None:
si, sj = ends[i], ends[j]
reverse_i, reverse_j = False, True
else:
continue
if si == sj:
continue
(pi, di) = salesmen[si]
(pj, dj) = salesmen[sj]
dt = d + di + dj
if dt > max_dist:
continue
starts[pi[0]] = None
ends[pi[-1]] = None
starts[pj[0]] = None
ends[pj[-1]] = None
if reverse_i:
pi = list(reversed(pi))
if reverse_j:
pj = list(reversed(pj))
pt = pi + pj
starts[pt[0]] = si
ends[pt[-1]] = si
salesmen[si] = (pt, dt)
salesmen[sj] = None
return [s for s in salesmen if s]
输出:
def random_symmetric(N, min, max):
# Build a random symmetric matrix
dists = [[random.randint(1, 9) if i != j else 0 for j in range(N)] for i in range(N)]
return [[(dists[i][j] + dists[j][i]) // 2 for j in range(N)] for i in range(N)]
random.seed(100)
# This takes long!
distances = random_symmetric(5000, 1, 9)
max_distance = 100
journeys = make_journeys(distances, max_distance)
print('{} journeys computed.'.format(len(journeys)))
在上面的测试中,生成随机距离矩阵需要相当长的时间(使用NumPy会更快)。对50 journeys computed.
的调用在我的电脑中花费了大约16秒。显然是YMMV,但它没有运行数小时或数分钟。