Cod sursa(job #412577)

Utilizator chri5tyJohn Doe chri5ty Data 5 martie 2010 20:13:33
Problema Ciurul lui Eratosthenes Scor 100
Compilator cpp Status done
Runda Arhiva educationala Marime 5.9 kb
/*
Rezolvarea este bazata pe 'Ciurul lui Eratostene' ( http://ro.wikipedia.org/wiki/Ciurul_lui_Eratostene )

*/

#include <stdio.h>
#include <math.h> //sqrt

#define REGISTER_SIZE (sizeof(int)*8)//marimea unui registru din CPU;
//folosind int-uri cu aceeasi dimensiune ca a registrului,
//vom beneficia de viteza mai mare de acces la nivel de CPU

#define getBit(bitfield,i) ((bitfield[(i)/(REGISTER_SIZE)]>>((i)%(REGISTER_SIZE)))&1)
#define setBit(bitfield,i) (bitfield[(i)/(REGISTER_SIZE)]|=(1<<((i)%(REGISTER_SIZE))))

int c=0,s=sizeof(int);

long int N; //datele de intrare

unsigned int*bitField=0;
unsigned int n; //spatiu alocat dinamic

//variabile folosite in program
unsigned int i,j,k,bitMask=0,currentBitMask,offset,a,b; 
unsigned int*p,*stop;

FILE*f;


int main(){

	
/*	
	freopen("bitcell3.in","r",stdin);
	//freopen("bitcell3.out","w",stdout);
	scanf("%d",N);
*/
	f=fopen("ciur.in","r");
	if(!f) return 1; //lipseste fisierul de intrare
	fscanf(f,"%ld",&N);
	fclose(f);
	
	//conditii preliminare
	if(N<0||N>2000000)
	{
		puts("-1");//input invalid
		return 1;
	}
	if(N<2)
	{
		puts("0");//zero
		return 0;
	}
	
	//alocam pana la 2 000 000 biti, cate unul pentru fiecare numar de la 1 la 2 000 000
	//bitul i va fi 1 daca numarul i nu este prim, iar 0 daca
	
	n=N/REGISTER_SIZE; //alocam spatiu pentru cei N biti
	if(N%REGISTER_SIZE)n++; //rotunjind prin adaos, la urmatorul int
	
	bitField=new unsigned int[n];//alocam spatiul necesar (conform problemei, maxim 250 000 bytes, adica ~244 KB)
	/*
	 bitul cel mai nesemnificativ, din bitField[0] corespunde lui 1
	*/
	
	//preliminar, excludem numerele pare, initializand vectorul cu bitii 0 si 1, alternativ
	//1)creem o masca de biti cu 0 si 1, de marimea int-ului (16,32,64, sau cine mai stie...)

	bitMask=0xAAAA;// 1010 1010
	for(i=16;i<REGISTER_SIZE;i+=16)//din 16 in 16 biti
	{
		bitMask<<=16;
		bitMask|=0xAAAA;// 1010 1010
	}
	
	//2)aplicam masca in tot vectorul, initializandu-l si eliminand numerele pare

	p=bitField; //p = cursor
	stop=bitField+n;//stop = pozitia de oprire, la finalul zonei alocate

	while(p<stop) //cat timp cursorul nu a ajuns la final,
		*p++=bitMask; //copie int-ul, apoi treci la pozitia urmatoare
	
	//Am folosit implementarea cu pointeri doar pentru a grabi putin executia, la nivel de asamblare

	/*
			In vederea implementarii algoritmului, vom lua fiecare numar impar mai mare decat 2
		si vom exclude multiplii sai din lista numerelor prime.
	
		Astfel, la nivel de bit, bitField[i*k]=1, unde k*i reprezinta multiplii lui i
	*/
	
	//in momentul de fata, 2 este marcat ca neprim, iar 1 ca prim
	bitField[0]^=3; //corectam printr-o operatie XOR pe biti
	
	unsigned int end=(unsigned int)sqrt((unsigned int)N);
	for(i=3;i<=end;i+=2) //numerele impare din 2 in 2, incepand cu 3...
	{
		if(0==getBit(bitField,i-1)) //daca bitul este zero, adica numarul este prim...
		{
			
			if(i<REGISTER_SIZE)
			{
				/* Observatie:
					Pentru numerele mai mici decat `REGISTER_SIZE`, se poate crea o masca de biti,
				care aplicata pe bitField cu operatorul |, marcheaza numerele neprime.
					
					Exemplu: i=3, REGISTER_SIZE=32
				MSB                                 LSB
				0100 1001 0010 0100 1001 0010 0100 1001
					
					Notam:
				reg = REGISTER_SIZE
				i = numarul curent
				n = cmmmc(i,reg)   --cel mai mic multiplu comun
				m = n/reg
					In masca de biti, la fiecare i biti, se repeta un 1, iar la fiecare n biti se repeta
				alinierea bitilor in int-uri. Am notat cu m = numarul de int-uri dintr-un ciclu complet.
					Dupa ce am realizat prima masca (unde bitul cel mai nesemnificativ este 1), celelalte
				configuratii le obtinem printr-o rotatie pe biti, la stanga (sunt intotdeauna i configuratii
				diferite).				
					Astfel: pentru i=3, REGISTER_SIZE=32, avem
				MSB                                  LSB
				0100 1001 0010 0100  1001 0010 0100 1001  -- configuratia 1
				1001 0010 0100 1001  0010 0100 1001 0010  -- configuratia 2
				0010 0100 1001 0010  0100 1001 0010 0100  -- configuratia 3

					Cu un offset corespunzator, fiecare masca, aplicata pe bitField din m in m pozitii,
				va marca automat numerele neprime.
				*/

				
				bitMask=1;
				for(j=0;j<REGISTER_SIZE/i;j++)//folosim variabila j pentru a adauga bitii de 1
				{
					bitMask<<=i;
					bitMask|=1;
				}
				//am creat prima masca de biti, de dimensiunea unui int
				
				/* 
				R=REGISTER_SIZE
				i=numarul prim
				R*a=int-ul pe care aplicam masca (raportat la bitField)
				i*b=primul bit din masca (raportat la bitField)
				X=offsetul bitului de inceput (raportat la bitMask)
				
				avem relatia: R*a+X=i*b, unde:
				-X ia valori de la 1 la i
				-r = constant
				-i = constant
				-a si b sunt variabile, cautam b=f(a)
				
				pentru a de la 0 la i
					b = (R*a +X)/i  trebuie sa se imparta exact
				*/
				
				for(offset=0;offset<i;offset++) //offset = `x` de mai sus
				{
					for(a=0;a<i;a++)
						if(!((REGISTER_SIZE*a+offset)%i))
							break; //l-am gasit pe `a`
					
					currentBitMask=bitMask<<((offset+i-1)%i);
					
					for(j=0;j*i+a<n;j++)
						bitField[j*i+a]|=currentBitMask;
				}
				bitField[0]^=1<<(i-1); //repara bitul i (l-am setat la 1 odata cu restul lui bitField)
			}
			else 
			{/*	Aici vroiam sa mai fac o optimizare...
				Distanta dintr 2 biti cu valoarea 1 este mai mare decat REGISTER_SIZE, deci avem int-uri neatinse, iar numarul de masti
			diferite este = REGISTER_SIZE. Mai trebuia sa gasesc cate un offset pentru fiecare din mastile acestea si sa le aplic pe bitField,
			ca in liniile de mai sus.
				Din pacate, am ramas la varianta de mai jos:
			*/
				for(j=2;j*i<=((unsigned int)N);j++)
					setBit(bitField,i*j-1);
			}
		}
	}
	f=fopen("ciur.out","w");

	j=0;
	for(i=0;i<((unsigned int)N);i++){
		if(0==getBit(bitField,i))
			j++;
	}
	fprintf(f,"%u",j);
	
	fclose(f);

	return 0;
}