Факторизация N чисел за O(N)

23.11.2013 - Valentin Gubarev

Данный алгоритм позволяет за линейное время найти все простые числа от 0 до N включительно, также за линейное время факторизовать все числа на данном интервале.

Реализация алгоритма написана так, чтобы минимизировать объем выделяемой памяти.

//Поиск простых чисел от 2 до N за O(N)
//Поиск наименьших делителей
//Если N до 2,5 млн., то достаточно uchar (1 байт), иначе short (2 байта)
#include<vector>
#include<cmath>
#include<algorithm>

template<class T>
class prime{
private:
 std::vector<unsigned>pr;
 std::vector<t>lp;//хранить индексы на pr, в которых находится наименьший делитель >1, или 0 - если число простое
public:
 prime(){};

 //Находит наименьшие делители для чисел от 0 до limit включительно
 prime(unsigned limit){
  lp.resize(++limit,0);
  pr.clear();
  pr.push_back(1);
  for(unsigned i=2;i<limit;++i){
   register unsigned max_index=lp[i];
   if(max_index==0){
    max_index=pr.size();
    pr.push_back(i);
   }
   register unsigned d;
   for(unsigned j=1;j<=max_index && (d=i*pr[j])<limit;++j)
    lp[d]=j;
  }
 }

 //Возвращает true, если число простое
 //И false, если нет, или неизвесно
 bool is_prime(unsigned number){
  return number<lp.size() && !lp[number];
 }

 //Возвращает порядковый номер простого числа за O( ln(pr.size()) )
 //0 - если число не простое, или неизвестно
 unsigned sequence_number(unsigned prime_number){
  if(!is_prime(prime_number))
   return 0;
  return std::lower_bound(pr.begin(),pr.end(),prime_number)-pr.begin();
 }

 //Возвращает простое число по его порядковому номеру,
 //0 - если такого простого числа нет в pr
 unsigned return_prime(unsigned sequence_number){
  if(sequence_number && sequence_number<pr.size())
   return pr[sequence_number];
  return 0;
 }

 //Возвращает наименьший делитель, 0 - если неизвесно
 unsigned least_divisor(unsigned number){
  if(number>=lp.size())
   return 0;
  if(is_prime(number))
   return number;
  return pr[lp[number]];
 }

 //Возвращает наибольшее число, для которого известен наименьший делитель
 unsigned limit(){
  return lp.size()-1;
 }

 //Возвращает факторизацию числа
 std::vector<unsigned> factorize(unsigned number){
  std::vector<unsigned> v;
  if(number<lp.size()){
   while(!is_prime(number)){
    v.push_back(pr[lp[number]]);
    number/=pr[lp[number]];
   }
   v.push_back(number);
  }
  return v;
 }
};

#include<iostream>
#include<ctime>

int main(){//Пример использования
 
 clock_t t_start=clock();
 prime<unsigned char>p(2500000);//249 (максимальный индекс)
 clock_t t_finish=clock();

 std::cout<<"work_time = "<<t_finish-t_start<<"\n";

 unsigned cnt_prime=0;
 for(unsigned i=10000;i<=11000;++i)
  if(p.is_prime(i))
   cnt_prime++;
 std::cout<<"cnt_prime (10000 to 11000) = "<<cnt_prime<<std::endl;

 std::cout<<"number_prime (13) = "<<p.sequence_number(13)<<std::endl;

 std::vector<unsigned> v=p.factorize(827364);
 unsigned mult=1;
 for(unsigned i=0;i<v.size();++i){
  std::cout<<v[i]<<" *"[i+1<v.size()];
  mult*=v[i];
 }
 std::cout<<"= "<<mult<<std::endl;

 return 0;
}
Лицензия Creative Commons
Code More Team - GitHub