#!/usr/bin/perl -w use strict; use List::Util qw(shuffle); srand(999); my $plot = 1; my $debug = 1; my $N = 100; my (@x,@y,@code, @ruta, @rutaopt, @xopt, @yopt, @codeopt, %track); my ($image, $white); my $file = 'image-vrp-d5.png'; $x[0]=0.5; # warehouse $y[0]=0.5; $code[0]="W"; $track{"W"} = 0; my $tmp; for (1..$N){ # points of demand push @x, rand; push @y, rand; $tmp = $code[$_-1]; $code[$_] = ++$tmp; $track{$code[$_]} = $_; print "$_ $code[$_] $x[$_] $y[$_]\n" if ($debug); } my @d; my $NN = $N+1; my @partner = (0) x $NN; matrix_distance(); my $Dmin=9e99; my $iter=0; my ($D, $rutas, $ropt); my @p = 1..$N; my @in; my ($fault, $faultant, $count); $faultant=9e99; do { @in = (0) x $NN; @ruta=(); $D=$rutas=$count=0; # for (my $n = $N ; $n > 0 ; $n--){ # for $n (1..$N){ # es peor, el orden importa $in[0] = 1 + $N * rand() / 2; for my $n ( shuffle( @p ) ){ # towards(random p) if (++$count <= $in[0]) { next if $in[$n]; # scalar(grep($n==$_, @ruta)); # if ($n>100 ) { $n=1; } $D += $d[0][$n]; goto cut if $D > $Dmin; push @ruta, 0; push @ruta, $n; $in[$n]=1; $rutas++; recursive($n); goto cut if $D > $Dmin; }else{ $D += 2*$d[0][$n]; # $in[$n]=1; } } $fault = scalar(grep ($_==0,@in)); # if ($fault <= $faultant) { # && $D <= $Dmin if ($D <= $Dmin) { print "$0 $iter: $rutas rutas; distancia $D faltas $fault\n"; $ropt=$rutas; @xopt=@x; @yopt=@y; @rutaopt = @ruta; @codeopt=@code; $Dmin=$D; $faultant=$fault; } # } cut: # swap(); # this line could be commented out to gain time at a minor cost # select ( undef, undef, undef, 0.001 ); # delay for cooling }until (++$iter > 2_000_000); # enough is enough ($N**3) if ($plot == 1) { use POSIX; use Imager; use Imager::Fill; # $file = 'image-vrp-d3.png'; $image=Imager->new(xsize=>1000,ysize=>1000,channels=>3); # $img_x_max = $image->getwidth; # $img_y_max = $image->getheight; # $x0 = $img_x_max / 2; # X = 0 # $y0 = $img_y_max / 2; # Y = 0 $white = Imager::Color->new( red=>255, green=>255, blue=>255 ); # Draw the coordinate axises and tick marks # $image->line(color=>$white, x1=>$x0, x2=>$x0, y1=>0, y2=>$img_y_max, aa=>0, endp=>1 ); # $image->line(color=>$white, x1=>0, x2=>$img_x_max, y1=>$y0, y2=>$y0, aa=>0, endp=>1 ); } print "$0 rutas $ropt, distancia total $Dmin\n" if ($debug); my ($i,$j); $j = shift @rutaopt; while (@rutaopt){ $i = $j; $j = shift @rutaopt; print "$track{$code[$i]}, " if ($debug); print "\n" if ($debug && $track{$code[$i]}==0); if ($plot == 1) { $image->line(color=>$white, x1=>r($xopt[$i]), x2=>r($xopt[$j]), y1=>r($yopt[$i]), y2=>r($yopt[$j]), aa=>0, endp=>1 ); } } if ($plot==1){ for my $z (1..$N){ if ($in[$z]==0){ $image -> line(color=>$white, x1=>r($x[0]), x2=>r($xopt[$z]), y1=>r($y[0]), y2=>r($yopt[$z]), aa=>0, endp=>1 ); } } } print "\n" if ($debug); if ($plot == 1){ $image->write(file => $file) or die "Cannot write: ",$image->errstr; } # -------------------------------- sub r { my $p = shift; return sprintf("%d", 1000*$p); } sub recursive { my $z = shift; return if $z==0; # return if ($in[$z]==1); # scalar(grep($z==$_,@ruta)) && $z!=$ruta[-1]); if ($in[$partner[$z]] == 1){ # $partner[$z]==$_,@ruta)) { # no $in[$z] # push @ruta, $z; push @ruta, 0; $D+= $d[$z][0]; return; }else{ $D += $d[$z][$partner[$z]]; push @ruta , $partner[$z]; $in[$partner[$z]]=1; recursive($partner[$z]); } } sub swap { my $t1 = 1 + int ($N*rand); my $t2 = 1 + int ($N*rand); # @d[$t1,$t2] = @d[$t2,$t1]; # my $tmp; # for my $i (0..$N){ # $tmp=$d[$t1][$i]; # $d[$t1][$i] = $d[$t2][$i]; # $d[$t2][$i] = $tmp; # } # ($partner[$t1],$partner[$t2]) = ($partner[$t2],$partner[$t1]); ($x[$t1],$x[$t2]) = ($x[$t2],$x[$t1]); ($y[$t1],$y[$t2]) = ($y[$t2],$y[$t1]); ($code[$t1],$code[$t2]) = ($code[$t2],$code[$t1]); matrix_distance(); } sub matrix_distance { for my $i (0..$N){ for my $j (0..$i){ $d[$i][$j] = sqrt(($x[$i]-$x[$j])**2+($y[$i]-$y[$j])**2); $d[$j][$i] = $d[$i][$j]; } } my @mini; for my $i (1..$N){ $mini[$i] = 9e99; for my $j (1..$N){ next if $i==$j; if ($d[$i][$j] < $mini[$i]){ # if ( ($x[$i]-$x[0])*($y[$j]-$y[0])-($y[$i]-$y[0])*($x[$j]-$x[0]) >= 0 ){ if (defined $partner[$j]){ if ($partner[$j] != $i){ $mini[$i] = $d[$i][$j]; $partner[$i] = $j; } } # } # Graham scan # Three points are a counter-clockwise turn if ccw > 0, clockwise if # ccw < 0, and collinear if ccw = 0 because ccw is a determinant that # gives twice the signed area of the triangle formed by p1, p2 and p3. # function ccw(p1, p2, p3): # return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x) } } } } __END__ vrp-d solo dist vrp-d3 only distance vrp-d5 changed algo; first three routes, adding until cost not reduces; consider local search Problema: