Find duplicated Number and Cycle detection

四月 11th, 2012

一个有趣的问题,据说这个题目耗费了Don Knuth 24小时解决。一起来看看。

# "You are given an array of integers of length n, where each element ranges
#  from 0 to n - 2, inclusive.  Prove that at least one  duplicate element must
#  exist, and give an O(n)-time, O(1)-space algorithm for finding some
#  duplicated element.  You must not modify the array elements during this
#  process."

这有几个重要的约束,O(n),O(1)的复杂度,不能修改这个数组。可能有多个数重复了,但至少有一个数重复了。

首先第一个证明问题,等价于n个鸽子,n-1个笼子,那么至少有一个笼子里面有2个鸽子,就是鸽笼原理(抽屉原理), 反证法可以证明。

难的是第二个问题,假设a[0, n], 值都在0,1, …, n-2 范围内。如果扫描这个数组,重复的会出现第二次(废话,囧),
关键是只能用O(1)的空间,否则用空间记录出现过的就行了。如果把数组看成一个映射,i -> f(i) = a[i], 那么这个问题可以转换成更抽象的模型。 举个例子:

n = 6
index: 0 1 2 3 4 5
value: 1 4 0 0 3 2

其index对应value映射为为0->1, 1->4, 2->0等等,那么把这个图画出来就是这样:

 

这个问题转换为求图中环开始的点,因为出现环说明某个点重复出现了。从5开始遍历这个图会在0处发现环,为什么选取5,因为5肯定为一个起点,并且不在0~N-2内。其实只要选取不孤立的那个点当作起点就可以检测环,极端情况比如:

n = 6
index: 0 1 2 3 4 5
value: 0 1 2 3 4 4

选择index=5还是可以发现环,如果选取0就发现不了4和5之间的那个环。

[Cycle detection]是一个经典的计算机问题。经典的算法是Floyd’s cycle-finding algorithm,这个算法简单而优美。严格的数学证明当然可以,也能更明显的从现实经验得出结论。如果一个赛道中间出现某个环(分两种情况,赛道本身是环、赛道前面有一段没环而中间出现一个环9字形),求这个环的周长C。让两位运动员同时出发,并且P1的速度是P2的两倍,当他们第一重新相遇的时候,一定是在环的某个点上,并且其路程之差为这个环的周长的K倍(K>=1),这解决了一部分问题,我们知道了KC的值,如果K==1,则得出结果,说明两人刚好是在环开始点相遇。否则就是在环内其余点相遇,可以得知现在P2的路程为KC(P1的路程为2KC), 如果让P3以和P2同样的速度从起点开始,P2继续从相遇点开始跑,那么P2和P3肯定还会相遇,并且相遇的点一定为环开始点! 回到这个问题,这个index的值就是重复的值。代码描述如下:

int detectCycle(int* seq, int Num)
{
    int slow = Num -1;
    int fast = Num -1;
    while(1) {
        slow = seq[slow];
        fast = seq[seq[fast]];
        if(slow == fast)
            break;
    }
    int finder = Num - 1;
    while(1) {
        slow = seq[slow];
        finder = seq[finder];
        if(slow == finder)
            break;
    }
    return finder;
}

算法描述很简单,但其中思维的却很有乐趣。以前同样有一个问题,检测一个链表是否有环,这是由此出来的一个特例,因为对于一个链表的每个节点除了头节点都有一个前节点和后节点(无环则末节点指向空),而图是一个更通用的模型。

int list_has_cycle(NODE *list)
{
    NODE *fast=list;
    while(1) {
        if(!(fast=fast->next)) return 0;
        if(fast==list) return 1;
        if(!(fast=fast->next)) return 0;
        if(fast==list) return 1;
        list=list->next;
    }
    return 0;
}

eproject.el为Emacs加上工程

三月 8th, 2012

我之前一直用的是project-mode.el来管理项目,在碰上代码很多的工程时还是有点不方便,源文件太多速度有点慢。快速检索文件还是可以,需要指定代码目录,可以增加目录。工程的概念还是不太直观,主要用来快速查找文件。 以前看有同学推荐过eproject, 当时没仔细看。这会儿想自己写一个,而今天偶尔试用了一下这个eproject.el, 这才是真正需要的好东西啊。

