按 ‘ infotech ’ 分类归档

程序员入伙书——换还是不换

话说,这道题,十几年前,玩论坛的时代,就出现在网上了:

三道门,一道门后面有车,另外两道门,后面都是羊。
有个节目主持人,他知道车在那道门后。
他请你选一道门,但不立即打开。
他在剩下的两道门里,打开一道,放出一只羊给你看。
现在台上还剩两道门关着,一道是你选的,一道是谁也没碰过的。
主持人说:现在你有个机会,可以改变主意,挑另一道门。
不管你换还是不换,你最终选中的那道门,后头的东西都归你。
你换不换?——假设价值观是车比羊好。

当时,论坛上吵成一片,有说该换的,有说不该换的。对于不换而中奖的概率,大家比较一致:1/3。而对于换而中奖的概率,基本上有三个说法:1/3、1/2、2/3。

论坛上贴题的那人说:“正确答案是应该换,中奖概率会从1/3提升到2/3。并且,此答案经过了智商排名极靠前的一位人士的认可。不过,还是有许多不愿盲信权威的年轻人,自己动手,用各种办法做实验,有大活人亲自做的,有用计算机模拟的,而结果一致确认:该换,中奖概率确实提升到了2/3。另外,虽然实验结果如此,有相当多的亲手做实验的人依然坚持(理论上)不该换。”

时至今日,这道题提到人口稠密的社交媒体上来,依然能够引起争论。

为了让读者老爷们怀着轻松的心情观看余下的内容,我提前把答案确认一下:

应该换。
我不是从一道门换到另一道门,
而是从一道门换到另外两道门。
“换并且选到车”的概率 = “第一次没选到车”的概率

三道门的迷惑性较大,读者可以极端化一下:如果是一亿道门,你挑一扇(选中车的概率很悲观),然后主持人把许许多多门打开,放出来满坑满谷的羊,最后只留下一道门,你换不换?

或者再换一个思路:主持人挑你和另外一个现场观众上来,让你选一道门,让那位观众选两道门,然后问你,愿不愿意拿你的一道门换那位观众的两道门?

“开门放羊”只是个迷惑动作,当门的总数大于2时,挑剩下的门里,一定能放出至少一只羊。所以它没有增加任何信息,也没有改变概率分布,这一步骤和事后开奖并无不同。

用概率论的运算过程则是:

  • 换而中车的概率 =
            首次选羊的概率 × 拿羊换到车的概率 +
            首次选车的概率 × 拿车换到车的概率
  • 首次选羊的概率 = 2/3
  • 首次选车的概率 = 1/3
  • 拿羊换到车的概率 = 100%
  • 拿车换到车的概率 = 0%
  • 因此,换而中车的概率 = 2/3

如果你看到这里还是觉得数学上不该换,那就可以关掉页面了。接下来并没有理论上的新内容,而且因为要用程序表达,技术上会比较烧脑。我不想让你同时背两个包袱走路,会芯片过热的。

 
虽然这道题没有数学理论难度,我还是想拿它做个编程的例子。原因是,我发现用计算机的思维,可以洞穿这道题的本质。做完实验依然坚持不该换的人里面,一定没有用计算机模拟的。

我的第一版程序很简单(手机读者可用手指左右拖动代码区):

import random

def lottery(n, change):  # n是门的个数,change设置“换还是不换”
    car = random.randint(0, n - 1)   # 为车生成一个随机的门号
    pick = random.randint(0, n - 1)  # 我选择一个随机的门号
    # 如果我未选中车但愿意换,或者我选中了车并且不换
    if (pick != car and change) or (pick == car and not change):
        return True      # 我都会中奖
    else:                # 否则
        return False     # 就不会

repeat = 100      # 重复做此实验100次
win = 0           # “中奖”计数器置零
for i in range(repeat):
    if lottery(3, True):  # 每次都选择“换”
        win += 1          # 如果中奖,计数器加一
# 打印中奖百分比
print("%.2f%%" % (win / repeat * 100))

结果如何呢?运行三遍:

=============== RESTART: cargoat.py ===============
68.00%
>>> 
=============== RESTART: cargoat.py ===============
71.00%
>>> 
=============== RESTART: cargoat.py ===============
68.00%
>>> 

