使用new Image()打点时的一个注意事项

本文源自http://hi.baidu.com/meizz/blog/item/a0f4fc0ae9d8be1694ca6b05.html,但是不幸的是这篇文章已经不能访问了,因此我在这儿将它的要点整理一下。

网站分析中,我们经常会用new Image()的方式向服务器发送一条打点消息,例如:

(new Image()).src = "http://log.mysite.com/1.gif?a=1&b=2&c=xxx";

这种方式简单易用,因而被广泛采用。但上面这段代码的问题是这个new Image()是一个没有引用的临时变量,随时可能被浏览器的垃圾回收机制回收。如果这个图片的HTTP请求尚未建立,那么在被回收时这个请求就会被取消,导致打点并没有真正发出。如果打点所在的页面比较复杂,浏览器垃圾回收机制可能会被频繁触发,那么这种方式打点的丢失率可能会高达10%以上。

解决方法很简单,将这个图片赋值给一个全局变量即可,例如:

var img = new Image();
var rnd_id = "_img_" + Math.random();

window[rnd_id] = img; // 全局变量引用

img.onload = img.onerror = function () {
	window[rnd_id] = null; // 删除全局变量引用
}

img.src = "http://log.mysite.com/1.gif?a=1&b=2&c=xxx";

用户体验的数据与数据的用户体验

如果你正好在从事互联网或IT类的工作,你一定经常听到“大数据”这个词。其实从好几年前开始,人们就在谈论数据了,这似乎会成为未来一段时间的一个热点,虽然大数据究竟能带来什么仍然没有人清楚。

如果数据真的很重要,那么,我们究竟应该做些什么呢?

Ibm704

上图是上世纪50年代时计算机的样子,它极其昂贵,庞大并且笨重,需要由受过专门训练的专业人士操作,运算缓慢,一个任务提交之后要等上很久才能得到结果。

现在所谓的大数据时代,其实正处在与上面的大型机类似的阶段。数据分析通常需要很高的硬件投入(搭建服务器集群),需要专业人士操作,响应缓慢,一个需求提交之后要好一会儿甚至第二天才能得到结果。

有趣的是,很多人认为这是理所当然的,在大众眼里,数据分析似乎是一件很高深的工作,当然需要大量的成本以及专业人员才能完成!

Mac air

上图是我写作本文时使用的电脑的样子,基本上每个人都能买得起它,它非常轻便,使用简单,大部分日常工作都能非常流畅地完成。

上世纪50年代时,没有多少人能想到电脑会变得这么便宜和流行。

试想一下,如果今天的电脑仍然和几十年前一样是昂贵的庞然大物,只有专业人士才知道如何使用,那么互联网还会像今天一样发达吗?电子商务还会发展起来吗?微博等产品还会诞生吗?我想,这些事物基本上都不会出现,电脑对我们的生活的影响或许也将非常有限。

只有当电脑足够多时,联网的需求才会产生,只有当互联网足够普及时,电子商务、即时聊天工具、Email、SNS、微博等产品才会兴起。这些产品中的很多,都是当初发明电脑的人以及最早使用电脑的人做梦也没有想到过的,但这些产品切切实实地改变了世界,也改变了我们的生活。

同样地,只有当数据足够普及,当大家都能很容易地使用数据时,数据的价值才会真正被引爆,有很多应用或许是现在的我们做梦也想不到的。

IBM的创立者托马斯·沃森曾表示,全世界只需要5台电脑就足够了。现在人们也认为,全世界只需要少量人处理数据就够了。

每个公司都在说,要让数据驱动业务,让数据驱动设计。简单来说,他们正在关注用户体验的数据,但似乎还很少有人关注数据的用户体验

要让数据产生更大的价值,就必须让数据走出象牙塔让每个人都能很方便地使用,要做到这一点,就必须提升数据的用户体验,降低使用数据的门槛,让数据的采集、存储、分析以及展现的各个环节都更简单易用。

