清华校园网用 PAC 文件

访问校内站点时直接连接。

function FindProxyForURL(url, host) {
   if (shExpMatch(host, "*.tsinghua.edu.cn")) {
     return "DIRECT";
   }
   if (isInNet(host, "59.66.0.0",  "255.255.0.0")){
     return "DIRECT";
   }
   if (isInNet(host, "166.111.0.0",  "255.255.0.0")){
     return "DIRECT";
   }
   if (isInNet(host, "101.5.0.0",  "255.255.0.0")){
     return "DIRECT";
   }
   if (isInNet(host, "101.6.0.0",  "255.255.0.0")){
     return "DIRECT";
   }
   if (isInNet(host, "183.173.0.0",  "255.255.0.0")){
     return "DIRECT";
   }
   return "__PROXY__";
}

寻找下一个浮点数

求比给定浮点数大的最小浮点数

这是清华大学软件学院雍俊海老师《软件工程(2)》课程的一道思考练习题。

实现实数类CP_Real的“++”和“–”运算符重载。要求“++”是将当前的浮点数变为下一个浮点数,即变为比当前浮点数大的最小浮点数。要求“–”是将当前的浮点数变为上一个浮点数,即变为比当前浮点数小的最大浮点数。

为此需要研究浮点数在计算机中实际的二进制表示……

我们知道只需要给尾数 +1 就可以了。

我们需要让编译器把浮点数当成整数,以让 CPU 进行整数的 +1 运算。问题是,我们必须防止编译器“自作聪明”地做强制转换。为此,我们先取地址,得到一个浮点数指针,然后强制转换为整型指针,此时内存中的数据并没有变化;送入 CPU 做完整数加法之后,再反过来做一遍……这样就骗过了编译器。

(我估计这件事用汇编语言来写……会更简单吧)

好在,从资料上学习了一种新的类型转换的运算符:( type & )。这就是我们要的,和强制转换并不一样,这个只是把内存中的数据重新解读,而没有做任何实质转换。

以下两种形式的作用相同。

*((int*)&a)
(int&)a

顺便指出,需要注意的是以下三种写法的意义互不相同。

(int)a
(int&)a
(int)&a

运算符重载的操作就不再赘述。下面贴测试代码。

//只对 IEEE 754,int 和 float 等长负责,可能有 bug
#include 
float increment(float a){
	int b=(((int&)a)+(a>=0?1:-1));
	return (float&)(b);
}