100次实验,如果选择“换”,中奖次数确实在2/3左右。当然我们也得反过来做一下,把lottery函数的第二个参数设为False:

repeat = 100      # 重复做此实验100次
win = 0           # “中奖”计数器置零
for i in range(repeat):
    if lottery(3, False):  # 每次都选择“不换”
        win += 1           # 如果中奖,计数器加一
# 打印中奖百分比
print("%.2f%%" % (win / repeat * 100))

运行三遍,选择“不换”的中奖概率确实较低:

=============== RESTART: cargoat.py ===============
34.00%
>>> 
=============== RESTART: cargoat.py ===============
34.00%
>>> 
=============== RESTART: cargoat.py ===============
27.00%
>>> 

还可以发现,实验的重复次数(repeat的值)越高,中奖概率越是稳定在2/3上,下面是repeat = 100000的三遍运行结果:

=============== RESTART: cargoat.py ===============
66.55%
>>> 
=============== RESTART: cargoat.py ===============
66.81%
>>> 
=============== RESTART: cargoat.py ===============
66.70%
>>> 

在这个程序里,原本热热闹闹的挑门、开门放羊、问你换不换的那些动作,一下子被简化成了“你第一次猜中的概率有多大”的问题。我刚才说:“做完实验依然坚持不该换的人里面,一定没有用计算机模拟的。”是因为写程序前,我先假装认为不该换,而写下lottery函数的前两行时,就找到了事情本质的描述方式(上面的粗大字体)。

 
然而,读者可能认为,我并没有精确地按照剧本来写,所以运算结果不可信。为此,我必须按照剧本写一遍程序,像宋小宝吃面那样,辣根和蒜瓣这些步骤都不能省。说实在的,因为需要彻底改变思路,写新程序很累,但我还是勉力完成了:
猛击阅读全文

所谓”银行卡盗刷黑幕”

被这个视频《记者卧底揭银行卡盗刷黑幕:10秒就可复制一张卡》尴尬到了。

bank-card-copy

视频简介称:

【震惊!记者卧底揭银行卡盗刷黑幕:10秒就可复制一张卡】近日河南@都市频道 记者卧底银行卡盗刷团伙,调查结果触目惊心:8000元买一台银行卡复制器,短短10秒就能复制银行卡,卡号、密码全准确!目前几万条银行卡信息被泄露!

 


 

听起来好像很吓人,不禁细细看了几遍。视频的内容是:记者找了个卧底,花8000块买了个读写卡机。记者刷了一下自己的银行卡,屏幕显示银行卡磁条里的数据,旁白口头说:“而且还有密码”(02:00)。记者又把这些数据复制到一张空白卡上,把新卡拿到银行取款机那里,输入自己的银行密码,之后就看到取款机唯命是从,想查余额就显示余额,想取现金就吐现金。

记者大声质问:我把钱存银行是因为我相信你,现在我还怎么相信你?

然而,看到这类新闻,就要按照经典思路想一想:有这么好的随便拣张卡就能取钱的工具,8000软妹币转让给别人?学雷锋啊?

02:00的女声旁白,声称读卡信息里“还有密码”,这时我们就得问一句:

你说有密码就有密码啦?

视频从头到尾,并未证实在取款机上输入的密码和刷卡屏显内容之间的因果关系:记者并未拍下屏幕上某串数字,并演示他是根据这串数字输密码的。

演示者必须事先不知道银行卡密码,演示才能称得上严密有效。

做个思维训练:通过电话银行改掉密码,再拿着毫不知情的银行卡到取款机,必须用新密码才能取钱。这就说明了密码存在银行的服务器里,而非卡上。多说一点,其实连银行服务器也不知道你的六位数密码是什么,不过这是另外一个话题了,见我以前写的《“会报警”的取款机》。

那么多贼在取款机上装针孔摄像头、键盘罩,就是为了偷这个不在银行卡上的密码。他们在取款机的插卡口装文中的这类读卡机,用来复制卡片;再用摄像头、键盘罩、或者更生猛的望远镜偷看储户按密码(所以取款机提示遮挡键盘)。两个信息都拿到,才能黑掉储户。