一个工程包含的是经常需要访问的文件,另一个重要的地方是可以自己绑定Make, clean, run, configure等命令。 一组常用命令加文件检索,非常方便。看下面的配置文件很清楚,make绑定到了一组命令。

(setq prj-tools
‘((“Make” “cd ~/source/Panda/; ./run.sh -c;” “f9″)
(“Clean” “cd ~/source/Panda/; ./run.sh -x;” “C-f9″)
(“Run” “cd ~/source/Panda/; bochs;” “f8″)
(“Stop” “-e eproject-killtool” “C-f8″)
(“Configure” “./configure” nil)
(“Explore Project” “nautilus –browser `pwd` &” nil)
(“XTerm In Project” “xterm &” nil)))

另外再推荐一个扩展viewer.el, 这个可以模拟vi里面的快捷键,其实我不是觉得vi的快捷键好,而是vi分为几个模式,编辑模式、浏览模式。这对emacs有些用,因为往往我打开一个文件只是看看,编辑的时候少,有时候按错了键使得文件内容不经意就改变了。所以通过这个扩展,默认打开一个文件都是浏览模式,还可以设置和vi一样的移动光标的快捷键,当需要进行编辑操作的时候按下i键进入编辑模式。状态栏可以显示当前所处的模式。

(add-hook ‘find-file-hook ‘view-exist-file)
(global-set-key (kbd “C-o”) ‘view-mode)

维权指南

十二月 22nd, 2011

 
我为什么一直没找消协和质检部门?—- 解决西门子冰箱门问题的“正当途径”,这得要多曲折啊,悲哀。

寒冷的睡不着的早晨的杂感

十二月 22nd, 2011

标题要长!

平常都睡到9点多才起床,今天早上3点多居然醒了,然后一直睡不着,起来随便写写。上海的冬天非常冷,还有一点点不适应,买了个电热毯。

刚才一直在想一个事情,在我毕业的时候给一个大学同学介绍了个工作,然后他就辞职去了那。昨天打电话和他聊了一段时间,感觉那边的情况不如预期,公司很多人都渐渐离去。这样心里就有些惭愧,当然我知道并不是我使得他辞职的,他那段时间正好想离职另外换工作。惭愧的原因在于我在这过程中至少当了个”说客”,帮那边公司说了一些好话,因为两边都是熟人,所以这完全不是出于利益目的。同学人很好,也没对我抱怨或者其他不满心情,这更加深了我的内疚感。这算是一个教训吧,永远不要站在自己的立场试图去影响别人的选择。公司招人也是一样的道理,不要对自己公司进行浮夸或者其他的不客观的形容,如果招来的人做得不开心这对两方都是损失。现在回想一下,我当时充当中间人也是受了那边公司的蛊惑,用蛊惑这个词因为当时我完全相信了对方。

再说说关于工作的感想,我觉得对于码农来说我们都还算是幸运,我所接触的一些码农,大部分还是对此是感兴趣的。做一件感兴趣而能养活自己的事情确实应该算幸运。这也正好是上次年纪比较大的同事吃饭的时候他一直说的”被老婆说,你啊lucky而已”。有时候会觉得老员工比我有激情了,这实在不好,改之。很多事情,开始觉得没什么意思,那是因为没做进去,做进去自然会有好玩的地方。码农有一个比较通用的惯例,看别人写的东西都觉得不那么顺心,所以要造自己的轮子并欣赏自己的轮子。这也能看出写代码和写文字的另外一点共同之处,应该有一点精神追求在里面,即使你做的东西要对着钱的。据说今年的毕业生非常好找工作,基本被抢了,另外一些经常听到的声音就是泡沫要来了。现在不少毕业生也不想出来到公司做,很多会倾向于进事业单位和研究所,相类似公务员考试成为国考,大家都往体制内挤啊。我在实验室的时候,那段时间《窝居》真他妈火啊,瞅瞅悲催的C++程序员小贝同志,我后面那排师弟师妹们就吼着要进体制内。果然,还是蛮快的。

