Teams


Asignación de equipos de trabajo a calendarios semanales.
La historia de este código es extraña. Hay una versión en C correspondiente a un paper colectivo que se mandó a un congreso en Bosnia Herzegovina para Sept. 2002 (ref. TMT02-089). El código fuente no se pudo aceptar por exceder el número máximo de páginas, por lo que sólo se describe el procedimiento. Pues he aquí la versión perl para asignar turnos de trabajo a equipos de tamaño arbitrariamente variable, según una tabla de requisitos de presencia.



#!/usr/bin/perl package t; use strict; my $TIME = time(); my @q = (0, 10, 20, 10, 15, 20, 25, 30, 20, 10, 25, 10, 12, 13, 14, 15, 20, 15, 10, 20, 16); my $maxwork = 5; my $maxacum = $maxwork; my ($numsol, $tryed, $ZZ, $min, $max, $numteam, $numperson); my (@s, @t, @y); my ($i,$j,$m,$n,$ok); for (@q){ $q[0] += $_; } $min = 999999999; $max =0; for (1..$#q){ if ($q[$_]<$min && $q[$_]>0){ $min=$q[$_]; } if ($q[$_]>$max) { $max=$q[$_]; } } my $MINRESOLVE = 1e+16; my $scut0 = int($#q/$maxwork); # my $scut1 = $#q-$scut0+.5; my $scut1 = int(.5+$#q/2); my $scut2 = $max-2.5; while (1){ INI: $ZZ=0; $numteam = $scut0+int(rand($scut1)); @t=(); @y=(); $y[0]=$y[1]=0; $t[1]=$min; $numperson=$t[1]; for (2..$numteam){ $t[$_] = 3+int(rand($scut2)); # mininum team, 3 workers $numperson += $t[$_]; $y[$_]=0; } redo if ($numperson*$maxwork<$q[0]); $tryed++; for $i (0..$#q){ # primitiveness for (0..$maxacum){ $s[$i][$_]=0; } } for $i (1..$#q){ # LLENADO next if ($s[$i][0]>=$q[$i]); for $j (1..$numteam){ next if (check($i,$j)!=0); for (1..$maxacum){ next if ($s[$i][$_]>0); $s[$i][$_]=$j; $y[0]++; $y[$j]++; $s[$i][0] += $t[$j]; $s[0][0] += $t[$j]; last; } last if ($s[$i][0]>=$q[$i]); } } for ($i=$#q;$i>=1;$i--){ # LLENADO B next if ($s[$i][0]>=$q[$i]); for $j (1..$numteam){ next if (check($i,$j)!=0); for (1..$maxacum){ next if ($s[$i][$_]>0); $s[$i][$_]=$j; $y[0]++; $y[$j]++; $s[$i][0] += $t[$j]; $s[0][0] += $t[$j]; last; } last if ($s[$i][0]>=$q[$i]); } } for $i (1..$#q){ # filtrar innecesarios next if ($q[$i]>=$s[$i][0]); for $j (1..$numteam){ next if ($y[$j] <= 0); next if ($t[$j]>$s[$i][0]-$q[$i]); for (1..$maxacum){ if ($s[$i][$_]==$j){ $s[$i][$_]=0; $y[0]--; $y[$j]--; $s[$i][0] -= $t[$j]; $s[0][0] -= $t[$j]; last; } } } } for ($i=$#q;$i>=1;$i--){ # rellenar posibles faltas # next if ($q[$i]<=$s[$i][0]); for $j (1..$numteam){ next if (check($i,$j)!=0); for (1..$maxacum){ $ok = $s[$i][$_]; if ($ok<1 && $q[$i]>$s[$i][0]){ $s[$i][$_]=$j; $y[0]++; $y[$j]++; $s[$i][0] += $t[$j]; $s[0][0] += $t[$j]; last; }elsif (($t[$j]>$t[$ok] && $q[$i]>$s[$i][0]) || ($t[$j]<$t[$ok] && $t[$ok]-$t[$j]<=$s[$i][0]-$q[$i])){ $y[0]--; # swap equipos $y[$ok]--; $s[$i][0] -= $t[$ok]; $s[0][0] -= $t[$ok]; $s[$i][$_]=$j; $y[0]++; $y[$j]++; $s[$i][0] += $t[$j]; $s[0][0] += $t[$j]; last; } } } if ($s[$i][0]<$q[$i]) { goto INI; } } $numperson=0; for ($j=$numteam;$j>=1;$j--){ # ahorrar personas $ok=0; $n=999; for $i (1..$#q){ next if ($q[$i]==$s[$i][0]); for (1..$maxacum){ if ($s[$i][$_]==$j) { $ok++; $m=$s[$i][0]-$q[$i]; if ($m>$t[$j]){ $m=$t[$j]; } if ($m<$n){ $n=$m; } } } } if ($ok==$y[$j] && $n<999){ # && $t[$j]>2){ $t[$j]-=$n; for $i (1..$#q){ next if ($q[$i]==$s[$i][0]); for (1..$maxacum){ if ($s[$i][$_]==$j){ $s[$i][0]-=$n; $s[0][0]-=$n; last; } } } } if ($t[$j]<=0 || $y[$j]<=0) { $ZZ -= 15000; }else{ $numperson += $t[$j] } } $ZZ += $numperson*35000+$numteam*15000; # wages of personal and a plus per team leader if ($ZZ <= $MINRESOLVE){ $MINRESOLVE=$ZZ; $numsol++; print "\nSolution $numsol: attempts $tryed objective $ZZ"; print " personal $numperson equipos $numteam\n"; for $i (1..$#q){ print "Shift $i demand $q[$i] supply $s[$i][0]: "; for $j (1..$numteam){ for (1..$maxacum){ if ($s[$i][$_]==$j){ print "team$j $t[$j], "; # workers } } } print "\n"; } print "Total demand $q[0], total supply $s[0][0]\nTeams $y[0]: "; for (1..$numteam){ print "team$_ $y[$_], "; } print "\n"; print "Desde inicio, ",((time-$TIME)/60)," minutos de tiempo real;\n"; my ($user,$system,$cuser,$csystem) = times; print "Se han consumido $user y $system segundos/cpu de usuario y sistema.\n"; } } sub check { my ($i,$j) = @_; my ($n,$a,$b,$short); if ($y[$j]>=$maxwork) { return -1; } for ($i-2..$i+2){ if ($_==$#q+2) { $n=2; } elsif ($_==$#q+1) { $n=1; } elsif ($_==0) { $n= $#q; } elsif ($_==-1) { $n= $#q-1; } else { $n = $_; } for (1..$maxacum){ if ($s[$n][$_]==$j){ return 1; } } } return 0; } __END__


Back