如今,“老杨”的机器只做了第一步就搞定了卡片和密码,我们就得问问以前的贼都是饭桶吗?

这个8000块的刷卡机只是螳螂捕蝉黄雀在后的骗局的一部分:

卖刷卡机的 骗 想偷银行的。

从前有号称用最廉价的工业明胶和石灰石做假鸡蛋的,开课授徒,收很贵的培训费。学生进门时,老师会带着你去看她用的原料和产品,也会示范把原料放到设备里的全过程,过一夜你再去看,老师已经把示范产品端出来了。确实很逼真,打到碗里完全看不出是假的——其实,就是真鸡蛋。老师并不靠卖假鸡蛋发财,她从培训费挣钱,骗的是想造假鸡蛋的奸商,而受骗的奸商,都具有自行脑补“老师从昨晚的设备里端出假鸡蛋”画面的天资。

同样,本视频里卖刷卡机的“老杨”,并不靠刷卡偷钱发财,他是高价卖刷卡机,骗的是想刷卡偷钱的贼。而受骗的贼和记者,都具有在做实验时主动补全密码的天资。

现在好了,有了大媒体的微博推广,“老杨”的刷卡机一定热销。如果你觉得不会有人受骗,那就看看这条视频的评论吧(看上面的截图也可以):蠢货无数,只要再加一个“坏”字,就是“老杨”的潜在客户呢。

另:平时大家不是把记者叫妓者的么?和这些蠢货是同一伙人么?

magic quotes

这篇博文里涉及两个问题。一是Mediawiki显示空白,二是在托管共享服务器上关掉自己PHP环境里的magic quotes。

对于第一个问题,我找到了原因但是没在上面费时间;解决了第二个问题,第一个问题也就N/A了。

 

先说事情的缘起。最近发现我那个自用小Wiki出状况了,所有页面都显示空白正文,只剩标题和Wiki标准界面菜单。

如果点“编辑”,就能在编辑框里看到正文。做一些改动再打开,可以看到词条内容确实被改动过。这是个很好的信号,说明数据库里的内容都还在,而且存取都没问题,只是后续的处理和显示出错了。

大致搜了一下网上内容,有遇到完全相同症状的,说是PHP的PCRE版本升级了,而低版本的Mediawiki代码会出错。方案是降级PCRE或者升级Mediawiki。

降级PCRE是不可能的了,Mediawiki早该升级了。

那就升吧。

 

  1. 下载Mediawiki。
  2. 解压。
  3. 上传到服务器上的一个新路径下。
  4. 把旧Wiki里的images目录拷过去。
  5. 把旧wiki里的LocalSettings.php改一下$wgScriptPath,拷过去。
  6. 浏览器上打开新路径的Wiki链接/mw-config,按指示操作。
  7. 确认新版本运行正常。
  8. 删除旧Wiki目录。
  9. 把新Wiki目录改为旧Wiki曾用名。
  10. 把LocalSettings.php的$wgScriptPath改回来。

 

做到第六步时报错了,说新版Mediawiki不能在magic_quotes_gpc打开的情况下运行,让我关掉。

magic_quotes_gpc是PHP_INI_PERDIR级别的,不能够被程序软关,就是说在LocalSettings.php里调用ini_set没用,必须在类似php.ini这类系统级设置里硬关。

可系统不是我一个人的,我只能局部定制。

 

直接在.htaccess里写

php_flag magic_quotes_gpc Off

刷,产生“500 Internal Server Error”错误。据说如果PHP在Apache上不是以Module运行,则会看到这个错误号。这条路走不通了,再来。

 

写个PHP脚本,只有一句话:phpinfo(); 上传运行。阅读phpinfo()的输出,找到PHP初始设置都用了哪些.ini文件。

再写个PHP脚本,只有一句话readfile($_GET[file]); 上传运行,把.ini文件一个个读出内容来,拼接成一个php.ini,在文件末尾附上

    magic_quotes_gpc = Off

在.htaccess里放一句

    SetEnv PHPRC 新php.ini全路径名

上传,再刷,没关掉……

 

几乎山穷水尽时留意到了phpinfo输出的这一栏:

    user_ini.filename     .user.ini

