Está en la página 1de 14

TABLASHASH

1.INTRODUCCIN.
Unaaproximacinalabsquedaradicalmentediferentealasanterioresconsisteenproceder,nopor
comparacionesentrevaloresclave,sinoencontrandoalgunafuncinh(k)quenosddirectamentelalocalizacin
delaclavekenlatabla.
Laprimerapreguntaquepodemoshacernosessiesfcilencontrartalesfuncionesh.Larespuestaes,en
principio,bastantepesimista,puestoquesitomamoscomosituacionidealelquetalfuncindsiempre
localizacionesdistintasaclavesdistintasypensamosp.ej.enunatabladetamao40endondequeremos
direccionar30claves,nosencontramosconquehay4030=1.15*1048posiblesfuncionesdelconjuntode
clavesenlatabla,yslo40*39*11=40!/10!=2.25*1041deellasnogeneranlocalizacionesduplicadas.En
otraspalabras,slo2decada10millonesdetalesfuncionesserian'perfectas'paranuestrospropsitos.Esatarea
esfactiblesloenelcasodequelosvaloresquevayanaperteneceralatablahashseanconocidasapriori.
Existenalgoritmosparaconstruirfuncioneshashperfectasquesonutilizadasparaorganizarlaspalabrasclave
enuncompiladordeformaquelabsquedadecualquieradeesaspalabrasclaveserealiceentiempoconstante.
Lasfuncionesqueevitanvaloresduplicadossonsorprendentementedificilesdeencontrar,inclusoparatablas
pequeas.Porejemplo,lafamosa"paradojadelcumpleaos"aseguraquesienunareuninestnpresentes23
mspresonas,haybastanteprobabilidaddequedosdeellashayannacidoelmismodiadelmismomes.Enotras
palabras,siseleccionamosunafuncinaleatoriaqueaplique23clavesaunatabladetamao365laprobabilidad
dequedosclavesnocaiganenlamismalocalizacinesdeslo0.4927.
Enconsecuencia,lasaplicacionesh(k),alasquedesdeahorallamaremosfuncioneshash,tienenla
particularidaddequepodemosesperarqueh(ki)=h(kj)parabastantesparesdistintos(ki,kj).Elobjetivoser
puesencontrarunafuncinhashqueprovoqueelmenornmeroposibledecolisiones(ocurrenciasde
sinnimos),aunqueestoessolounaspectodelproblema,elotrosereldedisearmtodosderesolucinde
colisionescuandostasseproduzcan.

2.FUNCIONESHASH.
Elprimerproblemaquehemosdeabordareselclculodelafuncinhashquetransformaclavesen
localizacionesdelatabla.Msconcretamente,necesitamosunafuncinquetransformeclaves(normalmente
enterosocadenasdecaracteres)enenterosenunrango[0..M1],dondeMeselnmeroderegistrosque
podemosmanejarconlamemoriadequedispongamos.comofactoresatenerencuentaparalaeleccindela
funcinh(k)estnqueminimicelascolisionesyquesearelativamenterpidayfcildecalcular,aunquela
situacinidealseraencontrarunafuncinhquegeneraravaloresaleatoriosuniformementesobreelintervalo
[0..M1].Lasdosaproximacionesqueveremosestnencaminadashaciaesteobjetivoyambasestnbasadasen
generadoresdenmerosaleatorios.

HasingMultiplicativo.

Estatcnicatrabajamultiplicandolaclavekporsmismaoporunaconstante,usandodespusalgunaporcin
delosbitsdelproductocomounalocalizacindelatablahash.
Cuandolaeleccinesmultiplicarkporsmismayquedarseconalgunodelosbitscentrales,elmtodose
denominaelcuadradomedio.Estemetodoansiendosimpleypudiendocumplirelcriteriodequelosbits
elegidosparamarcarlalocalizacinsonfuncindetodoslosbitsoriginalesdek,tienecomoprincipales
inconvenienteselquelasclavesconmuchoscerossereflejarnenvaloreshashtambinconmuchosceros,yel
queeltamaodelatablaestrestringidoaserunapotenciade2.
Otromtodomultiplicativo,queevitalasrestriccionesanterioresconsisteencalcularh(k)=Int[M*Frac(C*k)]
dondeMeseltamaodelatablay0<=C<=1,siendoimportanteelegirCconcuidadoparaevitarefectos
negativoscomoqueunaclavealfabticaKseasinnimaaotrasclavesobtenidaspermutandoloscaracteresde
k.Knuth(verbibliografa)pruebaqueunvalorrecomendablees:

HasingporDivisin.
Enestecasolafuncinsecalculasimplementecomoh(k)=kmodMusandoel0comoelprimerndicedela
tablahashdetamaoM.
AunquelafrmulaesaplicableatablasdecualquiertamaoesimportanteelegirelvalordeMconcuidado.Por
ejemplosiMfuerapar,todaslasclavespares(resp.impares)seranaplicadasalocalizacionespares(resp.
impares),loqueconstituiraunsesgomuyfuerte.UnareglasimpleparaelegirMestomarlocomounnmero
primo.EncualquiercasoexistenreglasmassofisticadasparalaeleccindeM(verKnuth),basadastodasen
estudiostoricosdefuncionamientodelosmtodoscongruencialesdegeneracindenmerosaleatorios.

3.RESOLUCINDECOLISIONES.
Elsegundoaspectoimportanteaestudiarenelhasingeslaresolucindecolisionesentresinnimos.
Estudiaremostresmtodosbasicosderesolucindecolisiones,unodeellosdependedelaideademantener
listasenlazadasdesinnimos,ylosotrosdosdelclculodeunasecuenciadelocalizacionesenlatablahash
hastaqueseencuentrequeseencuentreunavaca.Elanlisiscomparativodelosmtodosseharenbaseal
estudiodelnmerodelocalizacionesquehandeexaminarsehastadeterminardondesituarcadanuevaclaveen
latabla.
ParatodoslosejemploseltamaodelatablaserM=13ylafuncinhashh1(k)queutilizaremosser:
HASH=ClaveModM
ylosvaloresdelaclavekqueconsideraremossonlosexpuestosenlasiguientetabla:

Suponiendoquek=0noocurredeformanatural,podemosmarcartodaslaslocalizacionesdelatabla,
inicialmentevacas,dndoleselvalor0.Finalmenteypuestoquelasoperacionesdebsquedaeinsercinestn
muyrelacionadas,sepresentaranalgoritmosparabuscaruniteminsertndolosiesnecesario(salvoqueesta
operacinprovoqueundesbordamientodelatabla)devolviendolalocalizacindelitemoun1(NULL)en
casodedesbordamiento.

EncadenamientoseparadooHasingAbierto.
Lamaneramssimplederesolverunacolisinesconstruir,paracadalocalizacindelatabla,unalistaenlazada
deregistroscuyasclavescaiganenesadireccin.Estemtodoseconocenormalmenteconelnombrede
encadenamientoseparadoyobviamentelacantidaddetiemporequeridoparaunabsquedadependerdela
longituddelaslistasydelasposicionesrelativasdelasclavesenellas.Existenvariantesdependiendodel
mantenimientoquehagamosdelaslistasdesinnimos(FIFO,LIFO,porvalorClave,etc),aunqueenlamayora
deloscasos,ydadoquelaslistasindividualesnohandeteneruntamaoexcesivo,sesueleoptarporla
alternativamssimple,laLIFO.
Encualquiercaso,silaslistassemantienenenordenestopuedeversecomounageneralizacindelmtodode
bsquedasecuencialenlistas.Ladiferenciaesqueenlugardemantenerunasolalistaconunsolonodo
cabecerasemantienenMlistasconMnodoscabeceradeformaquesereduceelnmerodecomparacionesdela
bsquedasecuencialenunfactordeM(enmedia)usandoespacioextraparaMpunteros.Paranuestroejemploy
conlaalternativaLIFO,latablaquedaracomosemuestraenlasiguientefigura:

Avecesycuandoelnmerodeentradasalatablaesrelativamentemoderado,noesconvenientedaralas
entradasdelatablahashelpapeldecabecerasdelistas,loquenosconduciraaotromtododeencadenamiento,
conocidocomoencadenamientointerno.Enestecaso,launinentresinnimosestdentrodelapropiatabla
hash,mediantecamposcursores(punteros)quesoninicializadosa1(NULL)yqueirnapuntandohaciasus
respectivossinnimos.

