Card Magic Trick,订购卡片的算法

时间:2014-10-12 22:23:38

标签: java algorithm math

我已经在这个问题上工作了很长一段时间,问题如下:

考虑以下“魔术”技巧。你有一副n张牌,标记为1,2,...,n(但不一定按顺序排列)。 然后重复以下过程,直到没有卡片为止:

  • 向公众展示甲板顶部的卡片,并将其从甲板上取下
  • 从甲板顶部取下一张卡片,将其放在甲板底部,不要显示。

你的目标是先前在牌组中订购牌,以便向公众显示的牌顺序递增:1,2,...,n。

例如:
如果n = 5,则从安排1,5,2,4,3开始工作:
1,5,2,4,3 - > 2,4,3,5 - > 3,5,4 - > 4,5 - > 5

问题:编写一个算法,为任意给定数量的卡片打印适当的初始排序。

算法orderCards(n)
输入:整数n
输出:打印正确的卡片订购。

我设法得到以下内容:

n=1 --> 1
n=2 --> 1,2
n=3 --> 1,3,2
n=4 --> 1,3,2,4
n=5 --> 1,5,2,4,3
n=6 --> 1,4,2,6,3,5
n=7 --> 1,6,2,5,3,7,4
n=8 --> 1,5,2,7,3,6,4,8

正如您所看到的,每隔一个数字都有一个模式,例如1,x,2,x,4,x ...... 我也相信算法会有所不同,具体取决于你的n值是偶数还是奇数,但我不太清楚如何。如果n是偶数第二个元素=值(n-2)+ 1

任何建议都将不胜感激!

2 个答案:

答案 0 :(得分:2)

永远不要遵守" play"当涉及到魔法(或基于缩减或基于生成规则的缩减器编写生成器)时的规则:您知道所需的最终顺序,因此通过反向运行技巧来生成开始序列。将所有卡片视为可见,将最后一张卡片放在"空白"甲板,从底部移动到顶部(这当然不会改变最后一张卡的顺序),然​​后将下一张卡放在最顶层。重复直到完成。

所以,让我们从你想要的最终订单开始,最后以"结束。说,1,2,3,4,5。然后:

1,2,3,4,5, put last visible on top of the deck
→ 1,2,3,4 + 5. Then move bottom to top, add last visible card:
→ 1,2,3 + 4,5. Then move bottom to top, add last visible card:
→ 1,2 + 3,(4,5 → 5,4) = 3,5,4. Then move bottom to top, add last visible card:
→ 1 + 2,(3,5,4 → 4,3,5) = 2,4,3,5. Then move bottom to top, add last visible card:
→ 1,(2,4,3,5 → 5,2,4,3) = 1,5,2,4,3.

进行。

如果我们为1,2,3,4,5,6,7,8,9,10运行此操作,我们会分10步获得1,6,2,10,3,7,4,9,5,8

另请注意,此算法为O(n):对于1000张卡的序列,我们执行1000"从下到上移动,然后在顶部添加卡"脚步。我们能改进吗?可能,但这需要查看更长的序列,看看我们是否可以在这产生的排列中找到模式。

我们知道它非常规律,因此可能会有改进 - 例如,使用序列1,2,...,n-1,n我们可以立即完全确定"奇数"卡片中的卡片:1,2,...,n/2(在1 ... 5序列中,数字为1,x,2,x,3,而对于1..10,我们会看到1,x,2,x,3,x,4,x,5,x)。如果我们能够弄清楚其余数字是如何落实到位的,我们可能会找到一个O(1)算法来立即生成甲板序列。但是,略微超出了这个答案的范围。

答案 1 :(得分:1)

从空白卡片开始,并在每个卡片上写下索引(即:顶部的一个为0,下一个为1,依此类推)。执行该技巧,每次向公众展示一张卡片时,请在上面写下您希望他们看到的卡片。

这是执行此操作的代码。 cards存储卡的原始索引,并像操作规则一样被操纵。 values存储每个索引卡上写的数字。诀窍完成后,values存储诀窍的数字。

这不是Java,但这是算法:

def order(n):
    cards = range(n)
    values = [None] * n
    for i in xrange(1, n+1):
        values[cards.pop(0)] = i
        if cards:
            cards.insert(len(cards)-1, cards.pop(0))
    return values

for n in xrange(1, 9):
    print 'n=%d --> %s' % (n, ','.join(map(str, order(n))))

输出:

n=1 --> 1
n=2 --> 1,2
n=3 --> 1,3,2
n=4 --> 1,3,2,4
n=5 --> 1,5,2,4,3
n=6 --> 1,4,2,6,3,5
n=7 --> 1,6,2,5,3,7,4
n=8 --> 1,5,2,7,3,6,4,8

代码比原始魔术更神奇!