对啊,PHP_INI_PERDIR的说明里也提到了.user.ini,我早该用这个最省力的方法。

创建一个.user.ini文件,把“magic_quotes_gpc = Off”写到里面,上传到Wiki主目录下。再刷,呼,终于行了。

微博分享按钮

三年前往博客里添加了一个微博分享按钮,最近发现微博分享方式有WBML新版了,于是按照微博介绍的WBML界面重写了这段代码。

先贴代码。新版的添加微博分享按钮的方法:编辑正在使用的monochrome族主题(其它主题可能要微调)的header.php,做两个改动:

一、在<html>标签后增加wb命名空间:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:wb="http://open.weibo.com/wb">

二、在</head>之前加入以下代码:

<script src="http://tjs.sjs.sinajs.cn/open/api/js/wb.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
jQuery(function(){
//  window.setTimeout(function() {  // Make it asynchronous if using the iframe mode.
    jQuery('div.post').each(function() {
        var url = jQuery(this).find('h2.post_title > a').first().attr('href');
        if (typeof(url) == 'undefined') {
            url = location.href;
        }
        var titlestr = jQuery(this).find('.post_title').text() + ': ' + jQuery(this).find('p').not('.wp-caption-text').text();
        titlestr = titlestr.replace(/\s\s+/g, ' ').substr(0, 110);

        var buttonstr = '<div style="width:95%;text-align:right;margin-top:-40px;margin-bottom:40px;">';

        var param = {
            url:url,
            type:'button',
            addition:'number',
            picture_search:'true',
            title:titlestr,
            ralateUid:'1691250947'  // 关联用户的微博用户号码
        };
        var temp = [];
        for(var p in param) {
            temp.push(p + '="' + encodeURIComponent(param[p] || '') + '"');
        }
        buttonstr += '<wb:share-button ' + temp.join(' ') + '></wb:share-button>';


        buttonstr += '</div>';
        jQuery(this).find('div.post_content').first().after(buttonstr);
    });        // jQuery.div.post.each
//  }, 5000);  // window.setTimeout
});
</script>

第一次重写后,IE不稳定地显示按钮、Firefox完全不行(IE竟然胜出,奇迹)。调试发现和一年前的一次优化有关。当时我把分享按钮的JS代码改为异步(橙色代码,页面加载完毕5秒后)运行,而此定时器似与WBML版按钮冲突。

之所以怀疑这个定时器,是因为观察了博客右边栏放置的静态代码“微博关注”按钮。我留意到它总能显示,并且当整个页面加载完毕、网页闲下来时才开始运作。猜想渣浪也发现这些按钮很耗时,于是也在按钮里内置定时器,把我这些外层的定时器顶得不干活了。

另注:渣浪程序员把“关联用户”的设置参数命名为ralateUid,嗯,从上一版延续至今以后也一直会继续错下去的拼写,厚道地猜想这是为了防鹅防寨故意拼错的。另外,微博文档里说抓图选项是searchPic,而快捷设置页里则可以看到是picture_search。我测试之后得知,picture_search是有效参数。

Google Earth寻山

马卡鲁峰(左),2006年7月29日,久乌拉山口

马卡鲁峰(左),2006年7月29日,久乌拉山口

用Google Earth模拟,找到了2006年在久乌拉山口看到的、马卡鲁峰前头这座尖尖的小山峰的经纬度:28.1573N 87.0577E,峰顶海拔6717米左右。不幸的是它身边尽是些8000米以上的巨人,所以它似乎还是个无名山峰。在Himalayan Index数据库找不到它。

Google Earth模拟马卡鲁峰北方小峰

Google Earth模拟马卡鲁峰北方小峰

12306的“撞库”泄密事件

说说12306的这次“撞库”泄密事件

先照顾一下阅读不耐患者,叙述几个要点:

  1. 12306或者他们的员工没有直接泄露用户个人信息。
  2. 用户的登录名和密码是在别的乱七八糟的站点泄露的。
  3. 黑客拿别处搜集到的登录名和密码来试12306,凡登录成功的,就把用户个人信息偷走。
  4. 12306还应该再做改进,即使登录成功,也不应该把重要个人信息完整显示。
