EgtNumKernel 1.6l4 :
- aggiunta gestione ShortestPath.
This commit is contained in:
@@ -0,0 +1,478 @@
|
||||
//----------------------------------------------------------------------------
|
||||
// EgalTech 2015-2015
|
||||
//----------------------------------------------------------------------------
|
||||
// File : ShortestPathTsp.cpp Data : 22.12.15 Versione : 1.6l4
|
||||
// Contenuto : Calcolo del percorso minimo nel caso generale.
|
||||
//
|
||||
//
|
||||
//
|
||||
// Modifiche : 22.12.15 DS Creazione modulo.
|
||||
//
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//--------------------------- Include ----------------------------------------
|
||||
#include "stdafx.h"
|
||||
#include "DllMain.h"
|
||||
#include "ShortestPath.h"
|
||||
#include "/EgtDev/Include/EGnStringUtils.h"
|
||||
#include <new>
|
||||
|
||||
using namespace std ;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
#if defined( _DEBUG)
|
||||
#define MY_LOG(szOut) LOG_INFO( GetENkLogger(), szOut) ;
|
||||
#else
|
||||
#define MY_LOG(szOut) ;
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
ShortestPath::Tsp( void)
|
||||
{
|
||||
// almeno un punto
|
||||
if ( m_nNumPnts < 1)
|
||||
return false ;
|
||||
|
||||
// per path aperta aggiungo un punto con distanze nulle da tutti gli altri ( per poter creare il lato nullo)
|
||||
if ( m_nType == SP_OPEN) {
|
||||
if ( m_nNumPnts <= MAX_SPPS) {
|
||||
m_Points[m_nNumPnts].dXi = 0 ;
|
||||
m_Points[m_nNumPnts].dYi = 0 ;
|
||||
m_Points[m_nNumPnts].dXf = 0 ;
|
||||
m_Points[m_nNumPnts].dYf = 0 ;
|
||||
++ m_nNumPnts ;
|
||||
}
|
||||
else
|
||||
return false ;
|
||||
}
|
||||
|
||||
// alloco la matrice delle distanze
|
||||
if ( m_Dists != nullptr)
|
||||
delete m_Dists ;
|
||||
m_Dists = new(nothrow) unsigned[ m_nNumPnts * m_nNumPnts] ;
|
||||
if ( m_Dists == nullptr)
|
||||
return false ;
|
||||
// alloco la matrice ausiliaria
|
||||
if ( m_Available != nullptr)
|
||||
delete m_Available ;
|
||||
m_Available = new(nothrow) bool[ m_nNumPnts * m_nNumPnts] ;
|
||||
if ( m_Available == nullptr)
|
||||
return false ;
|
||||
// alloco la path principale
|
||||
if ( m_pMain != nullptr)
|
||||
delete m_pMain ;
|
||||
m_pMain = new(nothrow) NODE[ m_nNumPnts] ;
|
||||
if ( m_pMain == nullptr)
|
||||
return false ;
|
||||
// alloco la copia della path principale
|
||||
if ( m_pDupl != nullptr)
|
||||
delete m_pDupl ;
|
||||
m_pDupl = new(nothrow) NODE[ m_nNumPnts] ;
|
||||
if ( m_pDupl == nullptr)
|
||||
return false ;
|
||||
|
||||
// calcolo la matrice delle distanze
|
||||
CalcDistances() ;
|
||||
|
||||
// organizzo percorsi
|
||||
PreparePath( m_pMain) ;
|
||||
PreparePath( m_pDupl) ;
|
||||
|
||||
// inizializzo minimo costo
|
||||
m_nMinCost = INT_MAX ;
|
||||
|
||||
// ---- applico algoritmo NearNeighbor con miglioramenti aggiuntivi ----
|
||||
unsigned nMinNN = NearNeighbor() ;
|
||||
string sOut = "-- NearNeighbor : TotalCost = " + ToString( (int)nMinNN) ;
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
// se migliora, salvo l'ordinamento
|
||||
if ( nMinNN < m_nMinCost) {
|
||||
MY_LOG( " Improve") ;
|
||||
m_nMinCost = nMinNN ;
|
||||
UpdateOrder( m_pMain) ;
|
||||
}
|
||||
// salvo il percorso in un duplicato
|
||||
SavePath( m_pMain) ;
|
||||
// miglioramenti aggiuntivi
|
||||
CalculateImprovements( m_pMain) ;
|
||||
|
||||
// ---- Applico percorso invertito, se risultato precedente scarso ----
|
||||
const double COEFF_INVERTED = 1.2 ;
|
||||
const int MIN_PNTS_INVERTED = 128 ;
|
||||
if ( m_nMinCost > COEFF_INVERTED * m_nTotMin || m_nNumPnts < MIN_PNTS_INVERTED) {
|
||||
// inverto il percorso
|
||||
NODE* pCurr = m_pDupl->pPrev ;
|
||||
NODE* pPath = m_pMain ;
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) {
|
||||
pPath->nPos = pCurr->nPos ;
|
||||
pPath = pPath->pNext ;
|
||||
pCurr = pCurr->pPrev ;
|
||||
}
|
||||
// ne calcolo il risultato
|
||||
unsigned nMinInv = TotalCost( m_pMain) ;
|
||||
string sOut = "-- Inverted NN : TotalCost = " + ToString( (int)nMinInv) ;
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
// se migliora, salvo l'ordinamento
|
||||
if ( nMinInv < m_nMinCost) {
|
||||
MY_LOG( " Improve") ;
|
||||
m_nMinCost = nMinInv ;
|
||||
UpdateOrder( m_pMain) ;
|
||||
}
|
||||
// salvo il percorso in un duplicato
|
||||
SavePath( m_pMain) ;
|
||||
// miglioramenti aggiuntivi
|
||||
CalculateImprovements( m_pMain) ;
|
||||
}
|
||||
else
|
||||
MY_LOG( "-- Inverted NN : Not necessary") ;
|
||||
|
||||
// ---- Applico pessima partenza, se risultato precedente scarso ----
|
||||
const double COEFF_FARNEIG = 1.4 ;
|
||||
const int MIN_PNTS_FARNEIG = 128 ;
|
||||
if ( m_nMinCost > COEFF_FARNEIG * m_nTotMin || m_nNumPnts < MIN_PNTS_FARNEIG) {
|
||||
unsigned nMinFN = FarNeighbor() ;
|
||||
string sOut = "-- FarNeighbor : TotalCost = " + ToString( (int)nMinFN) ;
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
// se migliora, salvo l'ordinamento
|
||||
if ( nMinFN < m_nMinCost) {
|
||||
MY_LOG( " Improve") ;
|
||||
m_nMinCost = nMinFN ;
|
||||
UpdateOrder( m_pMain) ;
|
||||
}
|
||||
// salvo il percorso in un duplicato
|
||||
SavePath( m_pMain) ;
|
||||
// miglioramenti aggiuntivi
|
||||
CalculateImprovements( m_pMain) ;
|
||||
}
|
||||
else
|
||||
MY_LOG( "-- FarNeighbor : Not necessary") ;
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
ShortestPath::CalculateImprovements( NODE* pPath)
|
||||
{
|
||||
// Ottimizzazione con movimento di singolo punto
|
||||
RestorePath( pPath) ;
|
||||
unsigned nPntOpt = PointOpt( pPath) ;
|
||||
string sOut = " PointOpt : TotalCost = " + ToString( (int)nPntOpt) ;
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
// se migliora, salvo l'ordinamento
|
||||
if ( nPntOpt < m_nMinCost) {
|
||||
MY_LOG( " Improve") ;
|
||||
m_nMinCost = nPntOpt ;
|
||||
UpdateOrder( pPath) ;
|
||||
}
|
||||
|
||||
// Ottimizzazione con movimento di due punti (lanciata solo se non ci sono troppi punti)
|
||||
const int MAX_NODES_2OPT = 768 ;
|
||||
if ( m_nNumPnts <= MAX_NODES_2OPT) {
|
||||
RestorePath( pPath) ;
|
||||
unsigned nTwoOpt = TwoOpt( pPath) ;
|
||||
string sOut = " TwoOpt : TotalCost = " + ToString( (int)nTwoOpt) ;
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
// se migliora, salvo l'ordinamento
|
||||
if ( nTwoOpt < m_nMinCost) {
|
||||
MY_LOG( " Improve") ;
|
||||
m_nMinCost = nTwoOpt ;
|
||||
UpdateOrder( pPath) ;
|
||||
}
|
||||
}
|
||||
|
||||
// Ottimizzazione ibrida (lanciata solo se non ci sono troppi punti)
|
||||
const int MAX_NODES_HYBRID = 512 ;
|
||||
if ( m_nNumPnts <= MAX_NODES_HYBRID) {
|
||||
RestorePath( pPath) ;
|
||||
unsigned nHybOpt = Hybrid( pPath) ;
|
||||
string sOut = " Hybrid : TotalCost = " + ToString( (int)nHybOpt) ;
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
// se migliora, salvo l'ordinamento
|
||||
if ( nHybOpt < m_nMinCost) {
|
||||
MY_LOG( " Improve") ;
|
||||
m_nMinCost = nHybOpt ;
|
||||
UpdateOrder( pPath) ;
|
||||
}
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
unsigned
|
||||
ShortestPath::GetDistance( float dDx, float dDy, float dDz, float dDh, float dDv)
|
||||
{
|
||||
// parte lineare
|
||||
unsigned nDist = unsigned( sqrt( dDx * dDx + dDy * dDy + dDz * dDz) + 0.5) ;
|
||||
// eventuali parti angolari
|
||||
if ( abs( dDh) > 1 && abs( dDv) > 1)
|
||||
nDist += unsigned( max( m_dAngHAdd, m_dAngVAdd) + m_dAngHMul * abs( dDh) + m_dAngVMul * abs( dDv)) ;
|
||||
else if ( abs( dDh) > 1)
|
||||
nDist += unsigned( m_dAngHAdd + m_dAngHMul * abs( dDh)) ;
|
||||
else if ( abs( dDv) > 1)
|
||||
nDist += unsigned( m_dAngVAdd + m_dAngVMul * abs( dDv)) ;
|
||||
return nDist ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void
|
||||
ShortestPath::CalcDistances( void)
|
||||
{
|
||||
// inizializzo somma delle minime distanze
|
||||
m_nTotMin = 0 ;
|
||||
|
||||
// in generale si calcolano le distanze per tutti i punti
|
||||
unsigned nLimit = m_nNumPnts ;
|
||||
|
||||
// nel caso di percorso aperto, ultima riga e ultima colonna si calcolano in modo speciale
|
||||
if ( m_nType == SP_OPEN) {
|
||||
// calcolo box dei punti
|
||||
float fXmin = (float) m_Points[0].dXi ;
|
||||
float fXmax = (float) m_Points[0].dXi ;
|
||||
float fYmin = (float) m_Points[0].dYi ;
|
||||
float fYmax = (float) m_Points[0].dYi ;
|
||||
float fZmin = (float) m_Points[0].dZi ;
|
||||
float fZmax = (float) m_Points[0].dZi ;
|
||||
// ciclo sui punti
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts - 1 ; ++ i) {
|
||||
// X
|
||||
if ( m_Points[i].dXi < fXmin)
|
||||
fXmin = (float) m_Points[i].dXi ;
|
||||
if ( m_Points[i].dXi > fXmax)
|
||||
fXmax = (float) m_Points[i].dXi ;
|
||||
if ( m_Points[i].dXf < fXmin)
|
||||
fXmin = (float) m_Points[i-1].dXf ;
|
||||
if ( m_Points[i].dXf > fXmax)
|
||||
fXmax = (float) m_Points[i].dXf ;
|
||||
// Y
|
||||
if ( m_Points[i].dYi < fYmin)
|
||||
fYmin = (float) m_Points[i].dYi ;
|
||||
if ( m_Points[i].dYi > fYmax)
|
||||
fYmax = (float) m_Points[i].dYi ;
|
||||
if ( m_Points[i].dYf < fYmin)
|
||||
fYmin = (float) m_Points[i].dYf ;
|
||||
if ( m_Points[i].dYf> fYmax)
|
||||
fYmax = (float) m_Points[i].dYf ;
|
||||
// Z
|
||||
if ( m_Points[i].dZi < fZmin)
|
||||
fZmin = (float) m_Points[i].dZi ;
|
||||
if ( m_Points[i].dZi > fZmax)
|
||||
fZmax = (float) m_Points[i].dZi ;
|
||||
if ( m_Points[i].dZf < fZmin)
|
||||
fZmin = (float) m_Points[i].dZf ;
|
||||
if ( m_Points[i].dZf > fZmax)
|
||||
fZmax = (float) m_Points[i].dZf ;
|
||||
}
|
||||
// imposto minimo di linea
|
||||
unsigned nRowMinDist = INT_MAX ;
|
||||
// assegno le distanze sull'ultima riga ( dal precedente all'inizio del percorso aperto) tranne ultimo elemento
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts - 1 ; ++ i) {
|
||||
unsigned nDist ;
|
||||
// il valore dipende dalla condizione imposta
|
||||
switch ( m_ObStart.nF) {
|
||||
case OB_NEAR_PNT : {
|
||||
float dDx = float( m_ObStart.dX - m_Points[i].dXi) ;
|
||||
float dDy = float( m_ObStart.dY - m_Points[i].dYi) ;
|
||||
float dDz = float( m_ObStart.dZ - m_Points[i].dZi) ;
|
||||
float dDh = float( m_ObStart.dH - m_Points[i].dHi) ;
|
||||
float dDv = float( m_ObStart.dV - m_Points[i].dVi) ;
|
||||
nDist = GetDistance( dDx, dDy, dDz, dDh, dDv) ;
|
||||
} break ;
|
||||
case OB_XMIN :
|
||||
nDist = unsigned( m_Points[i].dXi - fXmin + 0.5) ;
|
||||
break ;
|
||||
case OB_XMAX :
|
||||
nDist = unsigned( fXmax - m_Points[i].dXi + 0.5) ;
|
||||
break ;
|
||||
case OB_YMIN :
|
||||
nDist = unsigned( m_Points[i].dYi - fYmin + 0.5) ;
|
||||
break ;
|
||||
case OB_YMAX :
|
||||
nDist = unsigned( fYmax - m_Points[i].dYi + 0.5) ;
|
||||
break ;
|
||||
default : // OB_NONE
|
||||
nDist = 0 ;
|
||||
break ;
|
||||
}
|
||||
m_Dists[ Index( m_nNumPnts - 1, i)] = nDist ;
|
||||
// verifico se nuovo minimo di linea
|
||||
if ( nDist < nRowMinDist)
|
||||
nRowMinDist = nDist ;
|
||||
}
|
||||
// aggiorno il totale delle minime distanze
|
||||
m_nTotMin += nRowMinDist ;
|
||||
// assegno le distanze sull'ultima colonna ( dalla fine del percorso aperto al successivo) tranne ultimo elemento
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts - 1 ; ++ i) {
|
||||
unsigned nDist ;
|
||||
// il valore dipende dalla condizione imposta
|
||||
switch ( m_ObEnd.nF) {
|
||||
case OB_NEAR_PNT : {
|
||||
float dDx = float( m_ObEnd.dX - m_Points[i].dXf) ;
|
||||
float dDy = float( m_ObEnd.dY - m_Points[i].dYf) ;
|
||||
float dDz = float( m_ObEnd.dZ - m_Points[i].dZf) ;
|
||||
float dDh = float( m_ObEnd.dH - m_Points[i].dHf) ;
|
||||
float dDv = float( m_ObEnd.dV - m_Points[i].dVf) ;
|
||||
nDist = GetDistance( dDx, dDy, dDz, dDh, dDv) ;
|
||||
} break ;
|
||||
case OB_XMIN :
|
||||
nDist = unsigned( m_Points[i].dXf - fXmin + 0.5) ;
|
||||
break ;
|
||||
case OB_XMAX :
|
||||
nDist = unsigned( fXmax - m_Points[i].dXf + 0.5) ;
|
||||
break ;
|
||||
case OB_YMIN :
|
||||
nDist = unsigned( m_Points[i].dYf - fYmin + 0.5) ;
|
||||
break ;
|
||||
case OB_YMAX :
|
||||
nDist = unsigned( fYmax - m_Points[i].dYf + 0.5) ;
|
||||
break ;
|
||||
default : // OB_NONE
|
||||
nDist = 0 ;
|
||||
break ;
|
||||
}
|
||||
m_Dists[ Index( i, m_nNumPnts - 1)] = nDist ;
|
||||
}
|
||||
// punto su diagonale principale
|
||||
m_Dists[ Index( m_nNumPnts - 1, m_nNumPnts - 1)] = MAXCARD ;
|
||||
// devo ancora calcolare le righe/colonne precedenti
|
||||
nLimit = m_nNumPnts - 1 ;
|
||||
}
|
||||
|
||||
// ciclo per calcolare le distanze tra tutte le coppie di nodi
|
||||
for ( unsigned i = 0 ; i < nLimit ; ++ i) {
|
||||
unsigned nRowMinDist = INT_MAX ;
|
||||
for ( unsigned j = 0 ; j < nLimit ; ++ j) {
|
||||
if ( i != j) {
|
||||
// calcolo distanza i -> j
|
||||
float dDx = float( m_Points[i].dXf - m_Points[j].dXi) ;
|
||||
float dDy = float( m_Points[i].dYf - m_Points[j].dYi) ;
|
||||
float dDz = float( m_Points[i].dZf - m_Points[j].dZi) ;
|
||||
float dDh = float( m_Points[i].dHf - m_Points[j].dHi) ;
|
||||
float dDv = float( m_Points[i].dVf - m_Points[j].dVi) ;
|
||||
unsigned nDist = GetDistance( dDx, dDy, dDz, dDh, dDv) ;
|
||||
m_Dists[ Index( i, j)] = nDist ;
|
||||
// verifico se nuovo minimo di linea
|
||||
if ( nDist < nRowMinDist)
|
||||
nRowMinDist = nDist ;
|
||||
}
|
||||
else
|
||||
m_Dists[ Index( i, j)] = MAXCARD ;
|
||||
}
|
||||
// aggiorno il totale delle minime distanze
|
||||
m_nTotMin += nRowMinDist ;
|
||||
}
|
||||
|
||||
// nel caso di percorso aperto senza vincoli, il numero delle distanze è uno meno di quello dei veri nodi
|
||||
if ( m_nType == SP_OPEN &&
|
||||
m_ObStart.nF == OB_NONE && m_ObEnd.nF == OB_NONE)
|
||||
m_nTotMin = (unsigned) ( m_nTotMin / (double) nLimit * ( nLimit - 1)) ;
|
||||
|
||||
// stampe di debug
|
||||
#if 0
|
||||
MY_LOG( "----\nDistances :") ;
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) {
|
||||
string sOut ;
|
||||
for ( unsigned j = 0 ; j < m_nNumPnts ; ++ j) {
|
||||
sOut += ToString( int( m_Dists[ Index( i, j)]), 2) + " " ;
|
||||
}
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
}
|
||||
#endif
|
||||
string sOut = "MinCost = " + ToString( int( m_nTotMin)) ;
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void
|
||||
ShortestPath::PreparePath( NODE* pPath)
|
||||
{
|
||||
NODE* pCurr = pPath ;
|
||||
for ( unsigned i = 1 ; i < m_nNumPnts ; ++ i) {
|
||||
pCurr->pNext = (NODE*) ( (size_t) pCurr + sizeof( NODE)) ;
|
||||
pCurr->pNext->pPrev = pCurr ;
|
||||
pCurr = pCurr->pNext ;
|
||||
}
|
||||
pPath->pPrev = pCurr ;
|
||||
pCurr->pNext = pPath ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void
|
||||
ShortestPath::SavePath( NODE* pPath)
|
||||
{
|
||||
NODE* pCurr = m_pDupl ;
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) {
|
||||
pCurr->nPos = pPath->nPos ;
|
||||
pPath = pPath->pNext ;
|
||||
pCurr = pCurr->pNext ;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void
|
||||
ShortestPath::RestorePath( NODE* pPath)
|
||||
{
|
||||
NODE* pCurr = m_pDupl ;
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) {
|
||||
pPath->nPos = pCurr->nPos ;
|
||||
pPath = pPath->pNext ;
|
||||
pCurr = pCurr->pNext ;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
unsigned
|
||||
ShortestPath::TotalCost( NODE* pPath)
|
||||
{
|
||||
// ci devono essere almeno due nodi
|
||||
if ( pPath == nullptr || pPath->pNext == nullptr)
|
||||
return 0 ;
|
||||
// lunghezza del primo arco
|
||||
unsigned nCost = ArcCost( pPath->nPos, pPath->pNext->nPos) ;
|
||||
// ciclo sui nodi successivi (calcolo la lunghezza degli archi che li uniscono)
|
||||
NODE* pCurr = pPath->pNext ;
|
||||
while ( pCurr != pPath) {
|
||||
nCost += ArcCost( pCurr->nPos, pCurr->pNext->nPos) ;
|
||||
pCurr = pCurr->pNext ;
|
||||
}
|
||||
return nCost ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void
|
||||
ShortestPath::UpdateOrder( NODE* pPath)
|
||||
{
|
||||
// per circuiti aperti si parte dopo il nodo finale aggiunto per poter creare il lato nullo
|
||||
if ( m_nType == SP_OPEN) {
|
||||
NODE* pCurr = pPath ;
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) {
|
||||
if ( pCurr->nPos == m_nNumPnts - 1) {
|
||||
pPath = pCurr->pNext ;
|
||||
break ;
|
||||
}
|
||||
pCurr = pCurr->pNext ;
|
||||
}
|
||||
}
|
||||
// assegno l'ordinamento trovato
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) {
|
||||
m_nOrder[i] = pPath->nPos ;
|
||||
pPath = pPath->pNext ;
|
||||
}
|
||||
// stampe di debug
|
||||
#if 0
|
||||
MY_LOG( "Positions :") ;
|
||||
string sOut ;
|
||||
for ( unsigned i = 0 ; i < m_nNumPnts ; ++ i) {
|
||||
sOut += ToString( (int)m_nOrder[i]) + " " ;
|
||||
if ( i % 20 == 19) {
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
sOut.clear() ;
|
||||
}
|
||||
}
|
||||
if ( ! sOut.empty())
|
||||
MY_LOG( sOut.c_str()) ;
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user