2008年2月4日

 doc及原码点这里下载

 利用积性函数的优化.

这个文章主要介绍了3算法

1线性时间筛素数

2线性时间求前n个数的欧拉函数值

3线性时间求前n个数的约数个数

 

一、   首先介绍下积性函数。

 

下面是wiki的条目:

 

在非数论的领域,积性函数指有对于任何a,b都有性质f(ab)=f(a)f(b)的函数。

 

在数论中的积性函数。对于正整数n的一个算术函数f(n)当中f(1)=1且当a,b互质,f(ab)=f(a)f(b),在数论上就称它为积性函数。

若某算术函数f(n)符合f(1)=1,且就算a,b不互质,f(ab)=f(a)f(b),称它为完全积性的。

 

例子

φ(n) -欧拉φ函数,计算与n互质的正整数之数目

μ(n) -默比乌斯函数,关于非平方数的质因子数目

gcd(n,k) -最大公因子,当k固定的情况

d(n) n的正因子数目

σ(n) n的所有正因子之和

σk(n): 因子函数,n的所有正因子的k次幂之和,当中k可为任何复数。在特例中有:

σ0(n) = d(n)

σ1(n) = σ(n)

1(n) -不变的函数,定义为 1(n)=1 (完全积性)

Id(n) -单位函数,定义为 Id(n)=n (完全积性)

Idk(n) -幂函数,对于任何复数、实数k,定义为Idk(n) = nk (完全积性)

Id0(n) = 1(n)

Id1(n) = Id(n)

ε(n) -定义为:若n = 1,ε(n)=1;若n > 1,ε(n)=0。有时称为“对于狄利克雷回旋的乘法单位”(完全积性)

(n/p) -勒让德符号,p是固定质数(完全积性)

λ(n) -刘维尔函数,关于能整除n的质因子的数目

γ(n),定义为γ(n)=(-1)ω(n),在此加性函数ω(n)是不同能整除n的质数的数目

所有狄利克雷特性均是完全积性的

 

 

二、再介绍下线性筛素数方法

bool notp[mr];//素数判定

__int64 pr[
670000],pn,ans;//pr存放素数,pn当前素数个数。

 

void getprime()

{

    pn
=0;

    memset(notp,
0,sizeof(notp));

    
for(int i=2;i<mr;i++)

    
{

        
if(!notp[i])pr[pn++]=i;

        
for(int j=0;j<pn && pr[j]*i<mr;j++)

        
{

            notp[pr[j]
*i]=1;

            
if(i%pr[j]==0)break;

        }


    }


}


 

利用了每个合数必有一个最小素因子

每个合数仅被它的最小素因子筛去正好一次。所以为线性时间。

代码中体现在:

if(i%pr[j]==0)break;

pr数组中的素数是递增的,i能整除pr[j],那么i*pr[j+1]这个合数肯定被pr[j]乘以某个数筛掉。

因为i中含有pr[j],pr[j]pr[j+1]小。接下去的素数同理。所以不用筛下去了。

在满足i%pr[j]==0这个条件之前以及第一次满足改条件时,pr[j]必定是pr[j]*i的最小因子。

 

 

三、结合线性筛素数算法的优化算法

基于这个线性筛素数算法,我们可以很容易地得到某个数的最小素因子。

因为当i%pr[j]!=0的时候,最小素因子pr[j]i互质,满足积性函数的条件,可以直接得到f(i*pr[j])=f(i)*f(pr[j]).

不过当i%pr[j]==0时我们必须根据该积性函数本身的特性进行计算.或者在筛的同时保存并递推些附加信息.总之要O(1)求得f(i*pr[j])及完成递推附加信息.

 

下面的两个例子是欧拉函数phi和约数个数.这两个是最常用和最有优化价值的。

利用上面的性质都可以很容易地把前n个用O(n)时间推出来.

当然,利用这个性质还可以对其他积性函数进行优化,这里仅介绍两个常用和有优化价值的。

 

1)欧拉函数(phi)

传统的算法:

对于某素数pp|n(n能整除p)

if( (n/p) % i == 0 ) phi(n)=phi(n/p)*i;

else phi(n)=phi(n/p)*(i-1);

 

