
这个问题很简单……
假设五个同事分别为ABCDE,他们的工资分别为abcde
则A随便想一个数a1,他把a1告诉B
B也随便想一个数b1,把a1+b1的结果告诉C
C也随便想一个数c1,把a1+b1+c1的结果告诉D
D也随便想一个数d1,把a1+b1+c1+d1的结果告诉E
E也随便想一个数e1,带上自己的工资e,把a1+b1+c1+d1+e1+e的结果告诉D
D把自己的工资d加上去,把自己之前加上去的d1去掉,把a1+b1+c1+d+e1+e的结果告诉C
C把自己的工资c加上去,把自己之前加上去的c1减掉,把a1+b1+c+d+e1+e的结果告诉B
B把自己的工资b加上去,把自己之前加上去的b1减掉,把a1+b+c+d+e1+e的结果告诉A
A把自己的工资a加上去,把自己之前加的a1去掉,把a+b+c+d+e1+e的结果告诉E
E把自己的e1减掉,除以5,得到最终平均数。
这个做法的好处在于:
每个人把自己的工资随意拆成四个数的和,分别把四个数字告诉自己以外的四个人;
每个人手里收到四个数字,加起来,报出;
五个人的数字相加,即可得到五个人的总收入,除以5得到平均工资。
记得这是一个挺古老的解决方案?
———————————————————————————————————————————
5/23 19:40千赞留念。没想到昨天吃晚饭时随手回答的一个问题得到了这么多的赞同
有评论说我说的不够清楚,那可以这么再严格地叙述一下:
设这五个人的工资分别为 ,第
个人告诉第
个人的数字为
,那么有
;
现在得到一个矩阵:
第二步就相当于计算每一列的和,然后第三步把它们加起来再除以五。总体上就是说矩阵所有元素的和等于各行和之和,也等于各列和之和。
总结一下评论区一些讨论:
这个方法不是我原创的,应该是近几年在某一本书上看的,但是翻了翻没找到;也有可能是数学建模或者密码学课上老师提到过。
———————————————————————————————————————————
5/24 12:40 两千赞留念。因为这个回答关注我的各位……可能会比较失望?我在搓米问答上主要回答魔方话题下的问题,以及给大佬们点赞……
哈哈这事儿我刚入职时干过。找个计算器,叫第一个人输入一个巨大的不规则的数,然后把自己的收入加上去,之后依次传给其他人,每人都把自己的收入加上之前的数。最后传回第一个人。
第一个人再把最后的总和减去Ta选中的那个不规则数,然后除以人数,即可得到大家的平均。
我的老板听说以后警告我们说:这么做是fireable offence,叫我们以后别再这么干了。
找一个计算器,把屏幕封起来,每个人输自己的工资,5个人都输完后,再把屏幕打开。
…………………………………………………………………………
这么简单的回答收到好多赞,十分惊讶
作为一个密码学学渣,这个问题引起了我极大的兴趣,想从相对专业一点的角度聊一聊。答案较长,分为以下几个部分:第一部分介绍一些基本概念和讨论的大前提,第二部分分析目前知友们提出的方案,第三部分基于一位知友的答案进行改进,第四部分讨论几何均值的情况,第五部分总结。
在一个有信息需要保密的过程中,我们应该如何考虑消息的安全性?主要分为两个方面:
第一是消息的保密性。也就是说,要尽量保证不该知道这个消息的人不能知道与消息相关的信息(注意是信息而非内容。内容是指完整地掌握所有的信息,而信息则是与之相关的东西,比如最后一个比特,二进制中 1 的个数等等。这些信息的泄露也是需要避免的)。
第二是消息的可靠性。也就是说,A 收到了 B 发出的消息,他应该有办法验证这条消息确实是 B 发出的,而不是中间人掉包后的消息。
在研究一个协议的安全性时,我们经常构造一个“游戏(game)”,游戏中有两方:敌手和挑战者。挑战者进行保密通信,而敌手则需要窃取秘密或破坏通信。根据不同的假设,我们给予敌手一些能力,同时对通信中消息的保密性和可靠性做一些要求。最后,我们考虑在面对有这种能力的敌手时,需要的保密性和可靠性是否能够达到要求。
具体到这个问题中,我们先讨论一些大前提,再讨论其他知友给出的方案的安全性,构造一些攻击,最后提出一个相对来说安全性较高的方案。为了一般性,我们直接讨论 n 个人的情况。
说明:这是一个很不现实但又必须做的假设。原因很简单:如果有且只有一个节点撒谎,那么他将获得其他四个人的平均工资,同时其他四个人不知道五个人的平均工资,且完全无法验证;如果有至少两个人撒谎,那么没有人可以同时知道所有人的平均工资。无论如何,整个协议将变得没有意义。
因为如果 n-1 个节点攻击另外一个节点,无论如何都一定成功。(注: n-1 个节点如果可以合谋,可以在这 n-1 个节点不泄露自己工资的情况下得到剩下一个人的工资,这是一个递归的过程。简单起见,我们不允许这样的攻击。虽然 的答案讨论了这样的攻击)
也就是说,没有一个可信第三方来帮助计算。这个要求否决了找会计等答案。
下面我们分析一些知友提出的方案的安全性。
1. 扑克牌/麻将/大富翁钞票
在这些知友的方案中,每个人通过扑克牌等将自己的工资信息拆分,之后混在一起加起来求平均。由于这些牌是扣起来混合的,因此不可能知道其他人的工资。
分析:这个方法在大多数情况下可以保证自己的工资不被其他人所知,但一定会有信息泄露。比如说我们用红桃代表千位,你看到翻开后的牌里有一个红桃九,而你自己的工资只有一千多,矛盾是不是就产生了?但是如果是理想情况,你只能知道最后的平均工资,而不能知道每一个人工资的任何信息。
由于这个方案泄露了大家工资的信息,因此不可靠。
2.第一个人加上一个随机数,最后减掉(这个答案)
这样的方式是符合最基本的安全要求的。也就是说,如果每个人都只知道自己的工资和传来的数字,没有人可以知道其他人的工资。
分析:这个方案几乎没有直接泄露的信息,但少数人合谋可以对某个人的工资数进行攻击。如果第一个人知道了第三个人收到的数字,他就可以知道第二个人的工资;2 号知道了 4 号收到的数字,就知道 3 号的工资……对于任意一个 n ,只需要知道前后的数字就可以算出一个人的工资,这个协议还是比较脆弱的。
3.每个人拆分自己的工资并发给其他人(这个答案)
在这种情况下,任意 n-2 个人合谋也只能得到其余两人的平均工资,无法获得更多的信息。这是目前最好的方案。
但是注意,这个方案我们进行了一些假设:每个人发给其他人的信息不会被这 n 个人之外的敌手窃听,也不会被篡改。但实际中并不是这样。我们的方案应该能防止这些事情的发生。
下面,我们提出这个问题中的敌手能力和安全性要求,并对方案 3 做出改进来满足我们提出的要求。
三、方案改进
现在我们假设这 n 个人不在一起,彼此之间只能通过计算机发送信息。而通信的信道是不可靠的,也就是说我们不能验证这条消息是不是对方发来的、没有被篡改过的,传输的信息也会被敌手得到。
而我们的要求包括两方面。第一,这 n 个人中即使有 n-2 个人合谋,也只能知道剩下 2 人的平均工资,不能获得更多的信息;第二,敌手即使可以获得所有的通信消息,也可以进行篡改。在这种情况下,敌手不能获得关于工资的任何信息,也不能篡改通信内容而不被发现。
在这里我们还要再做两点补充。第一是敌手的计算能力是有限的,这允许我们进行公钥加密和签名(这两个概念马上会具体介绍);第二是公开信息是可靠的,也就是说,我可以公开一个字符串作为我的一个“公钥”,这个公钥没有保密性,任何人都可以看到。而这种情况下这个字符串是无法伪造的,也就是所有人都可以确定我确实公开的是这段字符串。
下面我们分别介绍公钥加密和数字签名。在传输中的消息是会被敌手看到的,所以要对消息进行加密。但如果使用传统的加密方式,需要加密者和解密者拥有共同的密钥,在这种情况下是做不到的(不考虑密钥交换……这个过程还是用公钥思想实现的)。因此我们需要使用另一种加密方式:加密密钥是公开的,每个人都可以看到并使用;但解密密钥是保密的,只有消息的接受者持有。这样,当 Alice 想给 Bob 发送一条保密消息时,他只需要使用 Bob 的公钥对消息进行加密再发给 Bob ,Bob 使用解密密钥进行解密即可。
这种加密方式需要一个困难问题,大家把困难问题理解为一个敌手和挑战者都无法进行的计算问题即可。比如说离散对数问题:在一个群 G 中, ,给出群 G 和
的值,求
。这里的对数和我们之前讨论的不同,是在群中的。实数中的对数是容易计算的,而一般情况下,群里的离散对数是难以计算的。
另外,我们还要做一个假设(CDH 假设):在群 G 中,给出 ,计算
是困难的。
这样,我们就可以构造一个公钥加密体制,也就是著名的 ELGamal :
下面我们介绍数字签名。在现实生活中,我们经常需要对文件进行签名,证明我们已经阅读并认可该文件。类似地,在数字世界中,我们也需要对文件进行签名。签名可以防止信息的伪造:因为我们提到了,每个人可以公开一个字符串,这个字符串是可靠的,所有人都可以验证它属于你。类比公钥,你把一个消息用你的私钥进行处理,得到的值任何人都可以用你的公钥进行验证,这就达到了目的。
类似地,ELGamal 同样可以进行签名的构造。比如 Alice 要对消息 m 进行签名:
其实具体的方法不重要,大家只要知道有一种方法,可以验证一条消息是不是由某个人发送的就行了。
好了,下面我们完整地过一遍:
1.每个人的工资分别记为
2.每个人将工资随机地分解为 n 个数之和,记为 等等
3.每个人在自己分解的 n 个数随机排序,将第 个数用第
个人的公钥加密(如果是给自己的则省略),用自己的私钥对加密后的消息进行签名,然后将消息与加密结果一起发送给第
个人
4.每个人验证签名后用自的私钥解密其余 n-1 条消息,将其与自己留下的一个数共 n 个数求和
5.把求和结果用其余 n-1 个人的公钥分别加密,并对结果签名,将加密后的消息与对应的签名发送给对应的人
6.每个人验证签名后解密所有消息,计算平均值即可
四、几何均值
在第三部分最后,我们已经给出了所有人算术平均值的方案,可以证明在加密算法和签名算法安全的情况下,这种方案也是足够安全的。下面我们讨论求几何均值的情况。
如果用类似的方法,我们需要把每个数分解为若干个数的乘积,这是很难做到的。一方面,对整数进行素因子分解本身就不是一件容易的事情;另一方面,有些整数没有足够的素因子,将 1 作为因数之一并不是一个好办法。
看来只有另寻出路了。注意到 ELGamal 加密算法中的消息是作为一个因子的,这给了我们一个提示。构造这样的算法:
1. 其中任何一个人公开一个群 和一个生成元
,每个人选取一个私钥
和一个随机数
,公开所有
2. 第一个人计算 ,传给第二个人
3.第二个人计算 ,以此类推,直到第
个人计算完毕,将结果
交给第一个人
4.第一个人计算 ,交给第二个人
5.第二个人计算 ,交给第三个人,以此类推,直到第
个人计算完毕。公布结果,即为所有人工资的乘积
6.别忘了这里的乘积是对 取模的,因此要得到最终答案还需要一点数学技巧:选取
个不同的大素数
,重复上述过程,我们就得到了
个数的乘积对不同的
取模的值。用中国剩余定理,我们可以算出这个乘积对所有的
乘积的模值。注意到后者大于前者,因此取模并不影响,该数字就是这
个数的乘积,开
次方即为答案。
关于可靠性只需要再加一个签名即可,不再赘述。
五、总结
这个看起来很简单的问题,深究起来竟然涉及如此多的密码学知识,连我都有些惊讶。这也是我们思考问题的方式:假设每个环节都有泄密或被篡改的可能,然后逐个去排除,让协议更加安全可靠。
第二部分第三个方案是一个很不错的方案,不仅泄露的信息最少,而且很容易类比到任意人数的情况。因此很容易想到在此基础上进行修正。而第四部分纯粹是我突然想到的,画蛇添足了。
事实上,问题本身是一个最简单的安全多方计算问题,有兴趣的朋友可以顺着这个关键词查阅相关的资料。
最后我想说,密码学真的很好玩~
每个人把自己工资分成三个以上数的和。比如5000元,可以是2000+2000+1000,也可以是4000+100+100+100+100+100……
出于不愿意让别人知道的考虑,一般不会有人写4998+1+1……
然后同样的纸条,打印,扔到一个盒子里。再把盒子里的总数除以5。
简单,搓一桌麻将,万为千,筒为百,条为十,各自选好后,背面朝上出牌,再搓掉,最后总数相加。
扑克以花色区分,也可达到类似效果。
最简单的办法:拿个黑箱子,弄一大把珠子,每人挣多少钱就往里放多少颗珠子,最后数。简单安全有效,就是累点……
之所以使用一块钱一个珠子,而不是用多种面值的珠子的方法,是考虑到条件概率。首先,假设每人都写自己的工资进去,那所有人都可以知道别人的工资了(虽然对不上号);其次,任何一个人如果像高票那样把工资分成几个数告诉别人,那么所有人都可以(1)知道其他人的工资不会小于他告诉自己的数,(2)对其他人的工资做估计(乘以4),所以告诉别人的数额和自己的实际工资并不是条件独立的;第三,同理,假如使用带面值的道具,那么就至少有一个人拥有比出现的最大面值更多的工资,考虑到一个10000和四个100这种情况,这样的策略也是有问题的。所以说,将信息的粒度变到最小才最可以降低条件概率带来的影响。
计算器,M+,C,M+,C,……,MR,÷5
一个简单的做法就是A想一个随机数,加上自己的工资发给B,然后B再加上自己的工资,发给C,以此类推,直到E把算出来的数发给A。A用收到的数减去自己的随机数,就能得到工资总和。而其他人如果不做额外的通信的话,也是得不到别人的工资信息的。
这么复杂?
我提供一个不用数学方法的抖机灵吧。
取一个不透明容器,称出容器重,每个人把自己的工资换成水的毫升数,倒入容器,然后称出总重,减去容器重,除五,搞定。
防止抱团很简单,在每个人倒水的时候,其他人都不在场,而总重到最后才能称。
其实,这个容器就是一个自动求和的黑箱。
又刷到了一个用密码学可以直接解的题~
步骤如下:
对啥是同态加密感兴趣的朋友可以看这篇专栏:
说明一下,题目设置五人互不能知道工资的要求可以说就是要绝对保护隐私,即通过答案不能得出任何一人的工资。
所以之前那些回答之所以会被指出四人抱团情况是指这个回答有漏洞,在互不知道工资的情况下依旧可以推断出其中一个人的工资。
评论里所提到的方法如四人串通就可以互相通报等是不符题意的,不然五人随机报团推算剩余一人,还不如直说,这个题目根本没有意义。
还有就是提到的可以用同样的方法求四人的工资平均值,以此推算第五人,这种方法同样可以三人报团,导致四人中有一人工资被暴露,以此类推,所以如果你是那五人之一,绝不会同意此方案,因此根本行不通。
。。。。。。。。。。。。。。。。。。。。
原回答
看了一下回答,被质疑最多的就是如何防止报团的问题,即方案如何只能在五个人中可行而四人及其以下则不可行的问题
有一位答主提到过的一个挺老的方法,将工资拆分为四个数,告诉其他四个人,最后每人报出手里的四个数之和,再相加计算平均数,这个解法其实将工资拆分成2–5个数都行得通,不一定为4,而如果要去除报团因素,那么其实拆为五份才是最优解。
这里要加一个条件,就是每人将工资值分为五份后,写在同样的纸上,各自均是通过抓阄的方式抽取一份数值,这样抽到的数值都是随机的,就算是有四个人想要抱团,也无法区分数值是属于谁,而他们如果互相通报,则是他们四个人的工资数额也都能得出,不可行
很好的问题,如何对数据脱敏,同时得到有效的运算结果。
这个问题其实有一个更广义的情况,就是一共有n个数,能否加密后对他们进行四则运算,获得他们的最终运算结果,却过程中不暴露n个数本身的数值。
广义的情况的终极解决办法就是同态加密(Homomorphic encryption),2009年Marten van Dijk、Craig Gentry 、Shai Halevi、Vinod Vaikuntanathan的论文《Fully Homomorphic encryption over the Integers》实现了对整型的全同态加密,既加密后的整型在四则运算可以解密后得到真实的数值。后人优化的二代算法,github上shaih/HElib 这开源的库直接就能使用。
以同事的这个为举例,有四个工资数值a,b,c,d,和一个同态加密算法F和其对应的解密算法f。那么最终的平均值为f(F(a)+F(b)+F(c)+F(d))/4
以另外一个例子举例,有四个人分别知道且知道一个电商网站的流量,付费转化率,客单价和每月经营成本a,b,c,d。那么最终电商网站的利润就是f(F(a)*F(b)*F(c)-F(d))
这件事情的巨大价值在于,很多敏感的数据也可以开始放到第三方的云平台去进行计算了,反正平台做了一堆加减法乘除法,却依然不知道数值对应什么。
综合各位答主的答案,我想到一个办法。
卡片法。
1、准备一定量(具体数量在2和3讨论)白黄紫黑红五色卡片。每张白色卡片,代表十元;黄色,代表百元;紫色,代表千元;黑色,代表万元;红色,代表十万元。(在吃个蛋炒饭都要花十块钱的今天,个位数咱就不讨论了。)
2、根据公司最低薪资水平,选择卡片的权重上限;根据公司的最高薪资水平,规定最高权重卡片的备用数。比如,工资最少的人可能只有1000块,那么卡片中最高的就只能是紫色;而公司工资最高的人可能有几十万,那么就给每一个人准备1000张紫色卡片(当然,实际上也用不了准备那么多,年薪几百万的高管不大可能跟实习生一起玩这个游戏)。最高权重以下的,每人准备9张即可。
3、除去每个人手上的备用卡片之外,另准备一些总额已知的各色卡片用以扰乱视线,事先放到AB两个不透明的大盒子里。A用来统计工资总额,B用来装各自剩下的卡片。
4、接下来每个人把自己手上的卡片分成两堆,其中一堆代表自己的工资(比如工资是3980,那就是3紫9黄8白),另一堆是剩下的。
5、最后,大家把这些卡片一起放进两个盒子,再做最后的统计计算即可。(考虑到放的时候可能被彼此看见,最好先用小盒子装好,数一二三一起倒下去。)