素因数分解(線形篩)
init
- 整数\(N\)を与えると、前計算として\(N\)以下の合成数\(i\)について\(i\)の自明でない(\(1\)でも\(i\)でもない)約数の1つを求める
- それと同時に、\(N\)以下の素数のリストも求める
- 以降の関数に\(N\)より大きい値を与えると補完のためにinitが呼ばれる。これは最初にinitに十分大きい値を与えることで回避できる
- 計算量は\(Ο(N)\)
linear_sieve
- コンストラクタ。initを呼ぶ
primearray
- 整数\(N\)を与えると、\(N\)以下の素数を昇順に並べたvectorを返す
- もし前計算が足りない場合、ちょうど補完するようにinitが実行される
- 計算量はinitのもの+\(Ο(N/log(N))\)
divisor
- 整数\(N\)を与えると、前計算した\(N\)の自明でない約数の1つを返す
- もし\(N\)が素数なら\(N\)を、\(1\)なら\(1\)を、正でないなら\(-1\)を返す
- もし前計算が足りない場合、ちょうど補完するようにinitが実行される
- 計算量はinitのもの+\(Ο(1)\)
factorization
- 整数\(X\)を与えると、\(X\)を素因数分解し、結果を大きい順に並べ、vectorとして返す
- もし前計算が足りない場合、ちょうど補完するようにinitが実行される
- 計算量はinitのもの+\(Ο(log(N))\)
exp_enum
- 整数\(N,M,mod\)を与えると、\(0^M,1^M,...,N^M\)をそれぞれ\(mod\)で割ったあまりを計算し、vectorとして返す
- \(N,M\)が負である場合、空のvectorを返す
- もし前計算が足りない場合、ちょうど補完するようにinitが実行される
- 計算量はinitのもの+\(Ο(N + N \frac{log(M)}{log(N)})\)
class linear_sieve{
// Copyright (c) 2023 0214sh7
// https://github.com/0214sh7/library/
private:
long long S=0;
std::vector<long long> divisor_,prime;
public:
void init(long long N){
S=N;
prime.clear();
divisor_.resize(N+1);
for(long long i=0;i<N+1;i++){
divisor_[i]=0;
}
if(N>=1){
divisor_[1]=1;
}
for(long long i=2;i<=N;i++){
if(divisor_[i]==0){
divisor_[i]=i;
prime.push_back(i);
}
for(long long j=0;j<=prime.size() && prime[j]<=divisor_[i] && i*prime[j]<=N ;j++){
divisor_[prime[j]*i]=prime[j];
}
}
}
linear_sieve(long long N){
init(N);
}
std::vector<long long> primearray(long long X){
std::vector<long long> R;
if(X<=1)return R;
if(S<X)init(X);
for(long long i=0;i<prime.size();i++){
if(prime[i]<=X){
R.push_back(prime[i]);
}
}
return R;
}
long long divisor(long long X){
if(X<=0)return -1;
if(S<X)init(X);
return divisor_[X];
}
std::vector<long long> factorization(long long X){
std::vector<long long> R;
if(X<=1)return R;
if(S<X)init(X);
R = factorization(X/divisor_[X]);
R.push_back(divisor_[X]);
return R;
}
std::vector<long long> exp_enum(long long N,long long M,long long mod=LLONG_MAX){
std::vector<long long> R;
if(N<0 || M<0)return R;
if(S<N)init(N);
for(long long i=0;i<=N;i++){
R.push_back(-1);
}
R[0]=0;
if(N>=1)R[1]=1;
for(long long i=0;i<prime.size();i++){
if(prime[i]>N)continue;
long long r=1,b=prime[i],e=M;
while(e){
if(e&1){
r=(r*b)%mod;
}
b=(b*b)%mod;
e >>=1;
}
R[prime[i]]=r;
}
for(long long i=0;i<=N;i++){
if(R[i]!=-1)continue;
R[i] = (R[i/divisor_[i]]*R[divisor_[i]])%mod;
}
return R;
}
};
使用例
実行コード
#include <bits/stdc++.h>
class linear_sieve{/*省略*/};
int main(void){
linear_sieve sieve(1000);
std::cout << "100以下の素数のリスト" << std::endl;
std::vector<long long> P = sieve.primearray(100);
for(int i=0;i<P.size();i++){
std::cout << P[i] << " ";
}
std::cout << std::endl << std::endl;
std::cout << "1~10の約数の一つ" << std::endl;
for(int i=1;i<=10;i++){
std::cout << sieve.divisor(i) << " ";
}
std::cout << std::endl << std::endl;
std::cout << "4,12,57,97,210の素因数分解" << std::endl;
std::vector<long long> number={4,12,57,97,210};
for(int j=0;j<5;j++){
std::vector<long long> F = sieve.factorization(number[j]);
for(int i=0;i<F.size();i++){
std::cout << F[i] << " ";
}std::cout << std::endl;
}
std::cout << std::endl;
std::cout << "0~10について、i^3" << std::endl;
std::vector<long long> E = sieve.exp_enum(10,3);
for(int i=0;i<E.size();i++){
std::cout << E[i] << " ";
}
std::cout << std::endl;
return 0;
}
出力
100以下の素数のリスト
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
1~10の約数の一つ
1 2 3 2 5 2 7 2 3 2
4,12,57,97,210の素因数分解
2 2
3 2 2
19 3
97
7 5 3 2
0~10について、i^3
0 1 8 27 64 125 216 343 512 729 1000
更新履歴
日時 | 内容 |
---|---|
2023/06/29 | ライセンスのコメントアウトを変更 |
2021/03/24 | 素因数分解(線形篩)を追加 |