DireccionamientoabiertooHasingCerrado.
Otraposibilidadconsisteenutilizarunvectorenelqueseponeunaclaveencadaunadesuscasillas.Eneste
casonosencontramosconelproblemadequeenelcasodequeseproduzcaunacolisinnosepuedentener
amboselementosformandopartedeunalistaparaesacasilla.Parasolucionareseproblemaseusaloquese
llamarehashing.Elrehashingconsisteenqueunavezproducidaunacolisinalinsertarunelementoseutiliza
unafuncinadicionalparadeterminarcualserlacasillaquelecorrespondedentrodelatabla,aestafuncinla
llamaremosfuncinderehashing,rehi(k).
Alahoradedefinirunafuncinderehashingexistenmltiplesposibilidades,lamssimpleconsisteenutilizar
unafuncinquedependadelnmerodeintentosrealizadosparaencontrarunacasillalibreenlaquerealizarla
insercin,aestetipoderehashingseleconocecomohashinglineal.Deestaformalafuncinderehashing
quedariadelasiguienteforma:
rehi(k)=(h(k)+(i1))modMi=2,3,...

Ennuestroejemplo,despusdeinsertarlas7primerasclavesnosaparecelatablaA,(verlatablasiguiente).
Cuandovamosainsertarlaclave147,estaquedasituadaenlacasilla6,(tablaB)unavezquenosehan
encontradovacaslascasillas4y5.Sepuedeobservarqueantesdelainsercindel147habaagrupacionesde
clavesenlaslocalizaciones4,5y7,8,ydespusdelainsercin,esosdosgrupossehanunidoformandouna
agrupacinprimariamayor,estoconllevaquesisetratadeinsertarunelementoalquelecorrespondealgunas
delascasillasqueestnalprincipiodeesaagrupacinelprocesoderehashingtendrderecorrertodasesas
casillasconloquesedegradarlaeficienciadelainsercin.Parasolucionaresteproblemahabrquebuscarun

mtododerehashingquedistribuyadelaformamsaleatoriaposiblelascasillasvacas.
Despuesdellevaracabolainsercindelasclavesconsideradasennuestroejemplo,elestadodelatablahash
serelquesepuedeobservarenlatabla(C)enlaqueadmasapareceelnmerodeintentosquehansido
necesariosparainsertarcadaunadelasclaves.

Paraintentarevitarelproblemadelasagrupacionesqueacabamosdeverpodramosutilizarlasiguientefuncin
derehashing:
rehi(k)=(h(k)+(i1)*C)modMC>1yprimorelativoconM
peroaunqueestoevitaralaformacindeagrupacionesprimarias,nosolventaraelproblemadelaformacinde
agrupacionessecundarias(agrupacionesseparadasporunadistanciaC).Elproblemabsicoderehashinglineal
esqueparadosclavesdistintasquetenganelmismovalorparalafuncinhashseirnobteniendoexactamente
lamismasecuenciadevaloresalaplicarlafuncinderehashing,cunadolointerenanteseriaquelasecuenciade
valoresobtenidaporelprocesoderehashingfueradistinta.As,habrquebuscarunafuncinderehashingque
cumplalassiguientescondiciones:
Seafcilmentecalculable(conunordendeeficienciaconstante),
queevitelaformacindeagrupaciones,
quegenereunasecuenciadevaloresdistintaparadosclavesdistintasaunquetengaelmismovalorde
funcinhash,yporltimo
quegaranticequetodaslascasillasdelatablasonvisitadas.
sinocumplieraestoltimosepodradarelcasodequeanquedarancasillaslibresperonopodemosinsertarun
determinadoelementoporquelosvalorescorrespondientesaesascasillasnosonobtenidosduranteelrehashing.
Unafuncinderehashingquecumplelascondicionesanterioreseslafuncinderehashingdoble.Estafuncin
sedefinedelasiguienteforma:
hi(k)=(hi1(k)+h0(k))modMi=2,3,...

conh0(k)=1+kmod(M2)yh1(k)=h(k).
Existelaposibilidaddehacerotraseleccionesdelafuncinh0(k)siemprequelafuncinescogidanosea
constante.
EstaformaderehashingdobleesparticularmentebuenacuandoMyM2sonprimosrelativos.Hayqueteneren
cuentaquesiMesprimoentoncesesseguroqueM2esprimorelativosuyo(exceptuandoelcasotrivialdeque
M=3).
Elresultadodeaplicarestemtodoanuestroejemplopuedeverseenlastablassiguientes.Enlaprimerase
incluyenlosvaloresdehparacadaclaveyenlasegundapuedenverselaslocalizacionesfinalesdelasclavesen
latablaascomolaspruebasrequeridasparasuinsercin.