当然,降低使用数据的门槛并不是说专业的数据分析师将会失业,事实上,未来的数据用户可能会分为开发使用者和应用使用者两类,就如同现在的程序员与普通电脑用户一样。专业的数据分析及开发人员仍然是不可或缺的,但他们将不再是主要的数据用户,更多各种职业及背景的人都会参与到数据的创造及使用中来,一如今天的我们在互联网上创作、冲浪。

数据的本质是信息,不过这种信息是各种日志、文本等汇总处理之后的信息,更像是一种信息的信息,或者说元信息。我们有理由相信,随着技术及观念的发展,会有越来越多的人开始关注这种元信息,也会有越来越多的工具帮助人们发掘或使用元信息。

或许不久的将来,数据的演化也会像电脑一样,越来越易用,越来越平民化。

当浏览器默认禁用第三方cookie

前一阵子,我们发现高版本的Safari中默认会阻止第三方cookie,如下图所示。

Safari默认阻止第三方cookie

问题

什么是第三方cookie呢?在访问一个网站A时,网站A算作第一方,如果网站A中引用了另一个网站X(网站X的域名与网站A的域名不同)的资源,这时这个网站X就被认为是第三方。需要注意的是,这儿区分不同网站的标准是域名是否相同,而不是这两个网站是否由同一个公司运营。比如,taobao.com和tmall.com被认为是两个网站,尽管它们都属于阿里集团。

在网站建设中,使用第三方资源非常常见,大多数据情况下,这并不会带来问题。不过有时候,我们可能希望能读写这个第三方域下的cookie,这时问题就来了。

比如我们有一个网站a.com,其中埋有一段JavaScript脚本,每当用户打开a.com中的页面时,这段脚本就会发送一个GET请求到x.com。这样,只需要分析x.com的日志,就可以了解a.com的访问情况。

如果只是要统计a.com的PV,那么只需要将x.com的日志中所有记录加起来就行了,但是,如果要统计UV呢?

这时就需要在x.com这个域下写一个cookie用于标识当前用户,比如叫USER_ID。当用户访问a.com的页面,也即发起到x.com的请求时,x.com的服务器检查x.com域下是否有USER_ID这个cookie,如果有则什么也不做,如果没有,则生成一个新的USER_ID并写入cookie。有了这个cookie之后,分析x.com的日志就可以同时得到a.com的PV与UV。整个打点过程如下图所示。

打点 使用第三方cookie

但问题是,x.com在这儿属于第三方域,在高版本的Safari浏览器中,向第三方域写cookie受到了阻止。带来的结果就是,用户每次访问a.com时,发向x.com的请求的cookie都为空,于是x.com的服务器每次都认为这是一个新访问者,每次都生成一个新的USER_ID写回去,但当同一个用户再访问下一个a.com的页面时,发向x.com的请求的cookie仍然为空。最后,分析x.com的日志时就会发现,访问PV没有变化,但UV却暴涨,几乎和PV持平。

或许有人会问,打点服务器为什么要使用第三方域x.com呢?如果使用与站点相同的域a.com不就没有问题了吗?的确,如果打点服务器与站点同域那就没有问题了,不过很多时候我们并不能做到这一点,比如我们可能需要向很多个域名完全不同的站点提供同一套打点服务。

这个问题目前并不算严重,因为还只有高版本的Safari有这样的限制。但是,Safari增加这个限制是为了保持用户隐私(因为有大量的广告站点滥用第三方cookie),很有可能在不久的将来其他浏览器也会跟进,因此我们不得不尽早寻找解决之道。

P3P方案在这儿也是走不通的,KISSY的开发者承玉曾经提出过一个解决方案,简单来说,就是使用POST来代替GET,这样就能继续在高版本的Safari中写入第三方cookie。但遗憾的是这个方案在Safari 5.1.4+的版本中失效了,估计Safari已经修复了漏洞。另外,Google曾经因为使用类似的方式继续在高版本Safari下读写第三方cookie而被告上法庭,在去年8月时被罚款2250万美元,也就是说,如果继续使用各种hack的方式绕过浏览器限制读写第三方cookie,有可能面临法律风险。而且随着浏览器不断升级,各种原来可用的hack方式也都陆续失效了。

