Chico and Dico ——根据任意4张扑克猜第5张牌

忘记在哪里看到这个好玩的地方了:

Using your head is permitted

http://www.brand.site.co.il/

里面都是一些有意思的数学题。

挑其中一些翻译一下,说说自己的理解~


May 2007 riddle


“Chico和Dico是一对非常著名的魔术师。下面这个魔术常常出现在他们的节目中:

Chico首先掏出一副标准的扑克牌,一共是52张牌(大小王被他们俩吃掉了)。然后他会挑选一位观众,肯定不是托(这是个数学题)。

这位观众会从这副牌中随机选5张交给Chico,Chico会重新对它们排序。这整个过程Dico都是看不到的。

现在这位观众会按照Chico排好的顺序给Dico看前4张牌,接下来,Dico会准确地说出第五张牌是什么!”

将这个问题扩展一下,假设有n张牌,观众随机选k张牌,而Dico需要猜最后的j张牌,那么n、k、j需要满足什么样的条件才能保证这个魔术可行呢?

进一步,怎么样得到这样的策略,并且足够简单让两个魔术师学会呢?


篇外:我想起来一个同学跟我说过的魔术。他和一位搭档,叫一位观众随意告诉他搭档一件物品。当然他是不知道的。然后搭档会任意说一堆物品,每说一件他回答“是”或者“不是”,如果回答“是”那么那件物品就是观众说出来那件。他表示这个魔术甚至可以通过电话玩,当然我现在也还不知道怎么玩的。


这里要找到这样一种策略,能够按照前四张牌确定第五张牌,看起来还是比较混乱的。不过题目本身给出了一些建模的提示:

n, k, j 需要满足一些条件。

既然字母都标出来了,不妨想想它们之间的联系:一共n张牌,随机选择其中k张,然后由前(k-j)张的顺序,猜到最后的j张牌。

这样一看,还是有一些直观上能够联想到的公式:n张牌随机选k张一共是C(n,k)种选择,前k-j张的顺序一共是n*(n-1)*..*(n+j-k+1)=n!/(n+j-k)!种。

哟,那是不是需要在k-j张牌的顺序与C(n,k)种选择之间建立一种对应关系,从而能保证每次都能猜对呢?

答案正是如此。假设有n张牌,任意拿k-j张牌的排列一共是n!/(n+j-k)!种,也就是最多能有这么多种不同的编码;而随机挑选k张的组合一共是C(n,k)种。想要根据前k-j张的顺序确定整个k的组合,就必须满足C(n,k)≤n!/(n+j-k)!,也就是(n+j-k)!≤k!(n-k)!。

那么要找的映射,就是使得k-j这个排列的所有牌都在C(n,k)这k张牌中的映射。当然这样的映射应该是有很多种的。这里讲一种j=1的时候,比较简单的:

假设观众拿到的牌是a0,a1,...ak-1,并且a0<a1<a2...<ak-1,它们的和对k!取余为S,我们按照如下方法决定顺序:

vector<int> reorder(vector<int> orig, int S) {//原数组是从小到大排好序的数组
int x = S; //令x为a0到ak-1的和余k!,即S
vector<int> cards; //待排序的卡组,位置0是最后一张牌
for (int i = k; i > 0; --i) { //i从k依次递减到1
  cards.push_back(orig[x % i]); //每次将原数组中的第x%i个数放入cards
  orig.erase(orig.begin() + x % i); //从原数组删除这个数
  x /= i; //将x除以i
}
return cards;
}
其中cards[0]j就是最底下被遮住的牌。这样做的好处是,可以按照逆过程推测原牌的顺序!

比如最后拿到cards是这样的:c0, c1, ...ck-1, 首先拿出ck-1,然后看ck-2与ck-1的大小关系。我们知道这是倒数第二次循环的时候对2取余的结果,因此如果ck-2>ck-1,则对2取余是1,倒数第二次循环的时候x=1;如果ck-2<ck-1,则x=0。

继续看ck-3,如果ck-3最小,那么在倒数第三次循环的时候对3取余结果是0;否则是1或者2,由上面对2取余的结果x2,我们可以得到倒数第三次循环的时候x=x2 * 3 + x3。

依次往上,我们可以得到S - S%k。

不过这还不是最后要求的c0。这时候我们已知的数有k-1个,假设这k-1个数的和为Sk,未知的数为c0,那么我们知道 S = (Sk + c0) % k!, S % k = c0 在a0,a1..ak中的位置,现在我们只差S和这个位置不知道了。不过时候,我们需要尝试的数只有0到k-1,通过尝试S - S%k与这个数的组合,以及得到的这个数在a0,a1..ak中的位置进行验证,我们可以确定最后的值。

上面说了一堆,还是拿一个真实的例子来讲吧~

看一位观众,随机抽到13,19,28,44,49。接下来Chico如何处理呢?

1. 计算S。五个数的总和是153,于是S=153%5!=33。

2. i=5, 取第33%5 = 3个数,也就是44 (从0开始数)放入卡组,此时cards={44}。将44从原卡组移除,orig变为{13,19,28,49},x变为33/5=6

3. i=4, 取第6%4 = 2个数,即28放入cards,此时cards={44,28}, orig={13,19,49}, x变为6/4=1

4. i=3, 取第1%3 = 1个数,即19放入cards,此时cards={44,28,19}, orig={13,49}, x变为1/3=0

5. i=2, 取第0%2 = 0个数,即13放入cards,此时cards={44,28,19,13}, orig={49}, x变为0/2=0

6. i=1, 将49放入cards完成,cards={44,28,19,13,49}

好的,然后Dico如何通过他们之间的默契来只看后四张牌猜第一张牌呢?

我们来客串一下Dico,并且假设我们可以一瞬间忘记44这个数字,只能看到49,13,19,28这样的排列。

1. 49, 拿过来貌似暂时没什么用,放这儿吧。cards = {49}

2. 13, 这个有用了。从cards我们可以看出来它之前应该是在第0位置的。我们推测x在除以2之前余2是0,因此此时x=0。(为什么呢,x在第一步之前本身就是0,因为它余1是0啊,0*2还是0啊...),cards={13,49}

3. 19, 观察可知19是从13和49之间拿出来的,所以可以推测x在除以3之前余3是1,因此此时x=x*3+1=1(还原与计算相反,计算的时候是先求余再除,还原需要先乘再加),cards={13,19,49}

4. 28, 同样的道理x在除以4之前余4是2,所以此时x=x*4+2=6, cards={13,19,28,49}

5. 所有数都看完啦,还有啥呢?我们还可以算一步x,虽然我们不知道x除以5之前余5多少,但是范围肯定在[x*5, x*5+4]之间,也就是[30,34]之间,所以呢S的范围是[30,34]之间,也就是原来五个数的和的范围是[150,154]之间。

6. 目前4个数的和是109,所以隐藏数的范围就是[41,45]之间。这个范围已经很小了,而且基本不需要挨个尝试了,它肯定在28与49之间,所以S余5的值是3,轻松求得S=33,隐藏数为44。

新技能get√!又可以愉快地装X了,但是要玩熟练肯定也不容易。。


不过由于才疏学浅,最后一段话没看明白!如果有人指教,就太好了~ 



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值