这个传统算法的性质正好用在筛素数算法中.

pn的最小素因子,n/p包含该因子p,phi(n)=phi(n/p)*i;否则phi(n)=phi(n/p)*(i-1);

ppr[j], n/pi, ni*pr[j].

 

 

2)约数个数(divnum)

约数不能像phi那么自然,但还是有不错的方法.

约数个数有个性质

divnum(n)=(e1+1)*(e2+1)...(ei表示n的第i个质因数的个数.)

传统方法就是对每个数分解质因数,获得各因数个数再用上式.

 

开一个空间e[i]表示最小素因子的次数

这次说直接点:

筛到i j个素数

 

对于divnum

如果i|pr[j] 那么 divnum[i*pr[j]]=divsum[i]/(e[i]+1)*(e[i]+2) //最小素因子次数加1

否则 divnum[i*pr[j]]=divnum[i]*divnum[pr[j]] //满足积性函数条件

 

对于e

如果i|pr[j]  e[i*pr[j]]=e[i]+1; //最小素因子次数加1

否则 e[i*pr[j]]=1; //pr[j]1

 

 

最后谢谢指点我的鱼牛~~

posted @ 2008-02-04 14:48 oyy 阅读(1827) 评论(6) 编辑

 

n阶常系数线性递推方程的矩阵乘方解法

    大家都知道fibonacci数列可以用矩阵  1 1   进行乘方以达到O(nlogn)的时间内求

                                       1 0

数列第n项。下面我们来考虑能不能将它推广到所有n阶常系数线性递推方程的求解。

 

我们设递推方程f(n)=a1*f(n-1)+a2*f(n-2)……+ak*f(n-k),其中ai(1<=i<=k)为常

数。在结果矩阵中,我们只关心第一行,则设结果矩阵A(n)

A(n)=     f(n),f(n-1)……,f(n-k+1)    A(n-1)=    f(n-1),f(n-2)……,f(n-k)

          ………………………                     ………………………

          ………………                           ………………

          ………                                 ………

我们要求的递推矩阵G需要满足A(n)=A(n-1)*G

 

    下面我们来观察我们来观察矩阵相乘的关系式A(n)[i,j]+=A(n-1)[i,k]*G[k,j];如果要确定A(n)[1,1]的位置是f(n),我们发现,A(n)[1,1]位置的值仅与 A(n-1)[1,k](即A(n-1)的一行)和G[k,1](即G的第一列)有关,就是对应位置乘积之和。这与我们上边的那条递推方程刚好有共通之处!就是对应位置成绩之和!所以G的第一列我们可以确定为

G=    a1,……

      a2,……

      ……

      ak,……

 

    同样地,G后边的数也应该满足对应位置成绩之和规则。由于A(n)第一行之后的位置我们所需要的仅是前一个位置的A(n-1),所以我们只要在G中相应的位置设置1,其余位置设0就行了。这样我们就构造出递推矩阵:

G=    a1,  1,  0,  0……,  0

      a2,  0,  1,  0……,  0

      a3,  0,  0,  1……,  0

      ……

      ak-1,0,  0,  0……,  1

      ak,  0,  0,  0……,  0

 

     

posted @ 2008-02-04 14:41 oyy 阅读(569) 评论(0) 编辑


2008年2月2日

摘要: Math库实用汇总在FP中,Math库为我们提供了丰富的数学函数。以下介绍在OI中可能会用到的Math库中一些函数、过程。使用方法:在程序头用Uses语句加载Math库例子:Program Ex_Math;Uses Math;Begin Writeln(hypot(3,4));End.函数介绍:l hypot原型:function hypot(x:float;y:float):float功能:返回...阅读全文

posted @ 2008-02-02 23:38 oyy 阅读(167) 评论(0) 编辑

baidu 太恶心了...

来试试这个...

#include<cstdio>
#include
<algorithm>
using namespace std;

int main()
{
    scanf(
"Hello world!\n");
    
return 0;
}

posted @ 2008-02-02 23:34 oyy 阅读(109) 评论(0) 编辑


  

posts - 4, comments - 6, trackbacks - 0, articles - 0

Copyright © oyy