因此,必须要寻找其他解决方案。

第二方方案

经过测试,我们发现目前Safari只会在第三方域下完全没有cookie时阻止第三方cookie,而第三方域下只要有过任意一个cookie,即可继续使用以前的方式顺利读写。但是,怎么才能在第三方域下写入第一个cookie呢?

我们测试了很多方案,包括iframe嵌套等,最后发现至少在Safari 6中,如果第三方域下完全没有cookie,那么就没有办法向其写入cookie,唯一的办法是将它变成第二方,也即让这个域在顶层窗口打开。也就是说,如果第三方域下没有cookie,要向它写入第一个cookie,要么将页面先跳到这个域,写入cookie,再跳回来,要么弹出一个新窗口,写入cookie,再关闭弹窗。

显然,这两种方案对用户体验来说都不好。

localStorage方案

我们注意到,高版本Safari只阻止了第三方cookie,并没有阻止第三方localStorage,于是,我们便有了一个更为激进的方案:放弃第三方cookie,使用localStorage来代替。

这个方案的本质是这样的:在a.com中嵌入一个w.com域的iframe,这个iframe读取localStorage(是w.com域下),取到各种原来需要保存在第三方cookie中的值,然后发送一个GET或POST请求到x.com,原来那些记录在cookie中随着HTTP请求头发送的信息则改为通过url参数(GET方式)或Form表单(POST方式)的形式发送。如果要发送的内容不多,那么可以使用GET方式发送,只需返回一个jsonp即可,然后iframe再将jsonp中的数据写入localStorage。如果需要发送的内容很多,有可能使URL超长,那么就需要使用POST方式发送,这时,需要在iframe中再创建一个iframe作为POST的target,然后新iframe再将数据用postMessage等方式传回原iframe,原iframe再写回localStorage。

整个过程(使用POST)如下图所示:

打点 使用localStorage

这个方案的问题是比较复杂,整个流程长了很多,需要用到一些HTML5特性,比如localStorage、postMessage等,不过好在不支持第三方cookie的浏览器基本上都是对HTML5支持良好的高版本浏览器。

就目前来看,比较保险的做法是新老方案并行,在老浏览器上继续使用第三方cookie,在高版本Safari等默认阻止第三方cookie的浏览器上使用新方案。虽然不完美,但确实是可行的。期待不久的将来能有一种更完美的方案。

 

2013-03-26更新:Firefox开始跟进Safari默认阻止第三方cookie的策略

Ier与Ter

从事IT工作的人常被称为ITer,以前我也常这么自称,不过最近我觉得,正如名字所揭示的那样,IT的内涵其实包括I(信息)与T(技术)两个部分,同样的,根据各个er的不同偏好及特质,ITer也可以分为两类,分别是Ier和Ter,前者对I(信息)更有兴趣,后者则对T(技术)更有兴趣。

首先要说的是,I(信息)与T(技术)是相辅相成的,两者结合起来才有IT这个行业,因此,无论Ier还是Ter都是必不可少的。本文无意争论两者的高下,只是想谈一谈两者的区别,或许你曾经与我有着同样的困惑,或许在认真思考过I(信息)与T(技术)的区别之后,你会对自己在做或想做的事情有更多的认识。

信息时代,如果说I(信息)是血液的话,T(技术)则是各种经脉与血管。信息必须要有技术的支持才能顺利地流通,而如果没有信息,技术也就失去了存在的意义。那些真正关注信息本身的人,我称之为Ier,而那些痴迷于技术的人,则称为Ter。

哪些人可以被认为是Ier呢?个人觉得,设计师、产品经理、数据分析师等都大致可以认为是Ier,程序员、数据库管理员、运维工程师等则可以大致被认为是Ter。

