Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Satik

Counting sort stringů

c-plus-plus

#include "stdafx.h" 
#include <iostream> // konzole
#include <windows.h> // vsechno mozny, mereni casu
#include <string> // stringy pro vypis
#include <fstream> // zapis do souboru

using namespace std;

// dostane pole, index a vrati string - stringy dlouhe 4 znaky totiz nejsou zakoncene nulou
string GetString(unsigned char * pole, int pos)
{
	string ret = "1234";
	for (int i=pos; i<pos+4; i++)
	{
		ret[i-pos] = pole[i];
		if (!pole[i]) 
		{
			ret.resize(i-pos);
			break;
		}
	}

	return ret;
}

// vygeneruje nahodny char v rozmezi 'a' az 'z'
unsigned char RandomChar()
{
	return rand()%26+97;
}

// vygeneruje nahodne stringy dlouhe 1-4 znaky, obsahujici jen povolene znaky
void Generuj2 (unsigned char * pole, int size)
{
	for (int i=0; i<size; i++)
	{
		int len=1+rand()%4;

		for (int j=0; j<4; j++)
		{
			if (j>=len)
			{
				pole[(i*4)+j]=0;
			}
			else
				{
					pole[(i*4)+j]=RandomChar();
				}
		}
	}
}

// posledni verze radici metody (pouziva se counting sort)
void Serad3 (void * data, int velikost)
{
	// pole slov je vlastne pole zaznamu, kde kazdy ma delku 4 znaky a pokud je slovo kratsi nez 4 znaky, tak se na konec dava nula
	unsigned int * pole = (unsigned int *) data; // udelame si tedy z dat pole intu, je rychlejsi vzit z pameti jeden int nez postupne 4 chary
	const int sizeCounts = 32*32*32*32; // potrebna velikost pole, kdyz velikost jednoho znaku bude mocnina dvou, bude se nam s tim lip delat nez s cislem 27 (26 pro abecedu a 1 pro nulu)
	int * counts = new int[sizeCounts]; // pole ukladajici pocty opakovani
	ZeroMemory(counts,sizeCounts*4); // vynulujeme ho, C++ nam promenne nenuluje
	
	// tady projedeme cely vstup, kazde slovo prepocitame na jeho odpovidajici hodnotu a pak do pole, ktery uklada pocet vyskytu pricteme jednicku
	for (int i=0; i<velikost; i++)
	{
		unsigned int val = pole[i];
		unsigned int tmp = val;
		unsigned int hash;

		// v tmp mame cele slovo, & 0xff ze slova vytahne jen posledni ascii znak
		// pak se odecte 96 (tam nekde zacina znak 'a') a pak se to zase posune o nekolik bitu doleva, podle toho, jakou prioritu ten znak pri razeni ma
		tmp = tmp & 0xff;
		tmp = tmp - 96;
		tmp = tmp << 15; // posun o 3x5 bitu (protoze pro ulozeni 32 hodnot je potreba 5 bitu, 32 = 2^5)
		hash = tmp;

		// tady to same, akorat si musime posunout val o osm bitu doprava, abysme brali druhy znak z vstupu
		tmp = val >> 8;
		tmp = tmp & 0xff;
		if (tmp!=0) // kdyz narazime na nulu, uz dal jit nemusime, tam uz ty znaky neplatej
		{
			tmp = tmp - 96;
			tmp = tmp << 10;
			hash = hash | tmp; // a jen ty mezivysledny tmp (coz je teda ascii znak prevedeny na rozsah 0-32 pro nase ucely) pridame do vysledku, kterej nam urci index do pole counts

			tmp = val >> 16;
			tmp = tmp & 0xff;
			if (tmp!=0)
			{
				tmp = tmp - 96;
				tmp = tmp << 5;
				hash = hash | tmp;

				tmp = val >> 24;
				if (tmp!=0)
				{
					tmp = tmp - 96;
					hash = hash | tmp;
				}
			}
		}

		// pricist pocet vyskytu
		counts[hash]++;
	}

	// vypsat to z counts na vystup
	int outputindex=0;
	char * tmp = new char[4];

	for (int countsIndex=0; countsIndex<sizeCounts; countsIndex++)
	{
		if (counts[countsIndex]==0) continue; // pokud tohle slovo nebylo ani jednou, jedeme dal a ani ho neresime
		unsigned int hash=countsIndex;
		*(unsigned int*) tmp = 0; // vynulovat cele slovo najednou

		// nasledujici radky by se mozna daly optimalizovat, jeste jinak - jit na to z druhy strany a az se narazi na nulu, tak prestat pripovat chary, podobne jako o cca 20-30 radku vys, ale u velkych poctu slov (rekneme nad 2M-5M slov) by to uz asi nebylo skoro znat.

		// sestavi ten string
		char letter;
		// precte zkraceny znak a udela z nej odpovidajici ascii znak
		letter = hash & 31;
		if (letter!=0)
		{
			letter += 96;
			tmp[3] = letter;
		}
		
		// precte dalsi zkraceny znak a udela z nej odpovidajici ascii znak
		hash>>=5;
		letter = hash & 31;
		if (letter!=0)
		{
			letter += 96;
			tmp[2] = letter;
		}
		
		hash>>=5;
		letter = hash & 31;
		if (letter!=0)
		{
			letter += 96;
			tmp[1] = letter;
		}
		
		hash>>=5;
		letter = hash & 31;
		if (letter!=0) 
		{
			letter += 96;
			tmp[0] = letter;
		}

		// ted podle toho, kolikrat se to slovo opakovalo, ho prida na vystup. Vypocet toho slova probehne jen jednou, pak uz se jen lepi vysledek (lepit jeden int je rychlejsi nez 4xchar)
		for (int c=0; c<counts[countsIndex]; c++)
		{
			pole[outputindex] = *(unsigned int *)tmp;
			outputindex++;
		}
	}

	delete[] tmp;
	delete[] counts;
}

// main
int _tmain(int argc, _TCHAR* argv[])
{
	int size = 100000000;
	
	cout << "Word count" << endl;
	cin >> size;

	// pole slov
	unsigned char * pole2 = new unsigned char[size*4];

	// vygenerovani nahodnych slov
	Generuj2(pole2, size); 
	
	LARGE_INTEGER clock, clock2, freq;
	double time;
	// vypocet doby a samotne razeni
	QueryPerformanceCounter(&clock);
	Serad3((void *)pole2, size);
	QueryPerformanceCounter(&clock2);
	QueryPerformanceFrequency(&freq);

	time=((double)clock2.QuadPart-(double)clock.QuadPart)/(double)freq.QuadPart;
	cout << endl << endl << "Time: " << time*1000.0 << "ms." << endl;

	cout << endl << "Save results to file? (yes/no)";

	string answer="no";
	cin >> answer;

	// ulozeni vysledku do souboru
	if (answer=="yes")
	{
		// save results
		ofstream os;
		os.open("results.txt");
		
		for (int i=0; i<size; i++)
		{
			os << GetString(pole2, i*4) << endl;
		}

		os.close();
	}
	system("pause"); // tohle se mi libi vic nez cekani udelat treba pres cin

	delete[] pole2; // uklizime po sobe
	return 0;
}

Neformátovaný

Přidáno: 27.1.2013
Expirace: Neuvedeno

Aktivity