4.BORRADOSYREHASING.
Cuandointentamosborrarunvalorkdeunatablaquehasidogeneradapordireccionamientoabierto,nos
encontramosconunproblema.Sikprecedeacualquierotrovalorkenunasecuenciadepruebas,nopodemos
eliminarlosinms,yaquesilohiciramos,laspruebassiguientesparakseencontrarianel"agujero"dejadopor
kporloquepodramosconcluirqueknoestenlatabla,hechoquepuedeserfalso.Podemoscomprobarloen
nuestroejemploencualquieradelastablas.Lasolucinesquenecesitamosmirarcadalocalizacindelatabla
hashcomoinmersaenunodelostresposiblesestados:vacia,ocupadaoborrada,deformaqueenloque
conciernealabusqueda,unaceldaborradasetrataexectamenteigualqueunaocupada.Encasodeinserciones,
podemosusarlaprimeralocalizacinvaciaoborradaqueseencuentreenlasecuenciadepruebaspararealizar
laoperacin.Observemosqueesteproblemanoafectaalosborradodelaslistasenelencadenamientoseparado.
Paralaimplementacindelaideaanteriorpodriapensarseenlaintroduccinenlosalgortmosdeunvalor
etiquetaparamarcarlascasillasborradas,peroestoserasolounasolucinparcialyaquequedaraelproblema
dequesilosborradossonfrecuentes,lasbsquedassinxitopodranrequerirO(M)pruebasparadetectarqueun
valornoestpresente.
Cuandounatablallegaaundesbordamientoocuandosueficienciabajademasiadodebidoalosborrados,el
nicorecursoesllevarlaaotratabladeuntamaomsapropiado,nonecesariamentemayor,puestoquecomo
laslocalizacionesborradasnotienenquereasignarse,lanuevatablapodrasermayor,menoroinclusodel
mismotamaoquelaoriginal.Esteprocesosesueledenominarrehashingyesmuysimpledeimplementarsiel
arcadelanuevatablaesdistintaaldelaprimitiva,peropuedecomplicarsebastantesideseamoshacerun
rehashingenlapropiatabla.

5.EVALUACINDELOSMTODOSDERESOLUCIN.
Elaspectomssignificativodelabsquedaporhashingesquesieficienciadependedeldenominadofactorde
almacenamiento=n/MconnelnmerodeitemsyMeltamaodelatabla.
Discuteremoselnmeromediodepruebasparacadaunodelosmtodosquehemosvistoderesolucinde
colisiones,entrminosdeBE(bsquedaconxito)yBF(bsquedasinxito).Lasdemostracionesdelas
frmulasresultantespuedenencontrarseenKnuth(verbibliografa).

Encadenamientoseparado.
Aunquepuederesultarengaosocompararestemtodoconlosotrosdos,puestoqueenestecasopuedeocurrir
que>1,lasfrmulasparoximadasson:

Estasexpresionesseaplicaninclusocuando>>1,porloqueparan>>M,lalongitudmediadecadalistaser
,ydeberiaesperarseenmediarastrearlamitaddelalista,antesdeencontrarundeterminadoelemento.

HasingLineal.
Lasfrmulasaproximadasson:

Comopuedeverse,estemtodo,aunsiendosatisfactorioparapequeos,esmuypobrecuando>1,yaque
ellmitedelosvaloresmediosdeBEyBFsonrespectivamente:

Encualquiercaso,eltamaodelatablaenelhashlinealesmayorqueenelencadenamientoseparado,perola
cantidaddememoriatotalutilizadaesmenoralnousarsepunteros.

HasingDoble.
Lasfrmulassonahora:
BE=(1/1)*ln(1)

BF=1/(1)
convaloresmedioscuando>1deMyM/2,respectivamente.
Parafacilitarlacomprensindelasfrmulaspodemosconstruirunatablaenlaquelasevaluemosparadistintos
valoresde:

Laeleccindelmejormtodohashparaunaaplicacinparticularpuedenoserfcil.Losdistintosmtodosdan
unascaractersticasdeeficienciasimilares.Generalmente,lomejoresusarelencadenamientoseparadopara
reducirlostiemposdebsquedacuandoelnmeroderegistrosaprocesarnoseconocedeantemanoyelhash
dobleparabuscarclavescuyonmeropueda,dealgunamanera,predecirsedeantemano.
Encomparacinconotrastcnicasdebsqueda,elhashingtieneventajasydesventajas.Engeneral,paravalores
grandesden(yrazonablesvaloresde)unbuenesquemadehashingrequierenormalmentemenospruebas(del
orden1.52)quecualquierotromtododebsqueda,incluyendolabsquedaenrbolesbinarios.Porotra
parte,enelcasopeor,puedecomportarsemuymalalrequerirO(n)pruebas.Tambinpuedeconsiderarsecomo
unaventajaelhechodequedebemosteneralgunaestimacinaprioridenmeromximodeitemsquevamosa
colocarenlatablaaunquesinodisponemosdetalestimacinsiemprenosquedaralaopcindeusarelmetodo
deencadenamientoseparadoendondeeldesbordamientodelatablanoconstituyeningnproblema.

Otroproblemarelativoesqueenunatablahashnotenemosningunadelasventajasquetenemoscuando
manejamosrelacionesordenadas,yasp.e.nopodemosprocesarlositemsenlatablasecuencialmente,ni
concluirtrasunabsquedasinxitonadasobrelositemsquetienenunvalorcercanoalquebuscamos,peroen
cualquiercasoelmayorproblemaquetenerelhashingcerradoeseldelosborradosdentrodelatabla.

6.IMPLEMENTACINDELASTABLASHASH.

ImplementacindeHasingAbierto.
Enesteapartadovamosarealizarunaimplementacinsimpledelhasingabiertoquenosservircomoejemplo
ilustrativodesufuncionamiento.Paraellosupondremosuntipodedatochar*paraelcualdisearemosuna
funcinhashsimpleconsistenteenlasumadeloscodigosASCIIquecomponendichacadena.
Unaposibleimplementacinutilizandoeltipodedatoabstractolistaseralasiguiente:
#defineNCASILLAS100/*Ejemplodenmerodeentradasenlatabla.*/
typedeftLista*TablaHash;

Paralacualpodemosdisearlassiguientesfuncionesdecreacinydestrucin:

TablaHashCrearTablaHash()

{
tLista*t;
registeri;
t=(tLista*)malloc(NCASILLAS*sizeof(tLista));
if(t==NULL)
error("Memoriainsuficiente.");
for(i=0;i<NCASILLAS;i++)
t[i]=crear();
returnt;
}

voidDestruirTablaHash(TablaHasht)

{
registeri;

for(i=0;i<NCASILLAS;i++)
destruir(t[i]);
free(t);
}

Comofuemencionadoanteriormentelafuncinhashqueserusadaes:
intHash(char*cad)

{
intvalor;
unsignedchar*c;
for(c=cad,valor=O;*c;c++)
valor+=(int)(*c);
return(valor%NCASILLAS);
}

YfuncionesdeltipoMiembroHash,InsertarHash,BorrarHashpuedenserprogramadas:
intMiembroHash(char*cad,TablaHasht)

{
tPosicionp;
intenc;

intpos=Hash(cad);

p=primero(t[pos]);
enc=O;
while(p!=fin(t[pos])&&!enc){
if(strcmp(cad,elemento(p,t[pos]))==O)
enc=1;
else
p=siguiente(p,t[pos]);
}
returnenc;
}
voidInsertarHash(char*cad,TablaHasht)

{
intpos;
if(MiembroHash(cad,t))
return;

pos=Hash(cad);
insertar(cad,primero(t[pos]),t[pos]);
}
voidBorrarHash(char*cad,TablaHasht)

{
tPosicionp;
intpos=Hash(cad);
p=primero(t[pos]);
while(p!=fin(t[pos])&&!strcmp(cad,elemento(p,t[pos])))
p=siguiente(p,t[pos]));
if(p!=fin(t[pos]))
borrar(p,t[pos]);
}