不过,这个划分其实是非常粗糙的,一个人可以同时是Ier和Ter,比如一位资深程序员也可以同时是很好的产品经理,一位优秀的设计师可能也会写一些代码。但如果他们能意识到这两部分的不同,相信对工作会有更多的帮助。

另一方面,同一个头衔也可以有人是Ier,有人是Ter,或者同一个人也可以有时是Ier,有时是Ter。比如,如果你是一名设计师,当你在设计时,你心底的目标是什么呢?如果你的目标是设计出更简明、更易用的作品,以便更好地传递产品的信息,那么你是一名Ier;如果你的目标是设计出更漂亮、更与众不同的作品,那么你是一名Ter。再比如,如果你是一名程序员,当你在写代码时,你心底的目标是什么呢?如果你的目标是写出更易用的软件,以便信息更通畅地传播,那么你是一名Ier,如果你的目标是写出更精妙的程序,那么你是一名Ter。

这么看来,用户体验,其实也就是Ier的工作,所谓的用户体验良好,也可以理解为信息能在用户与应用(站点、设备……)之间顺畅地流通。同样的,数据分析工作也是Ier的工作,当你在分析数据时,要记住你的目标是I(信息),而不是T(技术)。

I与T是一对好兄弟,它们形影不离,但是也绝不相同。I≠T。

《jQuery Mobile即学即用》译者序

JQueryMobile up and running

jQuery无疑是近年来Web开发中最流行的JavaScript框架,而jQuery Mobile则是jQuery在移动互联网中的延伸。

如果你曾经和我们一样在几年前就尝试过移动互联网的开发,你一定还记得那时手机上所能看到的网页通常都十分简陋,和电脑端的体验相比简直有着天壤之别。但是,随着iOS以及Android等平台的迅速流行,这一切已经被彻底改变了,很多原来只能在电脑上做的事现在也能在小小的手机上完成,并且体验不输于前者,同时,全世界无数的聪明头脑正在勤奋地挖掘着智能移动终端这种能随身携带的小设备的潜力,每天都有大量新奇的应用被开发出来。

每个人都知道,移动互联网的浪潮正在迅速兴起,对设计师以及开发者来说,这带来了新的机遇,同时也带来了新的挑战,新的平台通常意味着要学习新的知识。幸运的是,有一些技术能让我们继续使用我们熟悉的HTML5/CSS/JavaScript来开发可用于移动设备的应用,jQuery Mobile就是为此而生的。

jQuery Mobile这个名字容易让人误解,其实它并不是jQuery的移动版本,事实上,它是一个基于jQuery的移动应用的UI组件库。移动互联网的历史虽然还很短,但是已经有一些被广泛接受的设计模式了,而jQuery Mobile则是这些模式的集成者。使用jQuery Mobile,通常只需要编写少量代码即可构建出漂亮并且易用的WebApp界面,这样,设计师或开发者将可以把更多的时间和精力放在产品本身的逻辑以及功能上。此外,借助一些第三方工具,jQuery Mobile开发的应用可以很容易地打包成混合应用(能够调用系统功能),因此也有不少开发者使用jQuery Mobile来快速制作移动应用原型。

jQuery Mobile秉承了jQuery一贯的设计理念,这让它很容易学习和使用,如果你曾经使用过jQuery或者jQuery UI,你会发现jQuery Mobile非常容易上手。同时,作为Web开发中最流行的框架,jQuery有着庞大的社区以及资源,无论你是想使用jQuery Mobile还是改进jQuery Mobile,你都可以从这个社区中受益。

