存档

文章标签 ‘算法’

sina面试猴王问题

2009年4月14日

近两天的phpchina上逛的时候,无意中发现有XD在谈论sina面试题目中的猴王问题。

仔细看了一下,原来这猴王问题就是传说中的约瑟夫环问题。约瑟夫环(Josephus)问题是由古罗马的史学家约瑟夫(Josephus)提出的。

约瑟夫?何许人也?

他参加并记录了公元66—70年犹太人反抗罗马的起义。约瑟夫作为一个将军,设法守住了裘达伯特城达47天之久,在城市沦陷之后,他和40名死硬的将士在 附近的一个洞穴中避难。在那里,这些叛乱者表决说“要投降毋宁死”。于是,约瑟夫建议每个人轮流杀死他旁边的人,而这个顺序是由抽签决定的。约瑟夫有预谋 地抓到了最后一签,并且,作为洞穴中的两个幸存者之一,他说服了他原先的牺牲品一起投降了罗马。

约瑟夫环问题的具体描述是:设有编号为1,2,……,n的n(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,报m 的人出圈,再从他的下一个人起重新报数,报到m时停止报数,报m的出圈,……,如此下去,直到所有人全部出圈为止。当任意给定n和m后,设计算法求n个人 出圈的次序。

无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。

为了讨论方便,先把问题稍微改变一下,并不影响原意:

问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。

我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):

k  k+1  k+2  … n-2, n-1, 0, 1, 2, … k-2并且从k开始报0。

现在我们把他们的编号做一下转换:

k     –> 0

k+1   –> 1

k+2   –> 2

k-2   –> n-2

k-1   –> n-1

变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x’=(x+k)%n

如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 —- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:

令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]

递推公式

f[1]=0;

f[i]=(f[i-1]+m)%i;  (i>1)

有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1

由于是逐级递推,不需要保存每个f[i],程序也是异常简单:

#include <stdio.h>

int main(){

int n, m, i, s=0;

printf (“N M = “); scanf(“%d%d”, &n, &m);

for (i=2; i<=n; i++) s=(s+m)%i;

printf (“The winner is %d\n”, s+1);

}

这个算法的时间复杂度为O(n),相对于模拟算法已经有了很大的提高。算n,m等于一百万,一千万的情况不是问题了。可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。

采用PHP实现如下:

/**
 * 线性表应用(选首领问题)
 *
 * @param  $n  总数
 * @param  $m  当报数到 m 时,m出列
 * @return 最后剩下的数字
 */
function yuesefu($n,$m) {  
    $r=0;  
    for($i=2; $i<=$n; $i++) {
            $r=($r+$m)%$i;  
    }
    return $r+1;  
}  
print_r(yuesefu(3,3));
?>

PHP教程 , , , ,

googlel pr and alexa

2009年3月20日

这两天想到一个问题,想直接在我的站点上面标出Google PR和Alexa世界排名,别说这两个东西不知道,不知道的同学去Google or Baidu哈。

于是看这两个的算法接口,将这两个接口放到我的站点上面以供朋友使用,测试地址如下:

Google PR:http://www.phpcoding.cn/lib/google.pr.php?url=http://www.phpcoding.cn

Alexa 排名:http://www.phpcoding.cn/lib/alexa.php?url=http://www.phpcoding.cn

若是哪位朋友想测试,抑或其它,可将url=后面的站点URL(即红色显示的URL)换成你自己的站点URL即可。

只是以上两均是以文本的方式进行显示,因为我爱好简约!若哪位朋友需要显示为精美图片,与我联系,另行制作。

考虑到大多数人都喜欢图形化的方式,因此采用CSS+DIV模拟了一个图形的方式,引用地址为:

Google PR:http://www.phpcoding.cn/lib/google.pr.php?pic=pic&url=http://www.phpcoding.cn

Alexa 排名:http://www.phpcoding.cn/lib/alexa.php?pic=pic&url=http://www.phpcoding.cn

对于显示图形的方式,只需要在页面上采用如下方式即可显示为图形效果:

<script language="javascript" src="http://www.phpcoding.cn/lib/alexa.php?pic=pic&url=http://www.phpcoding.cn"></script>
<script language="javascript" src="http://www.phpcoding.cn/lib/google.pr.php?pic=pic&url=http://www.phpcoding.cn"></script>

对于使用PHP获取Google PR和Alexa排名的代码稍后放出。

PHP教程 , , , , , , , , ,

MOD10,11双模校验码系统校验算法

2008年8月15日

MOD10,11双模校验码系统校验算法

关键词: 双模  校验码

本程序为账号校验码shell.采用MOD10,11双模校验码系统校验算法

校验算法:

(…((…(((M+an)//M*2)/(M+1)+an-1)//(M*2)/(M+1)+…+a1)//M*2)/(M+1)+…+a1)//M=1

式中:n—–包括校验字符在内的代码字符的总个数;

an—–为代码左端第1位数字;

M 与M+1——两个模数,M=10;

//M——式中前面的结果除以M后的整余数,若除尽无余数用M代替;

//(M+1)——-式中前面的结果除以(M+1)后的整余数

注:字符处理顺序从左至右。校验字符放在代码字符串的最右端。

awk -F ‘,’ ‘

function mod2(zh){

m=10;

p=m;

for(i=1;i<18;i++){

number=substr(zh,i,1);

p=((n=(p+number)%m)?n:m)*2%(m+1);

}

if(p<2){

p=1-p;

}else{

p=11-p;

}

return p;

}

{
merge=sprintf(“%s%s%s%s”, “60″,substr($1,1,7),substr($1,11,1),substr($1,13,7));

zhh=sprintf(“%s%s”,merge,mod2(merge));

if(length($3) == 8)

print zhh”|”$2″|”$3″|”$4″|\

PHP教程 , ,