Trade Off

supercalifragilisticexpialidocious

Ldconfig: /usr/lib/libmysqlclient.so.16 Is Not a Symbolic Link的问题

刚刚用yum升级了系统,发现日志中有很多这样的提示:

1
2
ldconfig: /usr/lib/libmysqlclient.so.16 is not a symbolic link
ldconfig: /usr/lib/libmysqlclient_r.so.16 is not a symbolic link

于是Google了一下,找到一个解决方法,在这里.

就是重建了两条连接过去即可:

1
2
ln -sf /usr/local/mysql/lib/mysql/libmysqlclient.so.16 /usr/lib/mysqlclient.so.16
ln -sf /usr/local/mysql/lib/mysql/libmysqlclient_r.so.16 /usr/lib/libmysqlclient_r.so.16

小作弊玩游戏

有这么一个小游戏,按照顺序输入字母表的26个英文字母,看看你输入的总时间是多少。有个变态的人竟然在2s内就输入了26个字母,而且不允许有任何错误,真心佩服!!!

我试了试,快的话我只能在6s左右完成,根本上不了排行榜,于是我考虑作弊……

1
2
3
4
5
6
7
8
9
tell application "Google Chrome"
activate
repeat with i from 0 to 25
delay 0.04
tell application "System Events"
keystroke (ASCII character of (97 + i))
end tell
end repeat
end tell

不减速的话系统根本无法记录,所以还是减速了,这个速度正好拿到第一名的位置,OK,剩下的让人去羡慕吧!!!

感谢Apple提供的AppleScript这样优秀的脚本工具~

Sqlite3连接查询和多次select的对比

通过todo_meta把todo和user联系起来,要查出userid为0~6的所有todo。随机了10W条todo数据进去。

左外连接查询比两次select快10~20%左右。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#! /usr/bin/env python
# coding: utf-8

import sqlite3
import os
import time
import random

def connect():
return sqlite3.connect('./test.db')


def insertProgress(conn, user_id):
cur = conn.execute('INSERT INTO todo(title, date) VALUES(?,?)', ['just test!!!', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())])
conn.execute('INSERT INTO todo_meta(todo_id, user_id) VALUES(?,?)', [cur.lastrowid, user_id])


def insertManyData(conn):
#reset database
os.system('sqlite3 test.db < schema.sql')

conn.cursor()
counts = [0,0,0,0,0,0]
for i in xrange(100000):
userid = random.choice([0, 1, 2, 3, 4, 5])
counts[userid] += 1
insertProgress(conn, userid)
conn.commit()
conn.close()
print counts

def selectUsingLeftOuterJoin(conn, user_id):
conn.cursor()
cur = conn.execute('SELECT t.title, t.date \
FROM todo t LEFT OUTER JOIN todo_meta tm \
ON tm.todo_id = t.id\WHERE tm.user_id = ?', [user_id])
def selectTwoTimes(conn, user_id):
conn.cursor()
cur = conn.execute('SELECT todo_id FROM todo_meta WHERE user_id = ?', [user_id])
for tid in [row[0] for row in cur.fetchall()]:
cur = conn.execute('SELECT title, date FROM todo WHERE id = ?', [tid])

if __name__ == "__main__":
uids = [0, 1, 2, 3, 4, 5]
insert_time_start = time.time()
insertManyData(connect())
print "Insert Time: %f" % (time.time() - insert_time_start)
select_ULOJ_start = time.time()
for uid in uids:
selectUsingLeftOuterJoin(connect(), uid)
print "Select Using Left Outer Join time: %f" % (time.time() - select_ULOJ_start)
select_two_times = time.time()
for uid in uids:
selectTwoTimes(connect(), uid)
print "Select Two Times time: %f" % (time.time() - select_two_times)

schema:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
drop table if exists todo;
drop table if exists user;
drop table if exists todo_meta;
create table todo (
id integer primary key autoincrement,
title string not null,
date datetime not null
);
create table user (
id integer primary key autoincrement,
email string not null,
password string not null,
nickname string not null,
salt string not null
);
create table todo_meta (
todo_id integer,
user_id integer,
foreign key(todo_id) references todo(id),
foreign key(user_id) references user(id)
);

启动脚本就会创建数据库并开始测试。

Sqlite3的rowcount

最近写了一个todo的webapp,在用户登陆的时候是select数据库中的内容进行对比判断,当然如果select的内容是0那就表示用户登陆失败了,可能账号或者密码写错了,数据库用的sqlite3,在python的api中发现有cursor.rowcount可用,于是就这么判断一下是不是有数据被select出来了,没想到这样判断是错误的。

select的rowcount永远为-1,哎,这里着实坑了我一番啊~

只好cursor.fetchone()先,然后判断得到的内容是不是None。

这里得到的答案。

逆序对的求解