在jQuery Mobile刚发布时笔者就曾经试用过,它的便捷性一度让笔者觉得十分惊艳,没想到后来能参与这本专门介绍它的技术书籍的翻译,这让笔者在倍感荣幸的同时也感到压力巨大。翻译本书的时间里,我们一方面希望能尽快完成本书的翻译,早日向大家介绍jQuery Mobile这个强大易用的框架,另一方面又担心自己水平不足,不能很好地完成这个工作。另外,在接下本书的翻译工作时,我们(本书的两位译者)都正好初为人父,我们显然低估了几个月大的婴儿的战斗力,对父亲这个角色工作量的估计也严重不足,以至于这本两百多页的图书的交稿时间也被推迟了大半个月。不过我们终于完成了这个工作,在此,我们要对翻译期间给予我们帮助的各位老师以及朋友说一声谢谢,尤其要感谢图灵的李松峰和傅志红老师,你们的专业与严谨让我们非常佩服,同时,我们也要对各自的家人说一声谢谢,如果没有你们的支持与容忍,我们无法完成本书的翻译!

最后,由于水平所限,书中翻译的不当以及错误在所难免,敬请各位读者批评指正。

吴英杰、吴敏琦
2012.11 于杭州

注:原文首发在图灵社区(http://www.ituring.com.cn/article/16393)。

涂鸦之听风者

听风者

今天看了一下电影《听风者》,上面是参考剧照涂鸦的梁朝伟,当然,电影里并没有这个他穿着周迅送的红毛衣拿着伞竖起大拇指的画面。

画完后再对照剧照,发现头发好像画多了一点。另外老婆大人审视之后,表示我完全没有把梁朝伟的神韵与气质表现出来。好吧,还需要练习啊!

我的2012

转眼一年又过去了,世界没有要毁灭的迹象,于是生活仍然继续。

去年写的今年想做的三件事完成得貌似不是很好,用户行为数据的工作仍然在探索之中,除了把SwitchHosts!升级了一下之外没有写出新软件来,游戏方面倒是写了一个Android上的迷宫

除了这三件事之外,今年我还做了这些事:

1、参与翻译了一本前端书籍:《jQuery Mobile即学即用》;

2、写了一部科幻小说:《川流》;

3、因听力问题去了若干次医院;

4、读了60本书

5、看了41部电影

今年看的书里,我最喜欢的三本分别是:

1、《30年后,你拿什么养活自己?》。其实这本书写得并不是很好,不过在读这本书这前我从没认真想过30年后的财务问题,读完这本书之后我才第一次拿起纸笔计算了一下正常情况下我这辈子能赚到的总收入以及可能的总支出,发现事情并不像我原来想象的那么简单。30年并不遥远,很多事情的确应该早一点计划一下。

2、《寻找家园》。高尔泰先生的回忆录,这是一本关于苦难的书。在读这本书之前我并不知道作者,书中有很多句子扣人心弦。

3、《复杂》。这是我最近几年读过的关于复杂性的最通俗的书,很多章节都很有启发性,不过要真正理解并尝试在实践中应用这些理论,可能还要很多年时间。

明年想做的事:

1、专业上继续深入。

我现在做的事情正游走在几个学科的边缘,经常觉得书到用时方恨少,于是压力山大。明年需要踏实专注一点,希望能真正成为这个小领域的专家。

2、完成第二部科幻小说。

今年完成的《川流》是一部关于时间的科幻小说,不过也可以认为是关于程序世界中版本管理的科幻小说。写作这部小说的过程让我收获颇多,同时也产生了另一个科幻故事的灵感,希望今年我能把这个故事完成。

3、写一个新的开源软件。

我还没想好写什么,也可是说是有好几个想法但还没决定选哪个,毕竟时间有限,一年的业余时间可能只够完成一个小软件。边走边看,希望能再写一个类似SwitchHosts!这样的小工具。

最后,祝本博客的各位读者新年快乐!:-)

代码中的Zipf定律

统计语言学中有一个Zipf定律,讲的是在自然语言的语料库里,一个单词出现的频率与它在频率表里的排名成反比。

根据Wikitionary上的一份资料,英文中出现频率最高的10个单词是这样的:

排名	词	计数
1	the	56271872
2	of	33950064
3	and	29944184
4	to	25956096
5	in	17420636
6	I	11764797
7	that	11073318
8	was	10078245
9	his	8799755
10	he	8397205

