位运算简介及实用技巧(四):实战篇
下面分享的是我自己写的三个代码,里面有些题目也是我自己出的。这些代码都是在我的Pascal时写的,恕不提供C语言了。代码写得并不好,我只是想告诉大家位运算在实战中的应用,包括了搜索和状态压缩DP方面的题目。其实大家可以在网上找到更多用位运算优化的题目,这里整理出一些自己写的代码,只是为了原创系列文章的完整性。这一系列文章到这里就结束了,希望大家能有所收获。
Matrix67原创,转贴请注明出处。
下面分享的是我自己写的三个代码,里面有些题目也是我自己出的。这些代码都是在我的Pascal时写的,恕不提供C语言了。代码写得并不好,我只是想告诉大家位运算在实战中的应用,包括了搜索和状态压缩DP方面的题目。其实大家可以在网上找到更多用位运算优化的题目,这里整理出一些自己写的代码,只是为了原创系列文章的完整性。这一系列文章到这里就结束了,希望大家能有所收获。
Matrix67原创,转贴请注明出处。
今天我们来看两个稍微复杂一点的例子。
n皇后问题位运算版
n皇后问题是啥我就不说了吧,学编程的肯定都见过。下面的十多行代码是n皇后问题的一个高效位运算程序,看到过的人都夸它牛。初始时,upperlim:=(1 shl n)-1。主程序调用test(0,0,0)后sum的值就是n皇后总的解数。拿这个去交USACO,0.3s,暴爽。procedure test(row,ld,rd:longint);
var
pos,p:longint;
begin
===== 真正强的东西来了! =====
二进制中的1有奇数个还是偶数个
我们可以用下面的代码来计算一个32位整数的二进制中1的个数的奇偶性,当输入数据的二进制表示里有偶数个数字1时程序输出0,有奇数个则输出1。例如,1314520的二进制101000000111011011000中有9个1,则x=1314520时程序输出1。var
i,x,c:longint;
begin
readln(x);
c:=0;
for i:=1 to 32 do
begin
c:=c + x and 1;
x:=x shr 1;
end;
writeln( c and 1 );
end.
但这样的效率并不高,位运算的神奇之处还没有体现出来。
同样是判断二进制中1的个数的奇偶性,下面这段代码就强了。你能看出这个代码的原理吗?var
x:longint;
begin
readln(x);
x:=x xor (x shr 1);
x:=x xor (x shr 2);
x:=x xor (x shr 4);
x:=x xor (x shr 8);
x:=x xor (x shr 16);
writeln(x and 1);
end.
为了说明上面这段代码的原理,我们还是拿1314520出来说事。1314520的二进制为101000000111011011000,第一次异或操作的结果如下:
什么是位运算?
程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理):
110
AND 1011
----------
0010 --> 2
由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。当然有人会说,这个快了有什么用,计算6 and 11没有什么实际意义啊。这一系列的文章就将告诉你,位运算到底可以干什么,有些什么经典应用,以及如何用位运算优化你的程序。
项目中碰到 PHP 和数据库之间,计算存在时间计算误差。大致的情况为根据段时间字符串,例如
2012-12-14 00:00:00 UTC
使用 MySQL 的 UNIX_TIMESTAMP 函数以及 PHP 的 strtotime 计算得出的时间戳,大概有半分钟(差不多有28秒)的误差。
同时,比较‘诡异’的是直接使用当前时间(MySQL 中 UNIX_TIMESTAMP 不带参数,同时 PHP 直接使用 time 函数),却不存在误差(测试脚本)。
排除了 PHP 和 MySQL 之间因为时区设置造成的时间误差 -- 根据经验,如果是时区设置造成的时间误差,应该有几个小时不会那么少。
搜索解决问题期间扫了下这篇帖子,觉得应该是‘闰秒’这玩意造成的问题。搜索 PHP 闰秒相关的配置似乎没有相关的,不过在这里似乎找到了些答案。
You also can experience this behavior if your system timezone
is with leap seconds. To avoid the problem in this case please
run query UPDATE mysql.time_zone SET Use_leap_seconds='N'
and restart the server. Please inform us if this helps.
按照上述的步骤执行,解决了问题。
回过头来,我在工作机(Windows)上测试,发现并不起作用。研究了下,原来闰秒也需要操作系统的支持:
1、对于大多数新的 Linux 内核,在设计时它们都是支持闰
秒的,这一点在 REHL4/5 的 2.6.x 内核中得到肯定。
2、如果 Linux 系统没有配种某种时间同步机制(比如NTP),
那么和闰秒无关,唯一导致的结果只是系统时间会比 UTC
时间快一秒。
3、Window Time Service 不支持闰秒,包括服务器和客户端。
回过头来考虑项目中碰到的这种情况,直接使用时间戳存储时间点会更精确些。最后,提供下相关的测试脚本,看看你的环境是否也会有类似的问题。