今天看到算法导论上关于求解一个序列的逆序对的问题,以前自己在这个地方写着解答方法,不过今天怎么看都不懂了,从网上找了找,大都说得很模糊,不过自己突然有种领悟了的感觉,找了个简单的例子,实验了一下,自己的结论是正确的:)

例子是(2,3,8,6,1)这个序列,找出所有的5个逆序对。最好的方法是nlogn复杂度的利用归并排序的副产品——merge部分统计,统计比较左右列表时,左侧大于右侧时“左侧”列表中剩余数目。

归并的划分应该是这样的,(2,3,8)为左侧,(6,1)为右侧,继续划分为(2,3)和8,右边是6和1。下面就开始合并了,(2,3)和8这边还是(2,3,8),右侧出现一个逆序对,在合并6,1的时候因为作为“左侧”的6要大于1,所以这里有1个逆序对,这里的1就是此时“左侧”序列中的元素个数。

在往下走,要合并(2,3,8)和(1,6),马上就出现了2>1这样的情况,此时会出现(2,1)、(3,1)、(8,1)这几个逆序对,后面还会出现(8,6)这个逆序对,一共就这么5个。

因为原始序列是(2,3,8,6,1),我们需要用到这个原始序列的顺序来判断顺序排在前面的值却大于后面的,这样的机会在每次merge的时候是非常合适的。

这是修改后输出逆序对的归并排序算法,有点可惜哈,这么长的排序算法只让副产品在台前显露了一把!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#! /usr/bin/env python
# coding: utf-8

def merge(arraylist, first, middle, last):
   temp = []
   i = first
   j = middle + 1
   while i <= middle and j <= last:
       if arraylist[i] <= arraylist[j]:
           temp.append(arraylist[i])
           i += 1
       else:
           for x in arraylist[i:middle + 1]:
               print "(%d,%d)" % (x, arraylist[j])
           temp.append(arraylist[j])
           j += 1
   while i <= middle:
       temp.append(arraylist[i])
       i += 1
   while j <= last:
       temp.append(arraylist[j])
       j += 1
   for i in range(0, last - first + 1):
       arraylist[first + i] = temp[i]

def merge_sort(arraylist, first, last):
   if first < last:
       middle = (first + last) / 2
       merge_sort(arraylist, first, middle)
       merge_sort(arraylist, middle + 1, last)
       merge(arraylist, first, middle, last)

if __name__ == "__main__":
lst = [2,3,8,6,1]
merge_sort(lst, 0, len(lst) - 1)

运行结果:

1
2
3
4
5
(6,1)
(2,1)
(3,1)
(8,1)
(8,6)

(我的代码缩进似乎出了点问题,本身这段归并排序也是从网上找来的,所以混合了不少tab和whitespace,弄了好久都是error indent,最后我无奈用whitespace来做缩进,好歹调试通过了!)

悲惨的CodeJam2012

资格赛晋级之后还稍稍得到了点自信,起码原来只能远观CodeJam的我现在也能继续走进看看了,不过家下来的Round1三场比赛直接被虐杀了,每一场都是最后,每个题都做不出来,说出来也是很惭愧的,但得承认自己这点能力,有人不到5分钟就能做对一个大题,而我5分钟可能都没读完题,更别说写代码了。

但最重要的是理解题目,能够理解了也就完成了90%,剩下的工作就是coding的过程,参加比赛的人基本都是有备而来,各种模版、脚本都准备好了,写起来非常快。对于非英语系国家的选手来说,读题似乎是很大的障碍,起码会比别人慢好多倍,这点只能多多看英文的资料来弥补了!

今天的Round1第三场出现了两个动态规划的题,感觉知道了怎么做可就是不会写,我也就是停留在知道有动态规划这种变态方法的层面上,真让我写出来可不行,抱着《算法导论》看了半天也是毫无头绪……

毕竟不是Acmer,咱也就只能止步于此啦!希望进入下一轮的国人们继续加油!

web.py真不好用

本来做了个一个todo的app感觉python写web app真得好快啊,于是想加入一个user的功能,这样注册的用户就可以写自己的todo了,不过这么一加才感觉到web.py真不好用了,特别是session,还有templates也不怎么好用,官方的文档不知道为何也不给个完整版的,api只是写出来了,但基本都不说是什么作用,想清楚知道运行机制那就得看源代码了,还好不“那么”长!

哎,暂时就放一下web.py吧!到头来过度依赖框架绝对不是什么好事,嗯,不是什么好兆头现在的我!

用python备份mysql

备份数据库的重要性不想多说了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#! /usr/bin/env python
# coding: utf-8

import os
import sys
from datetime import *
import time

#mysql's path
mysql_path = '/usr/local/mysql/bin/'
#edit this if your mysql username and password is not like this:
mysql_username = 'root'
mysql_password = 'xxxxxx'
#backup path
backup_path = 'mysqlbackup/'