如果把英文中出现频率最高的100个单词按出现频率以及排名画成直方图,可以得到下面的图像:

Wiktionary frequency list

我们的确可以用一条曲线来拟合这个排名数据,如下:

Wiktionary frequency list 2

这条曲线的方程是 y = ax^b 的形式,也就是一个幂函数曲线。

事实上,Zipf定律中的“反比”即是 y = a / x,也即上面 b = -1 时的情况。也即Zipf定律其实也是一种幂律。

这是一个实验定律,而非理论定律,但是的确在很多现象中可以被观察到。比如不仅是自然语言,我们平常写的代码中也存在这个定律,就像淘宝的前端框架KISSY中。

我们来写一个 Python 脚本,统计一下 KISSY 所有 js 文件中的词频。代码如下:

# -*- coding: utf-8 -*-
#
# author: oldj
# blog: http://oldj.net
# email: oldj.wu@gmail.com
#

u"""Count word frequency in codes.

Usage:
    python cf.py --folder=folder_path --ext=filename_extension

Example:
    python cf.py --folder=/Users/wu/ozcode/kissy/src --ext=js

"""

import os
import sys
import re

ROOT = "/Users/wu/ozcode/kissy/src"
#ROOT = "/Users/wu/studio/tb-assets"


def getWords(content):

    return re.findall(r"\w+", content)


def dealFile(path, words):

    c = open(path).read()
    fwords = getWords(c)

    for w in fwords:
        if w not in words:
            words[w] = 0
        words[w] += 1


def walk(root_path, ext):

    words = {}
    count = 0
    for root, dirs, files in os.walk(root_path):
        for fn in files:
            if fn.lower().endswith(ext):
                count += 1
                print("%d. %s" % (count, fn))
                dealFile(os.path.join(root, fn), words)

    save(ext, words)
    return count



def save(ext, words):
    kws = words.items()
    kws.sort(key=lambda a: -a[1])

    fn = "s_%s.txt" % ext
    f = open(fn, "w")
    for k, v in kws:
        ln = "%s\t%d" % (k, v)
#        print(ln)
        f.write("%s\n" % ln)
    f.close()
    print("-" * 50)
    print("output -> %s" % os.path.abspath(fn))


def main(folder, ext):
    counts = []
    c = walk(root_path=folder, ext=ext)
    counts.append(c)

if __name__ == "__main__":
    folder = None
    ext = None
    argvs = sys.argv[1:]

    for kv in argvs:
        k, _, v = kv.partition("=")
        if k == "--folder":
            folder = v
        elif k == "--ext":
            ext = v

    if folder and ext:
        main(folder, ext)
    else:
        print(__doc__.replace("cf.py", __file__))

使用方法为:

python cf.py --folder=folder_path --ext=filename_extension

当然,要将上面的 folder_path 和 filename_extension 替换为你真实的内容,比如:

python cf.py --folder=/Users/wu/ozcode/kissy/src --ext=js

统计KISSY源码的src目录下的所有 .js 文件,找出所有匹配正则 \w+ 的词,并按照词频从大到小排列,可以得到一个列表,其中前10项是这样的:

排名	词	出现次数
1	function	8437
2	var	6312
3	self	6120
4	if	5473
5	0	4525
6	return	4242
7	1	4057
8	S	3751
9	this	3452
10	expect	2947

当然,KISSY的开发者非常勤奋,更新很快,你最后得到的词频和我上面的可能会有一些不同,但比例应该是大致一致的。把前100项画成直方图,看起来是这样的:

Kissy *.js 词频

这些数字也与一条幂函数曲线吻合得非常好:

Kissy wf 2

除了js,KISSY中所有CSS文件的词频也符合同样的分布。我甚至还统计了一下工作环境下SVN上的所有js/css文件(这些文件出自数百名工程师之手)中的词频,发现这个规律仍然是存在的。