Comosepuedeobservarestaimplementacinesbastantesimpledeformaquepuedesufrirbastantesmejoras.
Seproponecomoejercicioelrealizarestalabordotandoaltipodedatodeposibilidadescomo:
Determinacindeltamaodelatablaenelmomentodecreacin.
Modificacindelafuncinhashutilizada,medianteelusodeunpunteroafuncin.
Construccindeunafuncinquepasaunatablahashdeuntamaodeterminadoaotratablaconun
tamaosuperioroinferior.
Construccindeuniteradoratravsdetodosloselementosdelatabla.
etc...

ImplementacindeHasingCerrado.
Enesteapartadovamosarealizarunaimplementacinsimpledelhashingcerrado.Paraellosupondremosun
tipodedatochar*aligualqueenelapartadoanterior,paraelcualdisearemoslamismafuncinhash.
Unaposibleimplementacindelaestructuraaconseguireslasiguiente:
#defineNCASILLAS100
#defineVACIONULL
staticchar*BORRADO='''';
typedefchar**TablaHash;

Paralacualpodemosdisearlassiguientesfuncionesdecreacinydestrucin:
TablaHashCrearTablaHash()

{
TablaHasht;
registeri;
t=(TablaHash)malloc(NCASILLAS*sizeof(char*));
if(t==NULL)
error("MemoriaInsuficiente.");
for(i=0;i<NCASILLAS;i++)
t[i]=VACIO;
returnt;
}
voidDestruirTablaHash(TablaHasht)

{
registeri;
for(i=O;i<NCASILLAS;i++)
if(t[i]!=VACIO&&t[i]!=BORRADO)
free(t[i]);
freet;
}

LafuncinhashqueserusadaesigualalaqueyahemosusadoparalaimplementacindelHasingAbierto.Y
funcionesdeltipoMiembroHash,InsertarHash,BorrarHashpuedenserprogramadastalcomosigue,teniendo
encuentaqueenestaimplementacinharemosusodeunrehashinglineal.
intHash(char*cad)

{
intvalor;
unsignedchar*c;

for(c=cad,valor=0;*c;c++)

valor+=(int)*c;
return(valor%NCASILLAS);
}
intLocalizar(char*x,TablaHasht)

/*Devuelveelsitiodondeestaxodondedeberiadeestar.*/
/*Notieneencuentalosborrados.*/
{
intini,i,aux;
ini=Hash(x);
for(i=O;i<NCASILLAS;i++){

aux=(ini+i)%NCASILLAS;
if(t[aux]==VACIO)
returnaux;
if(!strcmp(t[aux],x))
returnaux;
}
returnini;
}
intLocalizar1(char*x,TablaHasht)

/*Devuelveelsitiodondepodriamosponerx*/
{
intini,i,aux;
ini=Hash(x);
for(i=O;i<NCASILLAS;i++){
aux=(ini+i)%NCASILLAS;
if(t[aux]==VACIO||t[aux]==BORRADO)
returnaux;
if(!strcmp(t[aux],x))
returnaux;
}
returnini;
}
intMiembroHash(char*cad,TablaHasht)

{
intpos=Localizar(cad,t);
if(t[pos]==VACIO)
return0;
else
return(!strcomp(t[pos],cad));
}
voidInsertarHash(char*cad,TablaHasht)

{
intpos;
if(!cad)
error("Cadenainexistente.");
if(!MiembroHash(cad,t)){
pos=Localizar1(cad,t);
if(t[pos]==VACIO||t[pos]==BORRADO){
t[pos]=(char*)malloc((strlen(cad)+1)*sizeof(char));
strcpy(t[pos],cad);
}else{
error("TablaLlena.\n");
}
}
}

voidBorrarHash(char*cad,TablaHasht)

{
intpos=Localizar(cad,t);
if(t[pos]!=VACIO&&t[pos]!=BORRADO){
if(!strcmp(t[pos],cad)){
free(t[pos]);
t[pos]=BORRADO;
}
}
}

Obviamente,estaimplementacinaligualqueladelhasingabiertoestambinmejorabledeformaquese
proponeelejerciciodediseareimplementarunaversinmejoradaconposibilidadessimilaresalas
enumeradasenelapartadoanterior.

TutordeEstructurasdeDatosInteractivo
ExpositoLopezDaniel,AbrahamGarcaSoto,MartinGomezAntonioJose
Directordeproyecto:JoaqunFernndezValdivia
5LicenciaturaInformatica
ETSII99/00(UniversidaddeGranada).

También podría gustarte