Algorithm 桌子座位,使用什么算法?
所以我有这个问题,我需要找出什么样的算法可以解决它 给定一组人,并且给定该组中谁不喜欢谁,在圆桌会议上为所有人安排一个座位,以便没有人坐在他们不喜欢的人旁边(如果可能)Algorithm 桌子座位,使用什么算法?,algorithm,language-agnostic,computer-science,Algorithm,Language Agnostic,Computer Science,所以我有这个问题,我需要找出什么样的算法可以解决它 给定一组人,并且给定该组中谁不喜欢谁,在圆桌会议上为所有人安排一个座位,以便没有人坐在他们不喜欢的人旁边(如果可能) 我就是不知道该怎么做。基本上我把它画成一个有向图,节点代表一个人,边从不喜欢的人到不喜欢的人。然后,要将节点排列成一个圆,以便相邻的两个节点之间没有边。然而,我找不到解决这类问题的算法。为了让你的陈述更容易一些,假设你“反转”了你的图形,这样当且仅当两位客人可以坐在一起时,两位客人之间就有了边 现在,您要寻找的是一个循环(闭合行
我就是不知道该怎么做。基本上我把它画成一个有向图,节点代表一个人,边从不喜欢的人到不喜欢的人。然后,要将节点排列成一个圆,以便相邻的两个节点之间没有边。然而,我找不到解决这类问题的算法。为了让你的陈述更容易一些,假设你“反转”了你的图形,这样当且仅当两位客人可以坐在一起时,两位客人之间就有了边 现在,您要寻找的是一个循环(闭合行走),该循环恰好包含每个顶点。这叫做哈密顿循环。一般来说,这是NP难的(你的问题也是如此,因为哈密顿循环的任何实例都可以简化为你的问题),但在某些条件下,这更容易
遗传算法(如JasonC提到的)可以解决大多数问题,整数线性规划是一种选择,约束规划也是。老实说,我会选择某种退火算法。基本上:
:将一个人映射到他们不能坐在旁边的人列表的字典敌人
:一种座位安排,以表格周围姓名列表的形式排列。第一个和最后一个元素是相邻的座位,因为它是一个圆桌表格
def tablePenalty (enemies, table):
penalty = 0
for k, name in enumerate(table):
p = (k + len(table) - 1) % len(table)
n = (k + 1) % len(table)
if table[p] in enemies[name]:
penalty = penalty + 1
if table[n] in enemies[name]:
penalty = penalty + 1
return penalty
现在我们有了它,我们可以实现一个非常简单的搜索,它只是不断地随机交换用户,直到找到满意的东西。这将以姓名列表作为输入,并生成按座位顺序排列的姓名列表作为输出:
def findGoodSeating1 (names):
table = names[:]
while tablePenalty(enemies, table) > 0:
i, j = random.sample(range(0, len(table)), 2)
table[i], table[j] = table[j], table[i]
return table
当然,这不是一个很好的算法。但它有时是有效的。如果找不到解决方案(或者如果你运气不好),它会挂起,并且它可以随机采取非常迂回的路线来达到解决方案。事实上,它基本上与搜索座位安排的所有扰动相同,只是它以随机顺序进行搜索,有时可以重新检查重复的安排。是的,不太好
因此,我们可以做一个改进:只有在座位安排得到改善的情况下,才能进行交换:
def findGoodSeating2 (names):
table = names[:]
penalty = tablePenalty(enemies, table)
while penalty > 0:
newtable = table[:]
i, j = random.sample(range(0, len(table)), 2)
newtable[i], newtable[j] = newtable[j], newtable[i]
newpenalty = tablePenalty(enemies, newtable)
if newpenalty <= penalty: # Keep improvements.
table = newtable
penalty = newpenalty
return table
由于算法是随机的,我们最终得到3种不同但令人满意的解决方案
现在有一些关于上述算法的重要说明:
- 你可以调整阈值之类的,你真的只需要实验
- 如果没有解决办法,一切都将悬而不决。即使有解决方案,所有这些都有随机花费很长时间的风险,但这种可能性很低,对于真实的数据集,您可能永远不会注意到
- 阻止它挂起的方法很简单,我并不是为了简洁而实现它的:只需将算法限制在最大迭代次数。如果您这样做:
- 对于第一个变量,您的结果实际上是随机的
- 对于第二种变体,您的结果将是迄今为止最好的
- 对于第三个变体,您可能希望跟踪总体最佳状态,因为在随机洗牌之后,您可能会达到迭代极限,并丢失关于更早期初始状态的信息
- 这些算法并不理想——它们很简单。对于这个应用程序,它们的性能足够好。这是重要的部分
- 你可能只想通过,比方说,用随机位置交换坐在敌人旁边的人来进行实验。这可能会提高性能,也可能不会提高性能
无论如何,这并不是为了证明什么是最好的算法,只是为了给你一些关于如何从更模糊的方法开始的想法。就我个人而言,其中之一就是我的方法,因为虽然你当然可以找到一个基于图形的解决方案(或者甚至是直接搜索——本质上类似于本例中基于约束的搜索——将一个人放在他们旁边,当你碰到一个糟糕的安排时,返回并尝试下一个扰动),如果您这样做,速度肯定会非常快,但这很容易。另请参见和。首先想到的是一个模拟退火/遗传算法,这也是大家的共识(即使是最基本的算法:从随机安排开始,交换两个随机的人-交换是否有所改善?如果是这样,保持不变,不要这样做。重复,直到满意为止),或者如果你的客人名单足够小,可以在合理的时间内完成,通过所有唯一的组合进行简单的暴力搜索。@JasonC不,这是一个不同的问题。婚礼桌的问题要求对客人进行分区,而在这里,你要找的是单桌座位。对我来说,所有这些方法都在随机搜索和退火方法之间。遗传算法不仅应该基于重组,还应该基于种群。所以你会有一个群体,比如说,50个解决方案,通过1)组合(交叉)和2)交换(变异)创造200个后代,然后选择最好的50个来形成你的新群体。遗传算法框架中有自由度,但如果没有解决方案和选择的组合,它就不是遗传算法。@BastianJ我改变了这个词。这是一个粗心的错误。不用担心,我只是想让OP知道GA可能更复杂一点,但也值得一看
def findGoodSeating3 (names):
table = names[:]
penalty = tablePenalty(enemies, table)
stuck = 0
while penalty > 0:
newtable = table[:]
i, j = random.sample(range(0, len(table)), 2)
newtable[i], newtable[j] = newtable[j], newtable[i]
newpenalty = tablePenalty(enemies, newtable)
stuck = stuck + 1
if newpenalty < penalty:
table = newtable
penalty = newpenalty
stuck = 0
elif stuck > 50: # An arbitrary threshold.
random.shuffle(table)
penalty = tablePenalty(enemies, table)
stuck = 0
return table
import random;
# A list of names to test with.
names = [ 'Clarke', 'Octavia', 'Bellamy', 'Jaha', 'Murphy', 'Finn',
'Abby', 'Alie', 'Lexa', 'Kane', 'Lincoln' ]
# Build list of people each person can't sit next to.
enemies = {}
for name in names:
choices = [ x for x in names if x != name ]
enemies[name] = random.sample(choices, 3) # 3 enemies per person
print "%s: %s" % (name, enemies[name])
#-------------------------------------------------------------------
def tablePenalty (enemies, table):
penalty = 0
for k, name in enumerate(table):
p = (k + len(table) - 1) % len(table)
n = (k + 1) % len(table)
if table[p] in enemies[name]:
penalty = penalty + 1
if table[n] in enemies[name]:
penalty = penalty + 1
return penalty
def findGoodSeating1 (names):
table = names[:]
while tablePenalty(enemies, table) > 0:
i, j = random.sample(range(0, len(table)), 2)
table[i], table[j] = table[j], table[i]
return table
def findGoodSeating2 (names):
table = names[:]
penalty = tablePenalty(enemies, table)
while penalty > 0:
newtable = table[:]
i, j = random.sample(range(0, len(table)), 2)
newtable[i], newtable[j] = newtable[j], newtable[i]
newpenalty = tablePenalty(enemies, newtable)
if newpenalty <= penalty:
table = newtable
penalty = newpenalty
return table
def findGoodSeating3 (names):
table = names[:]
penalty = tablePenalty(enemies, table)
stuck = 0
while penalty > 0:
newtable = table[:]
i, j = random.sample(range(0, len(table)), 2)
newtable[i], newtable[j] = newtable[j], newtable[i]
newpenalty = tablePenalty(enemies, newtable)
stuck = stuck + 1
if newpenalty < penalty:
table = newtable
penalty = newpenalty
stuck = 0
elif stuck > 3 * len(table):
random.shuffle(table)
penalty = tablePenalty(enemies, table)
stuck = 0
return table
# Test them:
print findGoodSeating1(names)
print findGoodSeating2(names)
print findGoodSeating3(names)
Clarke: ['Bellamy', 'Lincoln', 'Octavia']
Octavia: ['Jaha', 'Abby', 'Bellamy']
Bellamy: ['Clarke', 'Abby', 'Alie']
Jaha: ['Finn', 'Lincoln', 'Alie']
Murphy: ['Octavia', 'Jaha', 'Lexa']
Finn: ['Lexa', 'Clarke', 'Alie']
Abby: ['Lexa', 'Clarke', 'Jaha']
Alie: ['Clarke', 'Kane', 'Lincoln']
Lexa: ['Murphy', 'Alie', 'Finn']
Kane: ['Lexa', 'Alie', 'Bellamy']
Lincoln: ['Murphy', 'Clarke', 'Octavia']
['Octavia', 'Lexa', 'Jaha', 'Bellamy', 'Murphy', 'Clarke', 'Kane', 'Finn', 'Lincoln', 'Abby', 'Alie']
['Clarke', 'Jaha', 'Bellamy', 'Lexa', 'Octavia', 'Alie', 'Abby', 'Finn', 'Lincoln', 'Kane', 'Murphy']
['Murphy', 'Clarke', 'Jaha', 'Lexa', 'Bellamy', 'Lincoln', 'Kane', 'Octavia', 'Finn', 'Abby', 'Alie']