JavaScript写的网页迷宫游戏(三)

最近学习了一下phoneGap,把之前做的JavaScript写的网页迷宫游戏改造了一下,做成了一个手机游戏。

游戏在我的第一代小米手机上的截图如下:

jm-maze

你用浏览器访问http://oldj.net/static/jm-maze/,在线体验一下。或者也可以在Google Play上搜索“oldj.net”,安装这个迷宫游戏的Android版本

开发过程中用到的主要技术有phoneGapCoffeeScript,另外,作为《深入浅出jQuery Mobile》的译者之一,我也把jQuery Mobile用上了,以便在一些效果的实现上偷懒。:)

我用的IDE是Intellij IDEA,有一个老外的博客详细介绍了在IDEA上配置phoneGap环境的步骤,如果像我一样一开始不知道怎么配置,可以参见前面的链接。

最后,这个游戏还比较粗糙,我还没有花时间来做优化,在我的小米手机上感觉没有在电脑上那么流畅,如果将来有时间,我会继续改进。

前传:

阅读杂记:链接、爆发以及其他

最近读过的一些书之间有这样的联系,箭头表示提到或引用。

你是否对如何制造流行感兴趣呢?《引爆点》告诉我们,制造流行需要几个关键角色,包括内行、联系员、推销员等。内行识别出哪些东西值行流行,联系员将这些东西传播出去,而推销员则说服人们加入流行的大潮。

什么是联系员呢?《链接》一书中做了充分的探讨,作者指出,人与人之间的联系是一个巨大的网络,但这个网络并不是随机网络而是无尺度网络,也即并不是每个节点与其他节点的连接数都大致相等,而是存在一些关键节点,这些节点的连接数明显地大于另一些节点。这不难理解,就像我们都认识或知道一些人,他们的社交圈子明显比普通人大很多,这些人在流行中就充当了联系员的角色。

《链接》还探讨了这种无尺度网络的形成原因,作者指出,如果一个网络是在不断地增长的(比如互联网上总是不断地有新的网页出现,人类社会中也总是不断地有新的成员出现),并且先出现的节点有先发优势的话,就一定会出现无尺度网络,也即所谓的马太效应:富者愈富。

或许你也像我一样曾经不明白无尺度网络中的“无尺度”究竟是什么意思,《复杂》一书则正好回答了这个问题。《复杂》的作者是《哥德尔、艾舍尔、巴赫》(下文中简称为《GEB》)的作者侯世达的学生,她的书中有很多《GEB》的影子,但是《复杂》一书并不像《GEB》一样包罗万象,而是重点关注有序是如何从无序中涌现出来的。所谓无尺度,其实是分形的一种别名,指的是这种网络具有自相似的特点。如果只考察无尺度网络的一个部分,你会发现它的结构和网络的整体非常相似,也会有连接很多的关键节点和连接较少的普通节点,并且关键节点与普通节点之间的数目仍然遵循相同的幂律递减规律。就像地球上有一些全球知名的名人,但把范围缩小到国家,每个国家也有一些名人,再小一些每个省、市也都有各自的名人。

《复杂》有一章是关于元胞自动机的介绍。一个问题是这样的:如果一个元胞只能知道离它最近的几个元胞的状态,并且可以根据这些状态改变自己的状态,那么,所有的元胞能不能达成少数服从多数的协议?即如果初始状态时元胞中黑色的点多一些,这些元胞有没有办法能在有限有个回合中仅仅根据各自邻居的状态了解到这一信息,并且自己也变成黑色?这个问题也有一个更通俗但是不那么精确的版本:如果社会上每个人都只能知道自己的邻居的意见,整个社会是否仍然有可能就某个问题达成一致?

令人惊讶的是这样的方法是存在的,并且这个看似复杂的方法如果用物理学的视角来看的话将有一个非常简单的解释:把一些信号的传递看作某种基本粒子,这些粒子也会发生碰撞、衰变等等。从这个角度看,我们的世界中的潮流、传染病等的流行在另一个抽象的世界中或许正对应着一些粒子的运动。