int main(){
	float samples[]={0, 0.1, 1e-9, 2.33, 1e20, -0.1, -1e-9, -2.33, -1e20};
	int n=sizeof(samples)/sizeof(float);
	for(int i=0; i

以上代码在 GCC 4.8.1 编译运行的结果是:

0.0000000000000000000000000000000000000000000000000000000000000000000000
0.0000000000000000000000000000000000000000000014012984643248171000000000

0.1000000014901161200000000000000000000000000000000000000000000000000000
0.1000000089406967200000000000000000000000000000000000000000000000000000

0.0000000009999999717180685400000000000000000000000000000000000000000000
0.0000000010000000827403710000000000000000000000000000000000000000000000

2.3299999237060547000000000000000000000000000000000000000000000000000000
2.3300001621246338000000000000000000000000000000000000000000000000000000

100000002004087730000.0000000000000000000000000000000000000000000000000000000000000000000000
100000010800180760000.0000000000000000000000000000000000000000000000000000000000000000000000

-0.1000000014901161200000000000000000000000000000000000000000000000000000
-0.0999999940395355220000000000000000000000000000000000000000000000000000

-0.0000000009999999717180685400000000000000000000000000000000000000000000
-0.0000000009999998606957660700000000000000000000000000000000000000000000

-2.3299999237060547000000000000000000000000000000000000000000000000000000
-2.3299996852874756000000000000000000000000000000000000000000000000000000

-100000002004087730000.0000000000000000000000000000000000000000000000000000000000000000000000
-99999993207994712000.0000000000000000000000000000000000000000000000000000000000000000000000

(完)

SYSTEM_DRV 和 Lenovo_Recovery 分区是做什么的

上图为一台 ThinkPad 在出厂状态下的内置 SSD 分区状况,机型为 ThinkPad X1 Carbon (TYPE: 20A8-A0X2CD)。

SYSTEM_DRV

该分区上有主引导记录,有 bootmgr、有 BCD,是活动分区,默认状态下从该分区引导。该分区上还有一个 300MB+ 的 WindowsRE 的镜像。

如果删除,可能无法正常启动 Windows(需要修改活动分区为 Windows 分区),也将无法在不能启动 Windows 操作系统的时候进入 Rescue and Recovery 工作空间进行系统恢复。

官方用户指南对此的说明是:

Rescue and Recovery 工作空间位于固态驱动器上一个受保护的隐藏区域中,该区域独立于 Windows 操作系统运行。通过它,即使无法启动 Windows 操作系统,也可以执行恢复操作。

Lenovo_Recovery

此分区不能作为引导分区,相对平凡一些。此分区内含 SYSTEM_DRV 分区的完整映像(sdrivebackup.wim),还有一个 Windows 分区的完整映像(cdrivebackup.wim)(出厂状态的 Windows 系统,已预装了不少 Lenovo 的软件)。

如果删除,将无法制作恢复盘。

根目录下的 LenovoQDrive.exe 给出以下信息:

Lenovo 原厂备份分区信息:Lenovo 在此分区中提供了一份原厂预装软件的副本。为方便使用,只要在 DVD 上安装了此实用程序的副本,即可使用它恢复此空间。

还有一个休眠分区

在用户指南 BIOS 部分找到了以下信息:

Intel Rapid Start Technology

要使用此功能,固态驱动器上需要一个特殊的分区。启用后,计算机在睡眠状态下经过指定的时间后,进入低功耗状态。并且只需几秒钟即可恢复正常运行。

在电源管理器帮助中亦有相关说明:

启用 30 天待机

选中此复选框后,Power Manager 将根据系统及其配置自动更改计算机电源状态:

1.如果计算机支持深度睡眠状态,Power Manager 将会在指定的一段时间后使计算机进入深度睡眠状态。
2.如果计算机不支持深度睡眠状态,Power Manager 将会在指定的睡眠状态持续时间过后使计算机进入休眠状态。此功能要求 Power Manager 开始学习睡眠和恢复操作。大约需要经过几个月才能生效。

通过启用 30 天待机功能,可延长电池使用寿命。

(完)

密码在空中飘扬

时至今日,用户登录居然还有 HTTP 明文 POST 密码的,这就发生在……

最近一不小心发现清华校园网认证登录机制有个明显的漏洞,做了点实验,还写了篇文章,投稿至年级的微信公众号发表了。此博文作为警示:

对用户的密码不要直接 HTTP POST !简单取一下散列并不行,直接 POST 明文更不行!要让服务器下发一个 nonse,要用 HTTPS,要不然太不安全,太不负责任了。。!

不过这真是自我打脸。本博客采用的 WordPress 在登录的时候就是 HTTP POST 明文的。。等我期中考试完闲下来,把它整个改掉。。

(待更新)

Symantec 防火墙可能阻止 IPv6 流量

这是一篇故障排除手记。摘要如题。Windows 7 Ultimate, x64, Symantec Endpoint Protection 12.1.2015.2015(网络威胁防护定义2015-4-13 r11), TUNet, cernet

宿舍有原生的 IPv6,可以用来 BT 下载、观看流媒体等,不计流量不要钱,非常愉快。有一天我发现那些 IPv6 的站点都登录不上去了。IPv4 正常。

换其他网口,不行。换其他电脑,正常。由此认定是本机问题。

ping 得到 General Failure,浏览器报超时。而 DNS 解析正常,ipconfig 查 IP 配置未见异常,IPv6 地址是有效的。

使用“带网络连接的安全模式”进入系统,IPv6 站点访问正常。由此认定是第三方软件的问题。

经仔细检查,是 Symantec 的防火墙的缘故。关闭后恢复正常。

并不知道为何 Symantec 防火墙无故拦截 IPv6 流量。考虑到故障现象是突然出现的,这可能和更新的防护定义有关。

(完)

使用 Mathematica 制作真值表

使用 Mathematica 内置的 BooleanTable 函数制作逻辑表达式的真值表。

核心是使用 BooleanTable 函数。以下是代码,使用时修改前两行即可。

expression := {(P && Q && R), (P || Q || R), Xor[P, Q, R]};
variables := {P, Q, R};
table = Reverse@Boole@BooleanTable[expression, variables];
Flatten[{
  {Flatten@{variables, expression}},
  Table[
    Flatten[{
      Characters@IntegerString[i - 1, 2, Length@variables], 
      table[[i]]}],
    {i, 1, Length@table}]
}, 1] // TableForm // TraditionalForm

在 Wolfram Mathematica 10.0 将产生以下的输出。

以上代码的输出

用全局变量记录调用深度,直观展现递归过程

一个小小的 JavaScript 框架,用全局变量记录调用的深度,用 console.log() 直观展现递归过程,以利调试或演示。

Abstract: 一个小小的 JavaScript 框架,用全局变量记录调用的深度,用 console.log() 直观展现递归调用的过程,以利调试或演示。


最近写了一个 JavaScript 的扫雷游戏。当玩家点开一个空白格子,一大片空白格子都会打开。为了实现这一特性,我采用了递归算法。

因为本人水平有限,难以一次写对,又因为本人水平有限,只会用 console.log() 进行调试。。所以写了点代码,用 console.log() 直观地展现出递调用的过程。感觉这个小小的框架完全可以复用,于是就发在这里了。。

//全局变量
var depth=0;
function openBox(x,y){
	//递归终止条件
	//if(...) return;
	
	depth++;
	var str='';
	for (var i = 0; i < depth; i++)str += ' ';
	console.log(str + '<' + x + ',' + y);
	
	//主要代码
	//...
	//openBox(a,b)...
	//...
	
	console.log(str + '/' + x + ',' + y);
	depth--;
}

我们将在控制台得到以下直观优美的图形:

(完)

投针法生成正态分布随机数

用投针法,将“均匀分布随机数”转化为“正态分布随机数”,附 C 语言源代码。

恩,主流程序设计语言都提供生成(伪)随机数的函数,比如 C 的 rand():

The rand() function returns a pseudo-random integer in the range 0 to RAND_MAX inclusive (i.e., the mathematical range [0, RAND_MAX]).

江湖传言,该函数给出的随机数服从均匀分布。果真如此吗……?恩,试一下不就知道了嘛。来十万个。

好极了。不过区间 [0, RAND_MAX] 并不实用。我们用以下函数把它拓展为任意的 [min, max]:

double randUniform(double min, double max){
	return (rand()/(RAND_MAX + 1.0)*((max)-(min))+(min));
}

下面我们将利用这个函数生成服从正态分布的随机数。我们打算用投针法。

原理是,先画出所要正态分布的概率密度曲线,然后拿一个矩形框起来,然后在这个矩形内随机取点(投针)。舍弃那些落在曲线上方的点。剩下的点的 x 坐标是我们要的。

从原理可以看出,本方法可以拓展到任意已知概率密度函数的分布。缺陷是,只能在有限区间内生成随机数,且并不能保证在有限步骤内结束

效果是极好的。比如我们生成 100000 个,保存到文件中,用 Mathematica 画出来,与标准正态曲线放在一起比较:

以下是 C 语言的源代码。

#include 
#include 
#include 
#include 

#define PI 4.0*atan(1.0)
#define E exp(1.0)

//uniform distribution in range [min, max]
double randUniform(double min, double max){
	return (rand()/(RAND_MAX + 1.0)*((max)-(min))+(min));
}
//probability density function of normal distribution
double pdfNormal(double x, double mu, double sigma){
	return 1/(pow(E,pow(-mu+x,2)/(2.0*pow(sigma,2)))*sqrt(2*PI)*sigma);
}
//normal distribution (mu, sigma), in range [-k*sigma, k*sigma]
double randNormal(double mu, double sigma, double k){
	double x, y, ymax=pdfNormal(mu,mu,sigma), xmax=k*sigma;
	while(233){
		x = randUniform(-xmax, xmax);
		y = randUniform(0,ymax);
		if(y
	

PHP:UTF-8 BOM 会导致 HTTP 头的发送

如题,PHP Warning 奇遇记: Cannot modify header information – headers already sent

用 Notepad++ 写 PHP 代码不小心踩中一颗地雷。即使代码看起来是这样


运行起来仍然会产生

Warning: Cannot modify header information - headers already sent by (output started at E:\test.php:1) in E:\test.php on line 2

而不管是用 curl 命令还是浏览器,看到HTTP消息正文都是空的。(PHP/5.3.10, PHP/5.3.29, nginx/1.4.4, Apache/2.2.21, libcurl/7.40.0)


真是怪了。第 1 行真的输出了什么奇怪的东西?在 Notepad++ 显示不可见字符,一无所获。一拍脑袋,用 UltraEdit 十六进制视图观察了一下。前三个字节映入眼帘,

0xEF 0xBB 0xBF

和传说中的“锟斤拷”简直神似……豁然开朗!于是回到 Notepad++,将编码格式由 “UTF-8” 改为 “UTF-8 无 BOM” ,问题排除。

(完)