| 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 | } |