Linha | Codigo |
001 | // Prof. Leo^nidas - http://www.matematica.br http://line.ime.usp.br |
002 | // MAC122 |
003 | // 2017/09/07 |
004 | |
005 | // Compara tempo efetivo de execucao de 2 metods de ordenacao, SEM trocar efetivamente as "strings" de posicao, |
006 | // usando vetor auxiliar para ordem (main.vetOrdem[]). Os metodos sao "Bolha" e "Radix". |
007 | // Supoe que os nomes usam apenas caracteres de 'a' ate' 'z' (pequena faixa de valores para "universo") |
008 | |
009 | // Le arquivo com N+1 linhas, estando na primeir "N" e nas seguintes um nome cada |
010 | |
011 | // Complexidade Bolha: O(n*n * u), se u = comprimento medio das palavras |
012 | // Complexidade Radix: O(n + u), se u = #universo |
013 | |
014 | // Exercicio 1: Altere o codigo para ler as linhas e deduzir numero nomes |
015 | // Exercicio 2: Prove que o algoritmo de ordenacao Radiz e' de fato estavel |
016 | |
017 | // Para depurar gerenciamento de memoria em caso de erro de 'malloc/free': |
018 | // $ gcc -fsanitize=address -g -o ordena_string_por_frequencia ordena_string_por_frequencia.c |
019 | // Para compilar com caracteres especiais "L'\u00e1" |
020 | // $ gcc -fsanitize=address -g -std=c99 -o ordena_string_compara_metodos ordena_string_compara_metodos.c |
021 | |
022 | // Arquivos para teste: |
023 | // https://pt.wikipedia.org/wiki/Categoria:Matemáticos_do_Brasil |
024 | |
025 | // Referencias: |
026 | // 1. "Chapter Ten. Radix Sorting" do Sedgewick (pg 154 "Program 10.2. MSD radix sort") |
027 | // 2. Paulo Feofilof: https://www.ime.usp.br/~pf/algoritmos/ |
028 | |
029 | #include <stdio.h> |
030 | #include <stdlib.h> // para "rand()" |
031 | #include <string.h> // para "strcmp(...)" e "strcpy(...)" |
032 | #include <time.h> // para "clock_t", "clock()" |
033 | |
034 | #define MAX 5000 // maximo de elementos |
035 | #define TAM 10 // testar com TAM elementos |
036 | #define MAXCOMPLINHA 1024 // tamanho maximo de uma linha |
037 | #define INTERVALO 27 // 'A' ate 'Z' (97 a 122) |
038 | |
039 | |
040 | // Comprimento de "strings" |
041 | int comprimento (char *vetStr) { // evita "strlen(vetStr[i])" devolvendo 1 a mais... |
042 | int i = 0; |
043 | while (vetStr[i]!='\0' && vetStr[i]!='\n') i++; |
044 | return i; |
045 | } |
046 | |
047 | // Tentativa de trocar caracteres especiais para "base" em ASCII |
048 | // Ver: https://www.w3.org/People/danield/unic/unitra.htm ; https://www.w3.org/International/questions/qa-what-is-encoding |
049 | void sanitarizaASCII (int ind, char *nome) { |
050 | int i = 0; |
051 | char c1; //unsigned |
052 | wchar_t c; // para conseguir armazenar caractere "estendido" (2 bytes) |
053 | while ((c=nome[i])!='\0' && c!='\n') { |
054 | if (c==' ') { |
055 | } |
056 | else |
057 | if (c>='A' && c<='Z') { // 'A'=65, 'B'=66, ... 'W'=87, 'x'=88, 'y'=89, 'z'=90 |
058 | nome[i] = c + ('a' - 'A'); |
059 | } |
060 | else |
061 | if (c<'a' || c>'z') { // 'a'=97, 'b'=98, ... 'w'=119, 'x'=120, 'y'=121, 'z'=122 |
062 | switch (c) { // tente trocar alguns caracteres usando notacao Unicode |
063 | case L'\u00e1' : // a' |
064 | case L'\u00e0' : // `a |
065 | case L'\u00e2' : // a^ |
066 | case L'\u00e3' : // a~ |
067 | case L'\u00e4' : // a" |
068 | c1 = 'a'; printf(" *** i=%d, %s", i, nome); break; |
069 | // ... |
070 | case L'\u00e7' : // c, |
071 | c1 = 'c'; printf(" *** i=%d, %s", i, nome); break; |
072 | default: c1 = ' '; |
073 | } |
074 | nome[i] = c1; |
075 | } //printf("2: nome=%s", nome); |
076 | i++; |
077 | } |
078 | } |
079 | |
080 | // Ler uma linha do arquivo apontado |
081 | char * leituraElemento (FILE *aptf, int i, char linha[]) { |
082 | if (fgets(linha, MAXCOMPLINHA, aptf)==NULL || ferror(aptf) || feof(aptf) ) { // char *fgets(char *str, int n, FILE *stream) |
083 | if (comprimento(linha)>0) { // tratar caso de ultima linha ter nome |
084 | sanitarizaASCII(i, linha); // tenta "limpar" linha eliminando caracteres especiais (i apenas apoio 'a depuracao) |
085 | return linha; |
086 | } |
087 | return NULL; // final de arquivo |
088 | } |
089 | sanitarizaASCII(i, linha); |
090 | return linha; |
091 | } |
092 | |
093 | // Leia vetor de "string", devolva o comprimento da maior (uniformiza todas) |
094 | int leituraVetor (FILE *apf, char vetStr[][MAXCOMPLINHA], int vetOrdem[], int *N) { |
095 | int *vetComprimentos; |
096 | int i = 0, j, tamI, max = -1; //, imax; |
097 | fscanf(apf, "%d", N); |
098 | if (*N > MAX) { |
099 | // printf("Erro! Primeira linha do arquivo de 'string' afirma existirem %d elementos, mas limite e' %d!\n", *N, MAX); |
100 | return -*N; |
101 | } |
102 | vetComprimentos = malloc(*N * sizeof(int)); // comprimento das palavras (evita computar 2 vezes) |
103 | |
104 | leituraElemento(apf, 0, vetStr[0]); // pula restante da linha |
105 | |
106 | for (i = 0; i < *N; i++) { |
107 | if (leituraElemento(apf, i, vetStr[i]) == NULL) { // EOF |
108 | i--; |
109 | break; |
110 | } |
111 | tamI = comprimento(vetStr[i]); // para nao usar "strlen(vetStr[i]" que pode devolver 1 a mais... |
112 | vetComprimentos[i] = tamI; |
113 | //D printf("#=%d : vetStr[%d]=%s", tamI, i, vetStr[i]); |
114 | if (tamI>max) { max = tamI; } // imax = i; } |
115 | } |
116 | |
117 | printf("leituraVetor: maior linha tem max=%d caracteres\n", max); |
118 | for (i = 0; i < *N; i++) { // para uniformizar |
119 | vetOrdem[i] = i; // ordem inicial e' identidade |
120 | for (j = vetComprimentos[i]; j < max; j++) { // uniformiza "strings" |
121 | vetStr[i][j] = ' '; |
122 | } |
123 | vetStr[i][j] = '\0'; |
124 | } |
125 | free(vetComprimentos); // libera memoria alocada |
126 | return max; |
127 | } // int leituraVetor(FILE *apf, char vetStr[][MAXCOMPLINHA], int vetOrdem[], int *N) |
128 | |
129 | void iniciaVetorOrdem (int ord[], int N) { |
130 | int i; |
131 | for (i = 0; i < N; i++) |
132 | ord[i] = i; |
133 | } |
134 | |
135 | // Auxilia depuracao |
136 | void imprimeVetorOrdem (int ord[], int N) { |
137 | int i; |
138 | for (i = 0; i < N; i++) { |
139 | printf("%3d", ord[i]); |
140 | } |
141 | printf("\n"); |
142 | } |
143 | |
144 | // Imprime vetor de nomes usando vetor de ordem "ord[]" |
145 | void imprimeVetor (FILE *ap, char vetStr[][MAXCOMPLINHA], int ord[], int N) { |
146 | int i; |
147 | for (i = 0; i < N; i++) { |
148 | fprintf(ap, " %3d : |%s|\n", i, vetStr[ord[i]]); |
149 | } |
150 | } |
151 | |
152 | void imprimeVetorDireto (char vetStr[][MAXCOMPLINHA], int N) { |
153 | int i; |
154 | for (i = 0; i < N; i++) { |
155 | printf(" %3d : |%s|\n", i, vetStr[i]); |
156 | } |
157 | } |
158 | |
159 | //-------------------------------------------------------------------------- |
160 | // Ordena via Bolha por vetor auxiliar (sem trocar elementos) |
161 | void trocaEmVetorOrd (int vetOrdem[], int i, int j) { |
162 | int aux; |
163 | aux = vetOrdem[i]; |
164 | vetOrdem[i] = vetOrdem[j]; |
165 | vetOrdem[j] = aux; |
166 | } |
167 | |
168 | void ordenaBolhaOrdem (char vetStr[][MAXCOMPLINHA], int ord[], int N) { |
169 | int i, j; |
170 | for (i = 1; i <= N; i++) { |
171 | for (j = i; j > 0; j--) { |
172 | if (strcmp(vetStr[ord[j-1]], vetStr[ord[j]]) > 0) |
173 | trocaEmVetorOrd(ord, j, j-1); |
174 | } |
175 | } |
176 | } |
177 | |
178 | //-------------------------------------------------------------------------- |
179 | // Ordena via Bolha trocando elementos |
180 | void trocaEmVetor (char vetStr[][MAXCOMPLINHA], int i, int j) { |
181 | char aux[MAXCOMPLINHA]; // como trata-se de "string", cada copia e' um laco |
182 | strcpy(aux, vetStr[i]); // aux = vetStr[i]; |
183 | strcpy(vetStr[i], vetStr[j]); // vetStr[i] = vetStr[j]; |
184 | strcpy(vetStr[j], aux); // vetStr[j] = aux; |
185 | } |
186 | |
187 | // Ordena via bolha trocando elementos |
188 | void ordenaBolha (char vetStr[][MAXCOMPLINHA], int N) { |
189 | int i, j; |
190 | for (i = 1; i <= N; i++) { |
191 | for (j = i; j > 0; j--) { |
192 | if (strcmp(vetStr[j-1], vetStr[j]) > 0) |
193 | trocaEmVetor(vetStr, j, j-1); |
194 | } |
195 | } |
196 | } |
197 | |
198 | //-------------------------------------------------------------------------- |
199 | // Ideia de "vetor de bits" associando caracteres ' ', 'a', 'b', 'c', ... 'as posicoes 0, 1, 2, ... do vetor |
200 | // Entretanto para funcionar o computo de frequencias acumuladas |
201 | // Devolve como indice a posicao seguinte (para liberar o 0 e permitir pegar o anterior dele) |
202 | int pegaIndice (char c, int U, int desc) { |
203 | int ind; // indice do caractere: ' ' = 32 -> 1; 'a' -> 2; 'b' -> 3; ... |
204 | if (c==' ') ind = 1; // encontra posicao (ind) do elemento no vetor auxiliar |
205 | else ind = c - 'a' + 2; // 'a' -> 2; 'b' -> 3; ... |
206 | if (ind<0 || ind>U) { // detectar valores do "universo" erroneos... |
207 | printf("Erro: ultrapassou universo previso!\nCaractere '%c' = %d -> %d > %d\n", c, c, ind, U); |
208 | return -1; |
209 | } |
210 | return ind; |
211 | } |
212 | |
213 | // Ordena crescente v[0..n-1] supondo que os elementos do vetor pertencem ao universo 0..U-1 |
214 | // Utiliza vetor de ordem 'vetOrdem[]' para NAO mover "strings" |
215 | // Supoe que vetor de "strings" 'vetStr[][]' esteja uniformizado |
216 | // (todas de mesmo comprimento, completadas com brancos 'a direita) |
217 | // IMPORTANTE: o metodo de ordenacao por "digito" menos significative e' estavel |
218 | void ordenaPorFrequencia (char vetStr[][MAXCOMPLINHA], int vetOrdem[], int tamLnh, int n, int U) { |
219 | int ind, jj, i; |
220 | int *vetFreqAcum, *vetAux, *vetOrd1; |
221 | char *vet; |
222 | vetFreqAcum = malloc((U+1) * sizeof(int)); // para freq de cada diferente elemento |
223 | vetAux = malloc(n * sizeof(char)); // auxiliar para vetor de dados |
224 | vet = malloc(n * sizeof(char)); // auxiliar para armazenar caractere da posicao jj das "strings" |
225 | vetOrd1 = malloc(n * sizeof(int)); // auxiliar para atualizar vetor de ordem |
226 | |
227 | // Ideia: |
228 | // 1. Computar frequencia de cada elemento considerando o "universo" (ideia de "vetor de bits"): O(n) |
229 | // 2. Acumular as frequencias (de 0 ate U-1) somando frequencia acumulada anterior (FAA) |
230 | // 3. A posicao da primeira ocorrencia de cada elemento eh dada pela FAA |
231 | // A proxima ocorrencia do elemento eh na posicao seguinte |
232 | |
233 | // Ordenar por "digito" menos significativo, como metodo e' estavel ao final ordena tudo |
234 | for (jj = tamLnh-1; jj >= 0; jj--) { |
235 | |
236 | // 1. Computar frequencia de cada elemento considerando o "universo" (ideia de "vetor de bits"): O(n) |
237 | for (i = 0; i <= U; i++) |
238 | vetFreqAcum[i] = 0; |
239 | for (i = 0; i < n; ++i) { |
240 | vet[i] = vetStr[vetOrdem[i]][jj]; // pegar caractere respeitando a ordem |
241 | if (vet[i]=='\0' || vet[i]==0) { // detecta erro: caractere invalido |
242 | printf("Erro em ordenacao: caractere invalido na posicao %d de %s\n", jj, vetStr[i]); return; |
243 | } |
244 | ind = pegaIndice(vet[i], U, 0); |
245 | if (ind<0) { // detecta erro: caractere fora do "universo" previso |
246 | printf("Erro em ordenacao (1): indice invalido! (jj=%d, i=%d, c=%c)\n%s\n", jj, i, vet[i], vetStr[jj]); return; |
247 | } |
248 | vetFreqAcum[ind]++; // mais uma ocorrencia desse elemento |
249 | } |
250 | |
251 | // 2. Acumular as frequencias (de 0 ate U-1) somando frequencia acumulada anterior FAA |
252 | // Acumular frequencias: vetFreqAcum[i] recebera a frequencia de i-1 => recebe soma dos anteriores |
253 | for (i = 1; i <= U; i++) { |
254 | vetFreqAcum[i] += vetFreqAcum[i-1]; |
255 | } |
256 | |
257 | // 3. A posicao da primeira ocorrencia de cada elemento eh dada pela FAA |
258 | // A proxima ocorrencia do elemento eh na posicao seguinte |
259 | // Como vetFreqAcum[i] eh a freq de todos os predecessores de i |
260 | // entao o elemento de i deve ser posicionado no indice vetFreqAcum[i] e proximas ocorrencias nas seguintes |
261 | for (i = 0; i < n; i++) { |
262 | ind = pegaIndice(vet[i], U, 1) - 1; // pegar indice do elemento no vetor "universo" (1 a menos pois o primeiro deve ir para 0 e sua freq e' 1) |
263 | if (ind<0) { printf("Erro em ordenacao (2): indice invalido! (jj=%d, i=%d, c=%c)\n|%s|\n", jj, i, vet[i], vetStr[i]); return; } |
264 | |
265 | // vetOrd1[0..n-1] fornece "strings" em ordem crescente (menor na posicao 'vetOrdem[0]' |
266 | vetOrd1[vetFreqAcum[ind]] = vetOrdem[i]; // elemento i tem posicao de ordem final 'vetFreqAcum[ind]' |
267 | |
268 | vetFreqAcum[ind]++; // a proxima ocorrencia do elemento i deve ir para proxima posicao |
269 | } |
270 | |
271 | // vetOrdem[0..n-1] fornece "strings" em ordem crescente (menor na posicao 'vetOrdem[0]' |
272 | for (i = 0; i < n; i++) // nova ordem |
273 | vetOrdem[i] = vetOrd1[i]; |
274 | |
275 | } // for (jj = tamLnh-1; jj >= 0; jj--) |
276 | |
277 | free(vetFreqAcum); // libera memorias alocadas - importante se este metodo ficar ativo |
278 | free(vetAux); |
279 | free(vet); |
280 | |
281 | } // void ordenaPorFrequencia(char vetStr[][MAXCOMPLINHA], int vetOrdem[], int tamLnh, int n, int U) |
282 | |
283 | // inicia aqui... |
284 | int main (int argc, char *argv[]) { |
285 | FILE *apontEntrada, *apontSaida; |
286 | char vetorDados[MAX][MAXCOMPLINHA]; |
287 | int vetOrdem[MAX]; |
288 | clock_t tempo_inicialF, tempo_finalF; // via 'include "time.h"' para pegar tempo |
289 | clock_t tempo_inicialB1, tempo_finalB1; |
290 | clock_t tempo_inicialB2, tempo_finalB2; |
291 | |
292 | int tam, max; |
293 | |
294 | if (argc < 2) { //2 (argc < 3) |
295 | //2 printf("Erro: eram esperados 2 nomes de arquivos, primeiro com nomes para ordenacao, segundo para saidas\n"); |
296 | //2 printf("./ordena_string_por_frequencia entradas saidas\n"); |
297 | printf("Erro: era esperado o nomes do arquivo com nomes a serem ordenados (primeira linha com N, seguido de N nomes)\n"); |
298 | printf("./ordena_string_compara_metodos entradas\n"); |
299 | return 1; |
300 | } |
301 | |
302 | printf("Abre %s\n", argv[1]); |
303 | apontEntrada = fopen(argv[1], "r"); |
304 | |
305 | max = leituraVetor(apontEntrada, vetorDados, vetOrdem, &tam); // pegue tamanho uniformizado das "strings" |
306 | fclose(apontEntrada); |
307 | if (max < 0) { |
308 | printf("Erro! Primeira linha do arquivo de 'string' afirma existirem %d elementos, mas limite e' %d!\n", -max, MAX); |
309 | return 1; |
310 | } |
311 | |
312 | printf("\n\n\nDados iniciais:\n"); |
313 | imprimeVetor(stdout, vetorDados, vetOrdem, tam); |
314 | |
315 | // 1. Ordena via frequencia (Radix) SEM trocas nas posicoes das "strings" |
316 | tempo_inicialF = clock(); |
317 | ordenaPorFrequencia(vetorDados, vetOrdem, max, tam, INTERVALO); |
318 | tempo_finalF = clock(); |
319 | |
320 | printf("\nOrdenado por Frequencia sem trocas:\n"); |
321 | imprimeVetor(stdout, vetorDados, vetOrdem, tam); |
322 | |
323 | //2 printf("Abre arquivo para registrar nomes ordenados: %s\n", argv[2]); |
324 | //2 apontSaida = fopen(argv[2], "w"); // |
325 | //2 imprimeVetor(apontSaida, vetorDados, vetOrdem, tam); |
326 | //2 fclose(apontSaida); |
327 | |
328 | // 2. Ordena via Bolha SEM trocas nas posicoes das "strings" |
329 | iniciaVetorOrdem(vetOrdem, tam); |
330 | tempo_inicialB1 = clock(); |
331 | ordenaBolhaOrdem(vetorDados, vetOrdem, tam-1); |
332 | tempo_finalB1 = clock(); |
333 | printf("\nOrdenado por Bolha sem trocas:\n"); |
334 | imprimeVetor(stdout, vetorDados, vetOrdem, tam); |
335 | |
336 | // 3. Ordena via Bolha COM trocas nas posicoes das "strings" |
337 | tempo_inicialB2 = clock(); |
338 | ordenaBolha(vetorDados, tam-1); |
339 | tempo_finalB2 = clock(); |
340 | printf("\nOrdenado por Bolha com trocas:\n"); |
341 | imprimeVetorDireto(vetorDados, tam); |
342 | |
343 | printf(" tam = %5d\nTempos de execucao\n * Frequencia = %fs\n * Bolha ordem= %fs\n * Bolha = %fs\n", tam, |
344 | (double)(tempo_finalF - tempo_inicialF) / CLOCKS_PER_SEC, |
345 | (double)(tempo_finalB1 - tempo_inicialB1) / CLOCKS_PER_SEC, |
346 | (double)(tempo_finalB2 - tempo_inicialB2) / CLOCKS_PER_SEC); |
347 | |
348 | return 0; |
349 | } |