同样的思想在《链接》一书中也有描述,当提到无尺度网络时,研究者发现它与爱因斯坦多年前提出的一个观点(玻色-爱因斯坦凝聚)有些类似,只要做一些转换,网络竟然与量子力学中的这个领域有着一一对应的关系,根据这个对应,研究人员发现了无尺度网络有可能出现一种他们尚未注意到的凝聚状态,并且他们真的观察到了这种凝聚的网络。简单来说,无尺度网络一般的特征是适者致富,比如电脑品牌中惠普、戴尔、联想等都做得不错,其中第一名与第二名之间并没有非常大(比如数量级)的差距,但处于凝聚态的无尺度网络则会出现胜者通吃的现象,第一名会占据绝大部分市场份额,比如桌面操作系统世界中的微软与其他系统。

如果用无尺度网络的视角来考察人类自身的行为会得出什么结论呢?《爆发》一书做了细致的探讨。《爆发》与《链接》的作者都是Albert-László Barabási,从《链接》到《爆发》,无尺度网络以及幂律的思想可谓一脉相承。作者在《爆发》中提到,人类的日常行为并非完全随机的,而是也呈现出幂律,有些事会马上被处理,有些则会等待一段相当长的时间,等待的时间长度的概率遵循幂律。这种幂律的一个表现是,我们的行为并不是均匀分布在一个时间段里的,而会是一段时间里什么也不做,接着一段时间里连续做很多次。就像我们写邮件(无论是现代的Email还是以前的纸质邮件),我们并不会每隔一小时就写一封邮件 ,而是很长一段时间里一封邮件也不写,然后在一段较短的时间里连续写很多邮件。

在讨论了人类行为的模式之后,《爆发》抛出了这样一个观点:人类行为并不是真正随机的,而是像天气预报一样在某种程度上可以被预测的。正如预报天气需要大量的数据以及计算能力,预报人类的行为也需要大数据的支持。就像《当我们变成一堆数字》中提到的一样,当我们的日常行为都被记录下来之后,人类社会作为一个整体,或许就像天气一样,至少在短期内可以被相当精确地预测。这个观点与另一本流行读物《黑天鹅》的观点正好相反,《爆发》认为通晓过去就能预测未来,《黑天鹅》则认为历史充满了随机因素,存在一些影响巨大的意外事件(黑天鹅事件),由于这类事件的存在使得任何按照历史预测未来的努力都注定会遭遇惨痛的失败。

随机漫步的傻瓜》与《黑天鹅》都是都是纳西姆·尼古拉斯·塔勒布写的,这本书里重点讨论了投资市场上的黑天鹅事件。作者在书中指出,你的成功不见得是因为比其他人高明,而很可能是运气的结果。这个观点显然会引起很多人的反感,至少《爆发》一书认为人类行为是有规则的,在一定程度上可以被预测。《复杂》中也提到一些学术界的大师使用了复杂的算法在投资中获得成功的故事。

那么,未来究竟是否可以预测呢?无论Barabási还是塔勒布似乎都同意这样一个观点:这取决于人类的行为在本质上的随机程度。如果人类行为是有章可循的,那么就是可预测的,但另一方面,如果人类行为是完全随机的,那么按泊松的观点,完全随机的事件在统计学上也是可预测的。这么说来,或许只要能掌握足够的信息,人类群体的行为可能真的是理论上可预测的,但问题是,掌握足够多的信息这一前提是否能够实现?我们会不会在收集这些信息的同时影响到系统本身?这又有点像量子力学中的测不准原理了。

总的来说,这些书都非常有趣,提出了许多漂亮的模型或理论,并且的确能解释若干问题,更妙的是它们相互关联,有的相互支持,有的相互否定。不过也可以看到,虽然有了这么多有趣的探索,但在一些关于人类自身的最基本的问题上,我们仍然远没有达成一致。