关于算法:如何确定谁可以在圣诞节给谁送礼物?

How can I figure out who can give gifts to whom on Christmas?

假设有一个七口之家,

1
["John","James","Jenna","Joseph","Jane","Jacob","Joanne"]

他们都在为圣诞节送礼物的季节做准备。他们商定了一些规则,以确保一切顺利进行:

  • 每个人都必须送一件礼物。
  • 每个人都必须收到一份礼物。
  • 没有人可以给自己送礼物。
  • 没有人可以给他的配偶送礼物。(简和约翰是配偶)
  • 没有人可以给他去年给过的那个人。(去年,约翰给了杰姆斯,杰姆斯给Jenna,Jenna给约瑟夫,约瑟夫给了约瑟夫,给了丫,给了丫,给了丫)
  • 最后,两个人不能互相赠送礼物。例如,如果约翰给了珍娜,珍娜可能不会还给约翰。

这些规则虽然复杂,但在遵守这些规则的同时,很难找出谁能给予谁。因此,他们雇我来编写一个程序,展示人们可以给予彼此的所有可能的法律途径。

我可以用什么算法优雅地解决这个问题?


我将使用一个简单的回溯算法。使用python生成器函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def calc_gifts(names, blacklist, gifts={}):
    if len(names) > 0:
        name, rest = names[0], names[1:]
        for other in names + list(gifts):
            if (other != name and
                    other not in blacklist[name] and
                    (other not in gifts or gifts[other] != name) and
                    other not in gifts.values()):

                gifts_new = dict(gifts.items() + [(name, other)])
                for solution in calc_gifts(rest, blacklist, gifts_new):
                    yield solution
    else:
        yield gifts

现在,我们设置名称和黑名单,让生成器生成一个解决方案:

1
2
3
4
5
6
7
8
9
10
all_names = ["john","james","jenna","joseph","jane","jacob","joanne"]
blacklist = {"john":   ["james","jane"],
            "james":  ["jenna"],
            "jenna":  ["joseph"],
            "joseph": ["jane"],
            "jane":   ["jacob","john"],
            "jacob":  ["joanne"],
            "joanne": ["john"]}
generator = calc_gifts(all_names, blacklist)
solution = next(generator)

那么,solution就是赠予者和受赠者的dict,例如{'joanne': 'joseph', 'james': 'john', 'jane': 'joanne', 'joseph': 'jacob', 'jacob': 'jane', 'john': 'jenna', 'jenna': 'james'}

对于第一个解决方案,即使用next(generator),仅调用10次calc_gifts;对于所有224个解决方案,例如使用list(generator),调用约1000次。


如果您从7x7网格开始,每个人都有一行和一列,指示该行中提到的人是否可以向该列中提到的人赠送礼物。

首先,将每个组合标记为允许的,然后开始删除约束3, 4和5明确禁止的组合。每一个有效的礼物组合必须是你在这一点上留下的一个子集。这将是你的起始位置。

现在你必须开始做决定,每一个决定都会影响到你留下的可能性。有些决定可能是错误的,最终并不是每个人都能得到礼物。在这种情况下,您应该收回那个决定,然后尝试另一个(提示:为此使用递归)。

如果你以一种结构化的方式尝试所有的可能性,你一定会找到所有的解决方案,如果它们存在的话。

现在,让它成为他们的钱的价值:)