中国铁路的声明

中国铁路的声明

好了,阅读不耐患者可以走了,留下来的继续。

这次12306的信息泄露事件,警方声明的原因是“撞库”。这儿假设我们能相信警方的描述,在此基础上进行讨论。

如果不相信警方的声明,也不用读下去了,慢走不送,留下来的继续。

先说“撞库”这个词:我的A网站上用的帐户名/口令被人知道了,那人拿到B网站上来试,果然也登上去了,这就是“撞库”成功了。

好比我在12306上有一个订票帐户,登录名是qujiong©sina.com(“曲炯在新浪稻糠母”,举例而已,反正不是我的电邮),口令是piao1234(“票1234”,举例而已,万一登录成功,本人不负责任)。

如果我这个帐户单单在12306使用,那么我是很安全的,12306会把我的口令加密成一串乱七八糟的东东“zGx.13jd8eklwq”,记录在它的数据库里,谁看了这串密码都不知道我的口令是什么,连12306自己都不知道。只有登录的一瞬间,12306读到piao1234,加密出“zGx.13jd8eklwq”时,它才会允许号称自己是“qujiong©sina.com”的人进入系统,之后,piao1234又会被系统永远忘记。(关于加密和安全的讨论,可以参阅另一篇文章《“会报警”的取款机》)

但是,如果我在别的站点注册,使用了和12306上一样的帐户和口令,而那个站点本身的节操比12306欠缺很多——比如说,用原文、而非加密密码来存放口令——那么我的12306帐户也不安全了。

好比说,我在一个约炮站点注册了,帐户还是qujiong©sina.com,口令还是piao1234(这次的意思是“嫖1234”),那个站点根本不把这个口令加密,而是直接存放原文。这家约炮站点的站长也不太有节操,轻轻易易就把一万个用户的帐户/口令,一块钱一个,卖出给黑客了(甚至他自己就是黑客)。

那么黑客就可以用这套帐户/口令来试验其它站点,12306、淘宝、支付宝、新浪微博……什么的,都可能是牺牲品。这个虽然算是误打误撞、但成功率可能不低(人们一般懒得到处使用不同的帐户/口令)。这个过程就是“撞库”。

进入12306的站点后,黑客就可以把用户的私人信息:手机号码、身份证号什么的,抓下来了。

事情描述完毕。

责任方是谁?

  1. 黑客是违法犯罪行为,首当其冲。
  2. 无良站长使用明码存储用户口令,难辞其咎。
  3. 用户自己不小心,在风险大的站点上竟敢使用和知名网站——特别是和财产相关的站点——相同的帐户/口令对。
  4. 12306虽然也算是被害者,但它轻易地相信了“登录者就是用户本人”,把用户的身份证号、手机号码这些私人信息完整显示,也应该负有责任,可以并必须改进站点设计。

关于这一点,负责任的网站,以支付宝为例,你登录进去之后,查看个人信息,自己的身份证、手机号码都不会完整显示,而是123**********456这样的格式,这就是有良心的站点。

琐事

上山前,手里火腿肠的听筒坏掉了,还好无事不可对人言,倒也一直撑到了现在。本来想投降苹果,今天到中国移动的店里一看,只剩下爱轰六在柜台里了,一个六千多,买不起。现在中等偏上的火腿肠才一千块一个,我就算一年换一部,也比一个爱轰用六年划算。

隔壁邻居整天把吃剩的鸭脖扔到电梯间那边,弄得同层住户(或小区清洁工)忍不住了,贴了个措辞猛烈的条请“大家注意公共卫生”,大家陪着挨骂,呵呵呵。有一天碰巧和楼下的一个老太太乘电梯,她一看我按的电梯楼层,就皮笑肉不笑地问我是哪个房间,我报了房号她就不感兴趣了,一定是我这邻居楼下的。

最近,邻居家又来了条狗。我不担心狗,而是担心狗主人:对着狗不停地跺脚、拍手、吼叫,比狗的动静大得多,我太同情他们楼下了。从这一屋子邻居平时的表现来看,我也怀疑,如果有一天他们腻烦了这条狗,也许会杀了吃掉。