睡觉之前看了看下厨房这个网站,作为豆瓣忠实用户,在这网站刚刚上线就上去观望了。当时觉得没那么有吸引力,不过现在可能用户多了,而且自己也想周末学学做饭,所以对这有好感。豆瓣系的小清新风格。创建人有这么句话:”下厨房是有关于理想,而不是一桩生意。理想这种形而上的东西,会让我觉得,就算是倒闭,也要有尊严地倒下去。我们期望赋予下厨房一些家庭生活理念,不会触及外出就餐的需求,甚至广告…”。那篇报道算是软文也好,这句话还是蛮认同的。这让人联想到老罗的创业理念啊,不管泡沫与否,互联网已经不是那么有技术门槛的行业了,办个网站这样的事情很多人能做。怎样让一个网站和其他的网站看起不一样?对于年轻的用户,有价值观输出的网站有吸引力,比如豆瓣,如果没有豆瓣”文艺”这个词应该这时候没这么流行。即使有时候豆瓣的一些改版让用户不满意,但大部分还是会留在上面,有价值观输出的网站会引导你。回想我之前待过的互联网公司(房产相关网站),上线一段时间大家还信心满满的,大家每做一些相对于同类网站的小改进都会激动,心里回想”你看他们的结果没我们的好,我们的信息更有价值”,诸如此类。后来就是慢慢推广不开,这是什么原因?有人会说啊,这信息有用啊,而且买房的很多人都要在网上搜啊,你看有人就是单纯做了个黄页的网站,就有多大的流量。这正是问题所在,房产网站的用户不是偏年轻的,很多年纪稍微大点的用户都不会去浏览器地址栏输入地址(所以需要黄页),同样的道理他们不会去用心发现你所呈现的不一样的东西,他们可能更偏好于搜房网那种首页全是链接而且到处这类提供信息的常用网站,不能做到第一、第二完全是白费。

唉,扯完,睡觉。

A Emacs func

十二月 21st, 2011

这个操作好像经常要用到,拷贝当前光标连续的一段字符串(除了空白和换行), 写了个小函数来实现。

(defun get-continue-string ()
  (interactive)
  (skip-chars-backward "^ \n")
  (setq low (point))
  (skip-chars-forward "^ \n")
  (setq high (point))
  (copy-region-as-kill low high)
  (message (buffer-substring low high)))

(global-set-key (kbd "C-x y") 'get-continue-string)

A Basket-Ball Demo

十月 20th, 2011

最近闲暇时间用C++写了个小Demo,一个小小的篮球模拟。在学校的时候看过《人工智能编程精粹》,里面有个足球模拟,看起来还比较逼真。我这个篮球模拟是比较类似的,主要好玩的地方是在于状态机。图形方面做得很简单,还是用OpenGL来实现的,另外用了一个库glui,这个东西很好,把GUI方面琐碎的事情就简化了。整个效果图如下,这可是湖人对火箭噢。

调试状态机是个很有趣的过程,每一个球队有自己的状态机,分为进攻状态、防守状态、准备发球状态,每个球员也有自己的状态机,如下图所示。这里使用的是状态模式,把复杂的转移逻辑分散到各个状态节点,这正是状态模式的精华啊。现在这个还只是个粗糙的版本,虽然看得出来有那么点意思,规则都出来了,但是每个球员的跑位不逼真,没有多少智能的感觉。当篮球碰到边界的时候自动反弹,这点有点假,不过这也简化了不少比较繁琐的捡球和发球动作。当然现在的规则都比较简单,连三分和两分都没有分出来,罚球也没有,哈哈。现在的状态机看起来大部分时间还可以,很少的时候会出现一些比较反常规的现象。把每个状态转移过程在画面中显示出来能比较直观的去调试。

下面这个是球员的状态转移图,也就是FieldPlayerStates.cpp实现的。球队的状态机要简单一些,只有三个。

程序在这里GitHub:BasketBall,感兴趣的可以看一下,也有7000行的代码了,也有点乱:). 后面有时间再调试一下,慢慢细化,球员的站位和防守动作做到更智能些。

Pages: 1 2 3 4 5 6 7 8 9 10 Next