2018-06-18_2 esercizio "integer sort"

Tema

Descrivere un algoritmo che, dati n numeri interi compresi nell’intervallo da 0 a k, svolga un’analisi preliminare del suo input e poi risponda nel tempo O(1) a qualsiasi domanda su quanti degli n interi ricadono nell’intervallo [a..b] con a e b interi. Il vostro algoritmo dovrebbe impiegare un tempo È(n+k) per l’analisi preliminare.

Svolgimento

Nota: descrivere = scrivere.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N_ELEMENTS 20
#define MAX_ABS_VALUES 50

int *prepare(int values[], int size, int *min, int *max) {
	int i;
	int * aux;
	if(size == 0) {
		return NULL;
	}
	*min = values[0];
	*max = values[0];
	for(i = 0; i < size; i++) {
		if(values[i] < *min) *min = values[i]; if(values[i] > *max) *max = values[i];
	}
	aux = malloc(sizeof(int) * (*max - *min + 1));
	for(i = 0; i < *max - *min + 1; i++) aux[i] = 0;
	for(i = 0; i < size; i++) {
		aux[values[i] - (*min)]++;
	}
	for(i = 1; i < *max - *min; i++) aux[i] = aux[i] + aux[i-1];
	return aux;
}


int main(int argc, char *argv[])
{
	int *values; // contiene i valori non ordinati
	int min; // il valore più piccolo
	int max; // il valore più grande
			 // la dimensione del vettore di appoggio è data da min - max
	int *aux; // l'array ausiliario dell'integer sort
	int i;
	int da, a; // il minimo e massimo tra i valori chiesti all'utente
	int ind_da, ind_a; // gli indici più vicini a da e a
	
	values = malloc(sizeof(int) * N_ELEMENTS);
	srand(time(0));

	for(i = 0; i < N_ELEMENTS; i++)
		values[i] = rand() % (MAX_ABS_VALUES * 2) - MAX_ABS_VALUES;

	aux = prepare(values, N_ELEMENTS, &min, &max);

	for(i = 0; i < max - min; i++) printf("%d: %d\t", i+min, aux[i]);

	printf("\nInserire il primo elemento: ");
	scanf("%d", &da);
	printf("Inserire il secondo elemento: ");
	scanf("%d", &a);

	if(a - da < 0) { a = da+a; da = a-da; a = da-a;} // scambio gli elementi se a < da

	printf("La quantità di numeri compresi tra %d e %d e' %d\n", da, a, (a>=max?aux[max-min-1]:aux[a-min]) - (da<=min?0:aux[da-min-1]));

	system("PAUSE");
	return 0;
}

Commento all'algoritmo

L'algoritmo è una variante dell'integer sort.

Dati n numeri compresi tra n1 e n2 si inizializza un array di k+1 elementi indicizzati da 0 a n2 - n1 e inizializzati a zero.

Esempio (vettore n):

6 -7 5 -1 8 0 4 -2 -8 3

Per ogni elemento in n si incrementano gli indici corrispondenti come nell'integer sort:

Esempio (vettore k):

Ind: -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8
Val: 1 1 0 0 0 0 1 1 1 0 0 1 1 1 1 0 1

poi si cumula la somma da n1 a n2

Ind: -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8
Val: 1 2 2 2 2 2 3 4 5 5 5 6 7 8 9 9 10

Questo richiede ancora un costo Θ(n+k).

In tempo O(1) si estrae l'elemento precedente di A (o zero se A è il primo) e si sottrae all'elemento B.

Esempio:

A = -2

B = 3

k[A-1] = 3

k[B] = 5

k[B] - k[A-1] = 2

Se A o B sono rispettivamente minore del più piccolo e maggiore del più grande è sufficiente considerare il primo e l'ultimo elemento.

Come richiesto, il tempo di preparazione è Θ(n+k) e quello di consultazione è O(1).

Commento al codice

Si è deciso di separare la procedura principale (il tema d'esame) da quella di appoggio, contenente l'inizializzazione e le richieste all'utente.

La chiamata prevede il ritorno di tre valori: l'array k e il minimo e il massimo. Questi due valori sono due int nella procedura di appoggio, che sono passati come puntatori alla procedura principale.

Due costanti indicano la dimensione dell'array e l'intervallo di numeri che è possibile inserire in senso assoluto (es. 5 => da -5 a +5) ottenendo un intervallo di n*2+1 valori.

Nella procedura principale, i valori di minimo e massimo vanno inizializzati con uno dei valori dell'array, poi con un ciclo si valuta se abbassare il minimo o alzare il massimo.

È necessario anche inizializzare i valori dell'array k a zero, perché c'è una probabilità non nulla che alcuni valori non compaiano mai (soprattutto se |n| è piccolo e |k| è grande).

Rispetto all'integer sort è necessario un ulteriore ciclo per calcolare il valore cumulato, che però non ha impatti sul costo asintotico.

Nella procedura principale, a parte il popolamento dell'array con valori casuali, è degno di nota il sistema per gestire la richiesta di valori minori del minimo o maggiori del massimo, che per sintesi è reso con l'utilizzo di operatori ternari.