木星探测气球?

木星大红斑·图片来源维基百科

木星大红斑·图片来源维基百科

说登陆太阳没人信,说登陆木星也许有人信。
说登陆木星没人信,说把一艘船在木星下海也许有人信。
说在木星下海没人信,说在木星表面飘个气球也许有人信。
对啊,干吗非要绕着飞,为什么不在木星表面飘个气球?

MH370分析图

MH370失踪分析

MH370失踪分析

以下时间,除非特别标准,均指东八区时间,即北京时间和吉隆坡时间。

3月15日,第一稿。依据信息“2:15在槟榔屿西北200英里(322公里)处,8:11最后联络卫星,最后已知速度是872公里/小时。”已槟榔屿西北200英里为圆心,5232公里为半径作一大圆。圆外地图画面去色。用Stellarium软件查得3月8日太阳赤纬-5.02°,依此信息作8:11时的晨昏线。依照8:11所联系的Inmarsat卫星及40度俯角数据,作红色圆弧,飞机在彩色地图范围内的圆弧上。

晨昏线本来是为了琢磨劫机者的想法的:8:11时,如果飞机在晨昏线以东,乘客们会奇怪为什么下面是大海(若无云)。如果飞机在晨昏线以西,乘客们会奇怪为什么天还没亮。开飞机的人更愿意乘客怎么想?这个还没有琢磨出来,就先遇到了不实信息传言:“3月19日,马尔代夫哈沃鲁日报报道,马尔代夫库达胡瓦杜岛多名居民称,3月8日曾看到一架大型客机从北部向东南方飞去,还看到白底红纹,舱门都看得清楚。”按照晨昏线一查就知道,马尔代夫那时天还未亮,此消息纯属刷存在感。

3月17日,加了个872公里的标尺。彩色地图的大圆,是基于“飞直线所能到达的最远距离”所绘,而如果飞机采取了躲避雷达的路线,则未必能飞到大圆边界上。872公里标尺是为了估计绕行路程所用。

3月20日,叠加了印度洋洋流图。如果澳大利亚称在3月16日拍到的卫星图上发现了一些疑似残骸,那么在盛行西风漂流的地方,他们今后的搜索海域应该向东偏移。

3月20日,认为飞机不可能出现在北方大陆上,把印度洋分析图做大些(如下)。图中底部三个图形纯属猜想:3月8日油尽坠机在晨昏线附近(机长想看最后一次日出?),3月16日,随着西风漂流和南印度洋寒流,漂移到澳大利亚卫星拍到的疑似残骸位置。3月20日,应该再向东漂移前述一半的路程。

MH370印度洋分析图

MH370印度洋分析图

1:19:29道晚安,1:20失联,半分钟的时间差。如果是劫机,这么准确的掐点,基本上只有机长做得到了。机长不需要对副机长采取暴力,只需要简单地说一句:“你帮我喊一下乘务长,给我弄杯咖啡。”或者“我怎么闻到一股烟味儿?你去机舱看一眼。”然后锁上驾驶舱即可。

1989年2月,联合航空811号班机因货舱失密造成机身破裂,9位乘客即刻被气流吸出舱外。幸免遇难的乘客当时以为飞机必毁,于是纷纷为现场拍照,如果被搜寻人员找到,则利于事故调查。1985年8月,日本航空123号班机尾翼脱落,在空中挣扎了半个小时后坠毁。坠毁前,乘客也拍照展示机舱内场景。黑匣子只能记录两个小时的驾驶舱语音,所以应答机被关掉的时刻、究竟发生什么,恐怕再也无人知道。但我祈祷乘客里有菩萨能以照片、语音、视频来告诉大家,这几个小时是怎么回事。

牡丹纨扇·墨兰

牡丹纨扇·墨兰

牡丹纨扇·墨兰

蒙微博好友@玄玄堂魚齋赠送亲绘的牡丹一幅(纨扇形制),极喜,装框后制作悬挂效果图一幅。成都连日阴沉,室内采光不够,故只有画和框是今日摄影所得,而陪衬之墨兰和画框阴影都是Photoshop后加的。墨兰是2006年2月在北京所拍,现在用上了。 😀