秘密圣诞老人 - 生成“有效”排列

发布于 2024-12-22 22:11:54 字数 325 浏览 5 评论 0 原文

我的朋友邀请我回家玩秘密圣诞老人的游戏,我们应该在那里画很多画和画画。为小组中的朋友扮演“圣诞老人”的角色。

所以,我们写下所有的名字并随机选择一个名字。如果我们中的任何一个人最终选择了自己的名字,那么我们就会重新洗牌并重新选择名字(理由是一个人不能成为自己的圣诞老人)。

我们有七个人在玩,所以我认为最终的“圣诞老人分配”是 (1:7) 自身的排列,有一些限制。

我想就如何使用 Mathematica 或任何编程语言甚至算法来提出各种想法:

  • 列出/打印所有“有效”圣诞老人分配
  • 随着玩“秘密圣诞老人”的朋友数量的增长,可扩展

My friends invited me home to play the game of Secret Santa, where we are supposed to draw a lot & play the role of 'Santa' for a friend in the group.

So, we write all our names and pick a name randomly. If any of us ends up having their own name picked, then we reshuffle and pick names all over again (the rationale being that one can not be one's own Santa).

There are seven of us while playing so I thought of the final 'Santa-allocation' as a permutation of (1:7) onto itself, with some restrictions.

I would like to invite various ideas about how we could use Mathematica in particular or any programming language or even an algorithm to:

  • List/print out ALL the 'valid' Santa-allocations
  • Is scalable as the number of friends playing 'Secret Santa' grows

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

泪眸﹌ 2024-12-29 22:11:54

你要找的东西叫做混乱(另一个可爱的拉丁词,比如放血和出窗外)。

所有排列中属于混乱的排列比例接近 1/e = 大约 36.8%——因此,如果您生成随机排列,只需继续生成它们,您就很有可能在 5 或 10 个选项中找到一个。随机排列。 (在 5 个随机排列中找不到一个的几率为 10.1%,每增加 5 个排列,未发现混乱的几率就会降低 10 倍)

这个演示非常实际,给出了直接生成混乱的递归算法,而不是而不是必须拒绝不是混乱的排列。

What you're looking for is called a derangement (another lovely Latinate word to know, like exsanguination and defenestration).

The fraction of all permutations which are derangements approaches 1/e = approx 36.8% -- so if you are generating random permutations, just keep generating them, and there's a very high probability that you'll find one within 5 or 10 selections of a random permutation. (10.1% chance of not finding one within 5 random permutations, every additional 5 permutations lowers the chance of not finding a derangement by another factor of 10)

This presentation is pretty down-to-earth and gives a recursive algorithm for generating derangements directly, rather than having to reject permutations that aren't derangements.

对风讲故事 2024-12-29 22:11:54

我建议:

f[s_List] := Pick[#, Inner[SameQ, #, s, Nor]] & @ Permutations@s

f @ Range @ 4
{{2, 1, 4, 3}, {2, 3, 4, 1}, {2, 4, 1, 3}, {3, 1, 4, 2}, {3, 4, 1, 2},
 {3, 4, 2, 1}, {4, 1, 2, 3}, {4, 3, 1, 2}, {4, 3, 2, 1}}

这比 Heike 的函数快得多。

f @ Range @ 9; //Timing
secretSanta[9]; //Timing
{0.483, Null}
{1.482, Null}

忽略代码的透明度,这仍然可以快几倍:

f2[n_Integer] := With[{s = Range@n},
    # ~Extract~ 
       SparseArray[Times@@BitXor[s, #] & /@ #]["NonzeroPositions"] & @ Permutations@s
  ]

f2[9]; //Timing
{0.162, Null}

I propose this:

f[s_List] := Pick[#, Inner[SameQ, #, s, Nor]] & @ Permutations@s

f @ Range @ 4
{{2, 1, 4, 3}, {2, 3, 4, 1}, {2, 4, 1, 3}, {3, 1, 4, 2}, {3, 4, 1, 2},
 {3, 4, 2, 1}, {4, 1, 2, 3}, {4, 3, 1, 2}, {4, 3, 2, 1}}

This is significantly faster than Heike's function.

f @ Range @ 9; //Timing
secretSanta[9]; //Timing
{0.483, Null}
{1.482, Null}

Ignoring transparency of code, this can be made several times faster still:

f2[n_Integer] := With[{s = Range@n},
    # ~Extract~ 
       SparseArray[Times@@BitXor[s, #] & /@ #]["NonzeroPositions"] & @ Permutations@s
  ]

f2[9]; //Timing
{0.162, Null}
鼻尖触碰 2024-12-29 22:11:54

没有元素映射到自身的排列是混乱。随着 n 的增加,紊乱的比例接近常数 1/e。因此,如果随机选择排列,则(平均)需要 e 尝试获得混乱。

维基百科文章包含用于计算小 n 的显式值的表达式。

A permutation that maps no element to itself is a derangement. As n increases, the fraction of derangements approaches the constant 1/e. As such, it takes (on average) e tries to get a derangement, if picking a permutation at random.

The wikipedia article includes expressions for calculating explicit values for small n.

旧城空念 2024-12-29 22:11:54

在 Mathematica 中,您可以执行类似操作

secretSanta[n_] := 
  DeleteCases[Permutations[Range[n]], a_ /; Count[a - Range[n], 0] > 0]

,其中 n 是池中的人数。然后例如 secretSanta[4] 返回

{{2, 1, 4, 3}, {2, 3, 4, 1}, {2, 4, 1, 3}, {3, 1, 4, 2}, {3, 4, 1, 2}, 
  {3, 4, 2, 1}, {4, 1, 2, 3}, {4, 3, 1, 2}, {4, 3, 2, 1}}

Edit

看起来 Mathematica 中的 Combinatorica 包实际上有一个 Derangements 函数,所以你也可以做类似的事情,

Needs["Combinatorica`"]
Derangements[Range[n]]

尽管在我的系统上 Derangements[Range[n]] 比上面的函数慢大约 2 倍。

In Mathematica you could do something like

secretSanta[n_] := 
  DeleteCases[Permutations[Range[n]], a_ /; Count[a - Range[n], 0] > 0]

where n is the number of people in the pool. Then for example secretSanta[4] returns

{{2, 1, 4, 3}, {2, 3, 4, 1}, {2, 4, 1, 3}, {3, 1, 4, 2}, {3, 4, 1, 2}, 
  {3, 4, 2, 1}, {4, 1, 2, 3}, {4, 3, 1, 2}, {4, 3, 2, 1}}

Edit

It looks like the Combinatorica package in Mathematica actually has a Derangements function, so you could also do something like

Needs["Combinatorica`"]
Derangements[Range[n]]

although on my system Derangements[Range[n]] is about a factor 2 slower than the function above.

绝不放开 2024-12-29 22:11:54

这并没有回答你关于计算有效混乱的问题,但它提供了一种算法来生成具有以下属性的混乱(这可能是你想要的):

  1. 它保证圣诞老人的关系中有一个循环(如果你在 4 玩) ,你最终不会得到 2 对圣诞老人夫妇 --> 2 个周期),
  2. 即使有大量玩家,它也能有效地工作,
  3. 如果应用得当,没有人知道谁是圣诞老人,
  4. 它不需要计算机,只需要一些纸。

这里的算法是:

  • 每个玩家将她/他的名字写在信封上,并将她/他的名字放在信封内的折叠纸上。
  • 一名值得信赖的玩家(对于上面的属性#3)拿起所有信封,并看着其背面(没有写名字)将它们洗牌。
  • 一旦信封洗得足够好,并始终看着背面,受信任的玩家就会将每个信封中的纸张移至下一个信封。
  • 再次打乱信封后,信封会被分发回信封上有名字的玩家,每个玩家都是信封上有名字的人的圣诞老人。

This does not answer your question about counting the valid derangements, but it gives an algorithm to generate one (which might be what you want) with the following properties:

  1. it guaranties that there is a single cycle in Santa's relationship (if you play at 4, you do not end up with 2 Santa couples --> 2 cycles),
  2. it works efficiently even with very large number of player,
  3. if applied fairly, nobody knows whose who Santa's,
  4. it does not need a computer, only some paper.

Here the algorithm:

  • Every player writes her/his name on an envelope and puts her/his name in a folded paper in the envelope.
  • One trusted player (for property # 3 above) takes all the envelopes and shuffles them looking at their back side (where no name is written).
  • Once the envelops are shuffled well enough, always looking at the back side, the trusted player moves the paper in each envelope to the following one.
  • After shuffling the envelops again, the envelopes are distributed back to the player whose name is on them, and each player is the Santa of the person whose name is in the envelope.
情话难免假 2024-12-29 22:11:54

我在文档中发现了内置的 Subfactorial 函数,并更改了其中一个示例以生成:

Remove[teleSecretSanta];
teleSecretSanta[dims_Integer] :=
 With[{spec = Range[dims]},
  With[{
    perms = Permutations[spec],
    casesToDelete = DiagonalMatrix[spec] /. {0 -> _}},
   DeleteCases[perms, Alternatives @@ casesToDelete]
   ]
  ]

可以使用 Subfactorial 来检查该函数。

Length[teleSecretSanta[4]] == Subfactorial[4]

正如 Mr.Wizard 的回答一样,我怀疑可以通过 SparseArray 优化 teleSecretSanta 。不过,我现在喝得太醉了,无法尝试这种恶作剧。 (开玩笑……我其实是太懒太笨了。)

I came across the built-in Subfactorial function in the documentation and altered one of the examples to produce:

Remove[teleSecretSanta];
teleSecretSanta[dims_Integer] :=
 With[{spec = Range[dims]},
  With[{
    perms = Permutations[spec],
    casesToDelete = DiagonalMatrix[spec] /. {0 -> _}},
   DeleteCases[perms, Alternatives @@ casesToDelete]
   ]
  ]

One can use Subfactorial to check the function.

Length[teleSecretSanta[4]] == Subfactorial[4]

As in Mr.Wizard's answer, I suspect teleSecretSanta can be optimized via SparseArray. However, I'm too drunk at the moment to attempt such shenanigans. (kidding... I'm actually too lazy and stupid.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文