def validate_backup_path():
if os.path.exists(backup_path) == False:
print "I create a directory here: %s " % backup_path
os.mkdir(backup_path)

def backup_all_databases():
filename = "all_%s.sql" % (datetime.utcfromtimestamp(time.time()))
os.system("%smysqldump -u%s -p%s --all-databases > '%s%s'" % (mysql_path, mysql_username, mysql_password, backup_path, filename))

if __name__ == "__main__":
validate_backup_path()
backup_all_databases()
print "finish!!!"

这个是修改了一次的备份脚本,本来是分数据库备份的,无奈制定了分数据库备份需要输入密码,这就不容易加入cron来自动完成了,不过听说在my.cnf中加入mysqldump的设置可以避免输入账号密码,这也算是一个办法吧,不过还是本着简化的思想,只导出所有数据库出来就好,配合着上一篇的dropbox自动备份,这样VPS的灾备就稍稍好些了:)

dropbox自动备份VPS

这里看过的文章,感觉很不错,于是发表过来,但发现有点问题,我就自己再改改。

安装linux下的dropbox(我的是32为系统,如果是64位在下面地址末尾加上“_64”即可):

1
wget -O dropbox.tar.gz http://www.dropbox.com/download?plat=lnx.x86

解压后在.dropbox-dist目录中有dropboxd可执行文件,让其在后台执行(就是在执行文件后面加“&”符号)

1
~/.dropbox-dist/dropboxd &

此后会出现一个地址,你得复制出来然后在浏览器中打开,之后就是注册或者登陆dropbox,这样就激活了在这台机器上的dropbox,就可以同步文件了。

把需要同步备份的文件软连接到Dropbox这个目录中:

1
2
3
cd ~/Dropbox
ln -s /home/wwwroot/
。。。

此时dropbox已经在帮你同步了,你可以通过web端观察。

我看到的文章中带着一个自动运行的脚本,用sh写的,我发现在主机上运行没反应,不会sh,会点python,于是我改写了一下,也是可用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#! /usr/bin/env python
# coding: utf-8

import sys
import os

def start():
print "Starting Dropbox..."
os.system("~/.dropbox-dist/dropboxd &")


def stop():
print "Stoping Dropbox..."
os.system("pkill dropbox")


def restart():
stop()
start()


if __name__ == "__main__":
action = sys.argv[1]
print "now action: %s" % action
if action == 'start':
start()
elif action == "stop":
stop()
elif action == "restart":
restart()
else:
print "Bad arguments!"

相应的crontab配置文件中这样写就好了:

1
2
3
#auto dropbox
0 4 * * * python /root/dropbox.py restart
0 5 * * * python /root/dropbox.py stop

如果没装crontab(极少情况),这样(也许你用apt-get来做这件事,我的是centos系统,用yum了):

1
yum -y install crontab

python让我很快乐:)

一开始在python用的是exec来执行命令发现不可用,于是用了os.system,查看pydoc才发现exec是执行python code用的……惭愧!

抓取一些网页上的表格

这些表格是所有参加比赛的学生成绩单,当然只有省份、学校、准考证、姓名、组别、奖项、是否进入决赛这几个列,我用python写的脚本来抓取,存入了本地的一个excel中,所以除了python环境之外还需要xlwt这个包包:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#/usr/bin/env python
#coding: utf-8

import urllib2
import re
import xlwt

link_list = []
xls = xlwt.Workbook()
table = xls.add_sheet("main")

for i in xrange(288, 318):
link_list.append("http://www.lanqiao.org/Article/ShowArticle.asp?ArticleID=%d"%i)

chinese = []
content = ''
while len(link_list) > 0:
for link in link_list:
print link
try:
content = urllib2.urlopen(url = link, timeout = 3).read()
link_list = link_list[1:]
except:
print "timeout!"
if link not in link_list[1:]:
link_list.append(link)
some = re.findall(u'<TR.*?<\/TR>', content.decode('gbk', 'ignore'), re.S)
for s in some:
chinese_value = re.findall(u'(?<=\u5b8b\u4f53>).*(?=<\/FONT>)', s)
chinese.append(','.join(chinese_value))

for line in xrange(0, len(chinese)):
tmp = chinese[line].split(',')
for i in xrange(0, len(tmp)):
table.write(line, i, tmp[i])
print "%d, %d, %s" % (line, i, tmp[i])

xls.save('x.xls')
print len(link_list)

链接抓取超时的时候会自动重新抓取,最终就有了一个叫x.xls的表格。其中有个省的数据有问题,可以在筛选中看到奖项这里面有个垃圾数据项,自己改改吧。

里面的正则表达式耗费了我好久时间!!!不过我还是很欣慰能找到这行:

1
(?<=\u5b8b\u4f53>).*(?=<\/FONT>)

前面两个unicode是“宋体”,这个得看具体的html代码了,所以这个脚本没什么通用性……