Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A fun study of some heuristics for the Travelling Salesman Problem.

License

NotificationsYou must be signed in to change notification settings

rsalmei/tsp-essay

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A fun study of some heuristics for the Travelling Salesman Problem.


I've always found the Travelling salesman problem particularly interesting.After studying a few papers on the subject (in this repository too), mainly the superbClarke & Wright's Savings Algorithm from 1997, I felt like trying a few techniques.

To see the originalipynb format (probably better):https://github.com/rsalmei/tsp-essay/blob/master/tsp_essay.ipynb

To run this yourself, you need:

  • a recent python (I'm using 3.6.5)
  • a virtualenv
  • pip install -r requirements.txt
  • jupyter notebook tsp_essay.ipynb

Enjoy :)

Setup environment

importpandasaspdimportnumpyasnp%matplotlibinline%configInlineBackend.figure_format='retina'importmatplotlib.pyplotaspltfrompylabimportrcParams
/Users/rogerio/.pyenv/versions/3.6.5/lib/python3.6/importlib/_bootstrap.py:219: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88  return f(*args, **kwds)/Users/rogerio/.pyenv/versions/3.6.5/lib/python3.6/importlib/_bootstrap.py:219: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88  return f(*args, **kwds)

Customize graphics style

print(plt.style.available)
['seaborn-dark', 'seaborn-darkgrid', 'seaborn-ticks', 'fivethirtyeight', 'seaborn-whitegrid', 'classic', '_classic_test', 'fast', 'seaborn-talk', 'seaborn-dark-palette', 'seaborn-bright', 'seaborn-pastel', 'grayscale', 'seaborn-notebook', 'ggplot', 'seaborn-colorblind', 'seaborn-muted', 'seaborn', 'Solarize_Light2', 'seaborn-paper', 'bmh', 'tableau-colorblind10', 'seaborn-white', 'dark_background', 'seaborn-poster', 'seaborn-deep']
plt.style.use('classic')rcParams['figure.figsize']=16,10

Initialize data

np.random.seed(18)size,cmin,cmax=150,-100,100data=pd.DataFrame(dict(x=[0],y=[0])).append(pd.DataFrame((np.random.random_sample(2*size)*(cmax-cmin)+cmin).reshape(-1,2),columns=['x','y']),ignore_index=True)data
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {    vertical-align: top;}.dataframe thead th {    text-align: right;}
</style>
xy
00.0000000.000000
130.0748481.090675
275.720294-63.631955
370.44661450.027257
433.22033397.579090
5-48.606315-94.338815
627.14382369.462477
747.234925-95.838578
8-77.679374-40.455252
937.39403872.325211
10-60.27312831.437806
1139.931126-29.521506
1257.99211062.809588
13-60.51574389.891059
14-4.67992333.320070
15-57.73759361.505265
16-36.062776-41.212805
17-39.61636465.577880
1810.506738-61.664519
1942.85134827.974071
206.64076614.505583
21-97.53349339.047596
226.931842-84.173057
23-97.272500-44.429392
2499.477056-54.879872
25-78.996929-96.739844
26-31.67451061.639612
27-56.53607586.920994
2897.699575-97.796661
2956.98055137.072724
.........
12189.841848-55.198411
12215.997366-84.731802
12357.065474-1.286114
1248.39740445.994062
125-95.97135991.328450
12633.324722-82.136313
12734.78326152.531436
12845.139227-60.250218
12935.98759783.219655
13067.31426413.003851
131-36.886862-35.034144
13242.06879965.334579
133-69.769883-43.590844
13433.143474-27.783041
135-41.79793475.064175
1365.32471537.861594
13768.512768-50.739106
138-64.144841-50.464696
139-26.84576679.781956
1400.06868088.957681
14118.895939-56.449630
142-26.10439040.905810
143-78.6010958.400365
14428.94101083.297786
145-61.07803833.677658
14644.080113-41.024718
147-61.0885772.871590
14815.45309721.393731
14926.54659068.767646
150-63.01289311.913804

151 rows × 2 columns

Plot problem

plt.scatter(data.x,data.y,c='r',marker='o',s=40)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Compute distance matrix

distance_func=lambdaa,b:np.sqrt((a.x-b.x)**2+ (a.y-b.y)**2)defcompute_distances(dfunc=distance_func):foriinrange(size+1):current=data.iloc[i]data[i]=dfunc(current,data)#data[str(i)][i]=np.nan%timecompute_distances()data
CPU times: user 346 ms, sys: 5.93 ms, total: 352 msWall time: 357 ms
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {    vertical-align: top;}.dataframe thead th {    text-align: right;}
</style>
xy01234567...141142143144145146147148149150
00.0000000.0000000.00000030.09461998.90697086.402846103.078947106.12438974.577630106.846484...59.52828948.52550479.04870888.18221669.74748360.21697361.15603226.39109673.71370764.129271
130.0748481.09067530.0946190.00000079.19927763.44027596.539672123.68311568.43459998.436519...58.61616468.857475108.92149682.21493096.80268744.38303491.18081925.02018667.76888093.714821
275.720294-63.63195598.90697079.1992770.000000113.781493166.719068128.062552141.68204442.996311...57.276463145.932898170.304863154.196790167.87776638.886864152.116365104.218532141.236354157.968538
370.44661450.02725786.40284663.440275113.7814930.00000060.390171187.12338947.464297147.701132...118.29961896.980912154.75147153.194391132.53695194.792692139.73247062.00133747.732738138.795084
433.22033397.579090103.07894796.539672166.71906860.3901710.000000208.63384828.765741193.924735...154.69335782.044400143.02753914.908663113.910385139.028595133.65508278.22968429.574281128.838562
5-48.606315-94.338815106.124389123.683115128.062552187.123389208.6338480.000000180.46868795.852974...77.408944137.103775107.028154193.825565128.622553106.92598998.008518132.278609179.587519107.224850
627.14382369.46247774.57763068.434599141.68204447.46429728.765741180.4686870.000000166.517540...126.18195760.422310122.10884213.95154695.203204111.777718110.54095549.4699650.916230106.958325
747.234925-95.838578106.84648498.43651942.996311147.701132193.92473595.852974166.5175400.000000...48.524090155.169851163.402757180.068054168.83765454.904573146.552646121.463982165.901224154.159518
8-77.679374-40.45525287.582604115.486087155.140639173.575323177.06559461.226474151.887491136.641483...97.89081296.33068748.864310163.34846475.969024121.76081946.394717111.798720150.97249154.384039
937.39403872.32521181.42020871.609564141.25598439.87068525.596449187.54455110.642469168.451485...130.09665470.846519132.44341013.851032105.784607113.546950120.50987955.45647511.415928117.179733
10-60.27312831.43780667.97930395.308474165.929113132.034914114.523783126.31655895.328899166.605100...118.28754235.45625129.438717103.1921512.380087127.04493828.57785376.38942794.50492319.715298
1139.931126-29.52150649.65897832.15978649.44074585.200944127.277634109.72767299.80653466.718059...34.17020596.543753124.450615113.353321119.15110412.228572106.08625256.49367599.196287110.970068
1257.99211062.80958885.48759667.739189127.67831517.84664242.691441189.89166631.557530159.012444...125.50407086.902225147.03083735.549018122.582092104.762143133.31456559.37037032.004987131.272965
13-60.51574389.891059108.363082126.854891205.254899136.89507594.050826184.61441190.008480214.722389...166.49868659.86397783.47342889.69939856.216213167.56859487.021354102.28953389.58821578.017229
14-4.67992333.32007033.64712047.398608125.95193576.96185274.603291135.00488448.156250139.201679...92.81390122.72776478.00853460.23403756.39924988.90831564.10184223.40034247.24008762.136653
15-57.73759361.50526584.359512106.587745182.948985128.69706897.850218156.11136285.253574189.146277...140.66291437.74913357.05625489.37613828.027384144.49651458.72935383.46144184.59648749.871252
16-36.062776-41.21280554.76330178.509679114.009092140.246209155.12362654.586750127.45231499.611656...57.03175682.72023065.352699140.45775078.95786780.14311050.69245181.076899126.55288459.571347
17-39.61636465.57788076.61536994.949802173.198517111.15611179.556659160.16918866.873109183.298716...135.33071728.12978069.20316170.81037138.447726135.53307566.28072770.60371566.23980058.542554
1810.506738-61.66451962.55321365.73526765.243228126.758990160.85532167.542303132.17822050.167978...9.877943108.908434113.354726146.129710119.22462339.41031596.38878883.205405131.414712104.013969
1942.85134827.97407151.17408229.76501297.32436335.32480370.268165152.72506844.362306123.890225...87.75661270.157849123.01961657.045691104.08577369.009729106.92821228.17738643.931320107.075533
206.64076614.50558315.95342327.002147104.29504373.02722587.222021122.06286558.656932117.574315...72.00577442.06206585.46021772.31644470.38043066.97252468.72127511.18497957.79804069.701862
21-97.53349339.047596105.059492133.133829201.395046168.338555143.256758142.076751128.333542197.869084...150.58397271.45326936.023443133.99208636.848832162.68370851.351098114.357458127.58976543.908023
226.931842-84.17305784.45800188.34875671.789888148.471716183.64347956.460868154.95935441.957394...30.194834129.368139126.038572168.910884136.06665556.936572110.469669105.910143154.193375118.848436
23-97.272500-44.429392106.938815135.238395174.055293192.488336192.85950469.709024168.673588153.379595...116.788665111.11704056.032174179.56637286.085715141.39361059.553834130.536369167.76393165.941462
2499.477056-54.879872113.61111489.15923225.317636108.849770166.233837153.250426143.85102266.384153...80.596406157.941751188.987345155.139934183.35859657.103298170.635735113.479903143.553342175.682608
25-78.996929-96.739844124.896406146.517791158.219947209.461105224.39376630.485314197.203121126.235072...105.859884147.458288105.140954209.914619131.642742135.100458101.208433151.249294196.296113109.823061
26-31.67451061.63961269.30163386.482119165.004877102.77923274.182119156.89472859.336275176.142212...128.46182021.46897270.96845764.36860840.576327127.58811665.71809361.97372158.65582058.777123
27-56.53607586.920994103.689859121.935621200.394455132.23369990.386990181.43318185.481724210.165367...162.00347055.16778681.56196685.55384153.436712162.76892384.17260597.34609985.04277375.286305
2897.699575-97.796661138.236731119.79903540.624081150.315118205.740751146.346747181.53163250.502624...88.992079185.918792205.814807193.708387206.14516078.090334188.010037144.813096181.125415194.589012
2956.98055137.07272467.97918844.929107102.43344318.68568465.004371168.57514344.037785133.268118...100.97954483.173313138.58025454.064519118.10739579.155743122.92288844.38874043.940802122.602601
..................................................................
12189.841848-55.198411105.44393082.10088616.448190106.998208162.932381143.874477139.53988558.881007...70.956942150.597316180.049519151.294774175.14499147.906471161.716165106.770982139.190062166.938975
12215.997366-84.73180286.22873186.96938063.340601145.343471183.12261465.314090154.59663533.153364...28.430318132.504217132.749649168.527388141.28488351.951419116.690174106.126929153.861518124.831864
12357.065474-1.28611457.07996627.09507465.07692553.029397101.700144140.80245876.81580995.062127...67.08149493.259771136.01193389.137095123.20858341.806413118.22718147.39161776.412903120.801706
1248.39740445.99406246.75436049.862068128.64771762.18015057.246772151.46861230.036557147.053904...102.98023734.87497994.77354642.58647170.55870594.05064581.77927125.59216829.12093979.125814
125-95.97135991.328450132.481650155.017749231.280675171.466411129.342815191.613623125.041868235.668275...187.17066286.16168484.727760125.17025067.388112192.69607195.086406131.553313124.57783785.982245
12633.324722-82.13631388.63921983.29041546.257927137.277982179.71543382.834751151.72474119.525516...29.461763136.642539143.959295165.492170149.41471542.495197127.044144105.061245151.056109134.634173
12734.78326152.53143663.00338951.655794123.16563635.75116345.074759168.89254618.574746148.891587...110.13300961.987590121.66989631.31613397.69776694.016941107.96994736.64985518.205969105.895608
12845.139227-60.25021875.28372063.16360230.767480113.144091158.27871199.750986130.95502235.650012...26.517063123.726290141.508196144.459031141.79052519.254651123.56661786.873475130.350663130.017410
12935.98759783.21965590.66762482.341544152.13179347.84515714.623649196.68029916.354579179.411128...140.71117175.139046136.8520897.047020108.977736124.507642126.01426565.14683817.262478122.006644
13067.31426413.00385168.55880939.09856677.09544837.15567591.188634157.98743269.290986110.679059...84.66475297.496483145.98795980.085853130.04610558.812516128.80198852.53542469.076799130.331715
131-36.886862-35.03414450.87270276.084644116.181782136.952243150.00429560.451548122.553957103.796215...59.75235576.70161860.221539135.40958072.84589781.18828744.97296676.964773121.64955553.727826
13242.06879965.33457977.70708565.353914133.28462032.24305333.436564183.62344415.485298161.255931...123.96925072.417874133.42686422.248948107.895462106.378312120.59464851.37308315.897325117.881047
133-69.769883-43.59084482.267845109.386510146.864011168.597197174.74534354.984118148.907085128.140356...89.59339595.11235352.735908160.76244877.755833113.87891347.266508107.172530147.99085555.914424
13433.143474-27.78304143.24797429.03632155.65905486.290015125.362154105.41678897.43042069.499101...32.01196090.710902117.456747111.160293112.49493717.17416999.09281452.26188396.775793104.028296
135-41.79793475.06417585.916806103.139593181.788752115.00298178.324082169.53975169.168959192.703401...144.84345737.59097276.14812171.21650545.657050144.40107374.72548678.47418068.63395866.618645
1365.32471537.86159438.23418544.324593123.51711666.24850965.911644142.77781438.401684140.114958...95.28265931.57619388.94664551.20719666.53443487.89215675.06680819.33325137.49069373.097992
13768.512768-50.73910685.25524264.52751314.770714100.784918152.459316124.971254127.12124349.866912...49.944368131.723944158.555876139.756261154.66081326.293040140.25197189.545938126.661060145.685918
138-64.144841-50.46469681.616458107.402542140.483568167.968724177.19181646.544432150.718768120.267375...83.25617598.97295260.614178162.96434384.198224108.63587853.423778107.235560149.80416262.388769
139-26.84576679.78195684.17752597.119896176.316042101.74059862.647221175.47525354.966966190.605668...143.70577138.88321588.16998255.89745557.423469140.08830484.18892272.09978254.51659176.903493
1400.06868088.95768188.95770892.849238170.31372280.42780334.254355189.64931033.363548190.720508...146.62111654.717565112.59846729.42186082.430589137.231302105.59841469.29334533.29740699.574313
14118.895939-56.44963059.52828958.61616457.276463118.299618154.69335777.408944126.18195748.524090...0.000000107.252558117.094805140.107971120.49383829.53253499.58177477.919459125.450782106.689342
142-26.10439040.90581048.52550468.857475145.93289896.98091282.044400137.10377560.422310155.169851...107.2525580.00000061.74551069.47716035.712774107.88176851.67683445.91019459.56851246.933719
143-78.6010958.40036579.048708108.921496170.304863154.751471143.027539107.028154122.108842163.402757...117.09480561.7455100.000000131.05315030.757098132.26306218.36452294.947451121.24456415.979246
14428.94101083.29778688.18221682.214930154.19679053.19439114.908663193.82556513.951546180.068054...140.10797169.477160131.0531500.000000102.789037125.240877120.72157863.35641914.726106116.409592
145-61.07803833.67765869.74748396.802687167.877766132.536951113.910385128.62255395.203204168.837654...120.49383835.71277430.757098102.7890370.000000128.99101430.80607077.51070594.38952621.849691
14644.080113-41.02471860.21697344.38303438.88686494.792692139.028595106.925989111.77771854.904573...29.532534107.881768132.263062125.240877128.9910140.000000113.96200768.670000111.183576119.462961
147-61.0885772.87159061.15603291.180819152.116365139.732470133.65508298.008518110.540955146.552646...99.58177451.67683418.364522120.72157830.806070113.9620070.00000078.750857109.6458519.244708
14815.45309721.39373126.39109625.020186104.21853262.00133778.229684132.27860949.469965121.463982...77.91945945.91019494.94745163.35641977.51070568.67000078.7508570.00000048.65545679.036577
14926.54659068.76764673.71370767.768880141.23635447.73273829.574281179.5875190.916230165.901224...125.45078259.568512121.24456414.72610694.389526111.183576109.64585148.6554560.000000106.081385
150-63.01289311.91380464.12927193.714821157.968538138.795084128.838562107.224850106.958325154.159518...106.68934246.93371915.979246116.40959221.849691119.4629619.24470879.036577106.0813850.000000

151 rows × 153 columns

Helper functions

estimate_cost=lambdaroute:sum(data.iloc[i][j]fori,jinzip(route,route[1:]))get_coords=lambdaroute:list(zip(*[(data.iloc[i].x,data.iloc[i].y)foriinroute]))

Scenario: center depot, one van, nearest neighbor heuristic

Apply a greedy NN algorithm

defnearest_neighbor(unserved,stop_condition=lambdaroute,target:False):current=0#depotresult_path=[]whileTrue:result_path.append(current)unserved.remove(current)ifnotunserved:breakcurrent=data.iloc[unserved,current+2].idxmin()ifstop_condition(result_path,int(current)):iflen(result_path)>1:breakresult_path.append(0)returnresult_path%timeroute=nearest_neighbor(list(range(size+1)))print('cost={}\nroute={}'.format(estimate_cost(route),route))
CPU times: user 116 ms, sys: 8.29 ms, total: 125 msWall time: 122 mscost=2623.828598616268route=[0, 53, 60, 20, 66, 148, 55, 51, 1, 120, 104, 123, 74, 93, 130, 42, 29, 19, 91, 107, 81, 11, 134, 117, 57, 30, 34, 67, 75, 46, 39, 131, 16, 114, 72, 96, 38, 22, 122, 92, 36, 126, 113, 7, 58, 111, 28, 108, 63, 2, 137, 50, 128, 69, 141, 18, 146, 37, 54, 95, 24, 121, 35, 83, 3, 49, 12, 73, 105, 129, 90, 144, 9, 86, 132, 31, 70, 127, 149, 6, 71, 41, 140, 76, 102, 32, 100, 97, 139, 88, 78, 26, 17, 135, 27, 13, 110, 89, 59, 119, 40, 15, 56, 145, 10, 106, 82, 94, 115, 87, 98, 44, 64, 52, 14, 33, 136, 124, 65, 142, 150, 147, 143, 109, 112, 23, 47, 101, 45, 138, 99, 133, 8, 48, 62, 80, 103, 43, 68, 61, 125, 21, 25, 118, 5, 79, 116, 77, 85, 84, 4, 0]

Plot result path

coords=get_coords(route)plt.plot(coords[0],coords[1],'b-',linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's improve it with a 2-opt heuristic

deftwoOptSwap(route,i,j):t=route[i:j+1];t.reverse();route[i:j+1]=tdeftwoOpt(route):cost=estimate_cost(route)definner():nonlocalcostforiinrange(1,len(route)-2):nodei=data.iloc[route[i]]forjinrange(i+1,len(route)-1):nodej=data.iloc[route[j]]save=nodei.iloc[route[i-1]+2]+nodej.iloc[route[j+1]+2]- (nodej.iloc[route[i-1]+2]+nodei.iloc[route[j+1]+2])ifsave>0:twoOptSwap(route,i,j)cost-=saveprint('exchanging {}-{},{}-{} with {}-{},{}-{} => save={} => cost: {}'.format(route[i-1],route[i],route[j],route[j+1],route[i-1],route[j],route[i],route[j+1],save,cost))returnFalsereturnTruewhileTrue:ifinner():break%timetwoOpt(route)
exchanging 53-64,60-52 with 53-60,64-52 => save=2.8803537373675177 => cost: 2620.948244878901...exchanging 1-120,81-107 with 1-81,120-107 => save=26.496627104257605 => cost: 2100.7350743873462CPU times: user 3min 11s, sys: 1.65 s, total: 3min 13sWall time: 3min 21s

Plot improved path

coords=get_coords(route)plt.plot(coords[0],coords[1],'b-',linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Incredible! A clean path, without any crossing parts!

Scenario: center depot, one van, 2-opt heuristic

Just for fun, let's pass a random route to 2-opt heuristic

route= [xforxinrange(size+1)]+[0]print('cost={}\nroute={}'.format(estimate_cost(route),route))
cost=16332.039263088578route=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 0]

Plot result path

coords=get_coords(route)plt.plot(coords[0],coords[1],'b-',linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's see what 2-opt heuristic can do

%timetwoOpt(route)
exchanging 0-4,1-5 with 0-1,4-5 => save=11.966404491447804 => cost: 16320.07285859713...exchanging 47-23,112-8 with 47-112,23-8 => save=11.556726776627453 => cost: 2181.5435075388827CPU times: user 19min 11s, sys: 8.18 s, total: 19min 19sWall time: 19min 44s

Plot improved path

coords=get_coords(route)plt.plot(coords[0],coords[1],'b-',linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

WOW, very cool, a clean path too, almost as good as the previous one.

But extremely slower... It took ~19 minutes, while NN + 2-opt took only ~3 minutes.

Scenario: center depot, several vans, no clustering

Let's apply a sequential NN algorithm, but including a max cost constraint

defsequential_nn(max_cost):unserved=list(range(size+1))result=[]whileTrue:result_path=nearest_neighbor(unserved,lambdaroute,target:estimate_cost(route+[target,0])>=max_cost)result.append(result_path)print('van {}: {}'.format(len(result),estimate_cost(result_path)))ifnotunserved:breakunserved.append(0)returnresult%timeresult_paths=sequential_nn(400)
van 1: 389.0742611200774van 2: 398.3533734081084van 3: 397.4195886051539van 4: 390.9917686566429van 5: 371.6762623788105van 6: 320.29683749922486van 7: 364.8394134778564van 8: 320.99556479051967van 9: 210.1189842618746van 10: 276.59421133431306CPU times: user 1.48 s, sys: 24.4 ms, total: 1.51 sWall time: 1.54 s

Plot result paths

forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's improve them with a 2-opt heuristic

defimprove_seq_nn():fori,pathinenumerate(result_paths):print('Starting path {}/{}'.format(i+1,len(result_paths)))twoOpt(path)%timeimprove_seq_nn()
Starting path 1/10exchanging 55-19,51-91 with 55-51,19-91 => save=2.6873147586143844 => cost: 386.386946361463...exchanging 67-39,75-0 with 67-75,39-0 => save=4.275672113835263 => cost: 368.50842741951476Starting path 2/10exchanging 17-97,78-0 with 17-78,97-0 => save=0.09782392307953103 => cost: 398.25554948502884...exchanging 88-78,26-0 with 88-26,78-0 => save=1.6805548383893267 => cost: 394.249851279636Starting path 3/10Starting path 4/10exchanging 124-127,65-0 with 124-65,127-0 => save=0.5258127779938491 => cost: 390.465955878649exchanging 14-65,33-0 with 14-33,65-0 => save=3.343148155406432 => cost: 387.1228077232426Starting path 5/10exchanging 0-18,141-69 with 0-141,18-69 => save=6.7520080457503155 => cost: 364.9242543330602exchanging 50-121,146-0 with 50-146,121-0 => save=41.908681513869396 => cost: 323.0155728191908Starting path 6/10exchanging 47-45,101-138 with 47-101,45-138 => save=1.4275147235708587 => cost: 318.869322775654Starting path 7/10exchanging 3-35,83-77 with 3-83,35-77 => save=3.633104522812438 => cost: 361.20630895504394...exchanging 90-129,144-0 with 90-144,129-0 => save=5.489882024385494 => cost: 347.690672350374Starting path 8/10Starting path 9/10Starting path 10/10CPU times: user 3.25 s, sys: 37.7 ms, total: 3.29 sWall time: 3.31 s

Plot improved paths

forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Great! Several clean paths, but with many superpositions, let's improve them once more.

Scenario: center depot, several vans, KMeans clustering

Compute delivery clusters

fromsklearn.clusterimportKMeanswork_per_van=20#how many deliveries each van will start, ie granularity; a better approach would be capacity.model=KMeans(n_clusters=1+size//work_per_van,init='k-means++',random_state=0)%timemodel.fit(data[['x','y']])
CPU times: user 75.7 ms, sys: 4.05 ms, total: 79.7 msWall time: 92.8 msKMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,    n_clusters=8, n_init=10, n_jobs=1, precompute_distances='auto',    random_state=0, tol=0.0001, verbose=0)

Plot clusters

plt.scatter(data.x,data.y,marker='o',c=model.labels_,s=200)plt.scatter(model.cluster_centers_[:,0],model.cluster_centers_[:,1],marker='+',c='r',s=500)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's apply a greedy NN algorithm sequentially, on each of the clusters

defgreedy_nn_clusters():result=[]foriinrange(model.n_clusters):unserved=data[model.labels_==i].index.tolist()if0notinunserved:unserved.append(0)result_path=nearest_neighbor(unserved)result.append(result_path)print('van {}: {}'.format(i+1,estimate_cost(result_path)))returnresult%timeresult_paths=greedy_nn_clusters()
van 1: 409.3080162039939van 2: 266.4745473994821van 3: 413.3606064604762van 4: 446.80922078430706van 5: 457.66477785115126van 6: 455.80304195030294van 7: 459.76894476196577van 8: 376.5008949566861CPU times: user 164 ms, sys: 10.1 ms, total: 174 msWall time: 174 ms

Plot result paths

plt.scatter(data.x,data.y,marker='o',c=model.labels_,s=200)plt.scatter(model.cluster_centers_[:,0],model.cluster_centers_[:,1],marker='+',c='r',s=500)forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],'k-',linewidth=2)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's improve them with a 2-opt heuristic

defimprove_result_paths():fori,pathinenumerate(result_paths):print('Starting path {}/{}'.format(i+1,len(result_paths)))twoOpt(path)%timeimprove_result_paths()
Starting path 1/8exchanging 48-112,99-138 with 48-99,112-138 => save=11.5162692633929 => cost: 397.791746940601exchanging 45-118,25-5 with 45-25,118-5 => save=13.984640981159586 => cost: 383.8071059594414Starting path 2/8exchanging 148-52,136-65 with 148-136,52-65 => save=1.6057804518926915 => cost: 264.8687669475894exchanging 148-124,52-142 with 148-52,124-142 => save=6.904413311483367 => cost: 257.964353636106Starting path 3/8exchanging 63-24,108-111 with 63-108,24-111 => save=11.657922849361384 => cost: 401.7026836111148...exchanging 113-36,126-0 with 113-126,36-0 => save=7.772149456496223 => cost: 378.1902685773841Starting path 4/8exchanging 147-109,150-0 with 147-150,109-0 => save=5.601665728075147 => cost: 441.2075550562319...exchanging 62-109,143-150 with 62-143,109-150 => save=0.5147852919035643 => cost: 394.93412566866925Starting path 5/8exchanging 9-149,6-144 with 9-6,149-144 => save=0.0011015802705607314 => cost: 457.66367627088067...exchanging 105-129,144-90 with 105-144,129-90 => save=3.918490824545831 => cost: 407.2051777421843Starting path 6/8exchanging 30-69,34-116 with 30-34,69-116 => save=1.1688372549307786 => cost: 454.63420469537215...exchanging 22-116,79-38 with 22-79,116-38 => save=5.039989423215701 => cost: 380.39750649818404Starting path 7/8exchanging 0-55,1-107 with 0-1,55-107 => save=14.962441964752642 => cost: 444.8065027972131...exchanging 0-107,55-42 with 0-55,107-42 => save=6.044195398723886 => cost: 403.197665386089Starting path 8/8exchanging 0-71,26-59 with 0-26,71-59 => save=17.738760993960298 => cost: 358.76213396272584...exchanging 17-78,26-88 with 17-26,78-88 => save=0.17155006280330554 => cost: 319.53217682612376CPU times: user 2.81 s, sys: 64.8 ms, total: 2.87 sWall time: 3.03 s

Plot improved paths

plt.scatter(data.x,data.y,marker='o',c=model.labels_,s=200)plt.scatter(model.cluster_centers_[:,0],model.cluster_centers_[:,1],marker='+',c='r',s=500)forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],'k-',linewidth=2)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Cool clean paths, with clearly defined cluster subsections!

Let's change the depot to the farthest point

Find farthest point and set it as new depot

maxid=data[0].idxmax()print('farthest point={}; distance={}; coords={}'.format(maxid,data.iloc[maxid,2],tuple(data.iloc[maxid][['x','y']].tolist())))data.drop(data.columns.difference(['x','y']),axis=1,inplace=True)data.iloc[0]=data.iloc[maxid]data.drop(maxid,inplace=True)data.reset_index(drop=True,inplace=True)size-=1data
farthest point=84; distance=138.29710566715653; coords=(99.07656719022526, 96.48794364952255)
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {    vertical-align: top;}.dataframe thead th {    text-align: right;}
</style>
xy
099.07656796.487944
130.0748481.090675
275.720294-63.631955
370.44661450.027257
433.22033397.579090
5-48.606315-94.338815
627.14382369.462477
747.234925-95.838578
8-77.679374-40.455252
937.39403872.325211
10-60.27312831.437806
1139.931126-29.521506
1257.99211062.809588
13-60.51574389.891059
14-4.67992333.320070
15-57.73759361.505265
16-36.062776-41.212805
17-39.61636465.577880
1810.506738-61.664519
1942.85134827.974071
206.64076614.505583
21-97.53349339.047596
226.931842-84.173057
23-97.272500-44.429392
2499.477056-54.879872
25-78.996929-96.739844
26-31.67451061.639612
27-56.53607586.920994
2897.699575-97.796661
2956.98055137.072724
.........
12089.841848-55.198411
12115.997366-84.731802
12257.065474-1.286114
1238.39740445.994062
124-95.97135991.328450
12533.324722-82.136313
12634.78326152.531436
12745.139227-60.250218
12835.98759783.219655
12967.31426413.003851
130-36.886862-35.034144
13142.06879965.334579
132-69.769883-43.590844
13333.143474-27.783041
134-41.79793475.064175
1355.32471537.861594
13668.512768-50.739106
137-64.144841-50.464696
138-26.84576679.781956
1390.06868088.957681
14018.895939-56.449630
141-26.10439040.905810
142-78.6010958.400365
14328.94101083.297786
144-61.07803833.677658
14544.080113-41.024718
146-61.0885772.871590
14715.45309721.393731
14826.54659068.767646
149-63.01289311.913804

150 rows × 2 columns

Plot relocated depot

plt.scatter(data.x,data.y,c='r',marker='o',s=40)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Recompute distance matrix

%timecompute_distances()data
CPU times: user 365 ms, sys: 5.95 ms, total: 371 msWall time: 374 ms
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {    vertical-align: top;}.dataframe thead th {    text-align: right;}
</style>
xy01234567...140141142143144145146147148149
099.07656796.4879440.000000117.736469161.81439254.57352565.865272241.29874876.842016199.190980...172.681309136.965856198.31483471.365094172.030897148.102471185.517910112.39228477.646716182.827181
130.0748481.090675117.7364690.00000079.19927763.44027596.539672123.68311568.43459998.436519...58.61616468.857475108.92149682.21493096.80268744.38303491.18081925.02018667.76888093.714821
275.720294-63.631955161.81439279.1992770.000000113.781493166.719068128.062552141.68204442.996311...57.276463145.932898170.304863154.196790167.87776638.886864152.116365104.218532141.236354157.968538
370.44661450.02725754.57352563.440275113.7814930.00000060.390171187.12338947.464297147.701132...118.29961896.980912154.75147153.194391132.53695194.792692139.73247062.00133747.732738138.795084
433.22033397.57909065.86527296.539672166.71906860.3901710.000000208.63384828.765741193.924735...154.69335782.044400143.02753914.908663113.910385139.028595133.65508278.22968429.574281128.838562
5-48.606315-94.338815241.298748123.683115128.062552187.123389208.6338480.000000180.46868795.852974...77.408944137.103775107.028154193.825565128.622553106.92598998.008518132.278609179.587519107.224850
627.14382369.46247776.84201668.434599141.68204447.46429728.765741180.4686870.000000166.517540...126.18195760.422310122.10884213.95154695.203204111.777718110.54095549.4699650.916230106.958325
747.234925-95.838578199.19098098.43651942.996311147.701132193.92473595.852974166.5175400.000000...48.524090155.169851163.402757180.068054168.83765454.904573146.552646121.463982165.901224154.159518
8-77.679374-40.455252223.598080115.486087155.140639173.575323177.06559461.226474151.887491136.641483...97.89081296.33068748.864310163.34846475.969024121.76081946.394717111.798720150.97249154.384039
937.39403872.32521166.24629871.609564141.25598439.87068525.596449187.54455110.642469168.451485...130.09665470.846519132.44341013.851032105.784607113.546950120.50987955.45647511.415928117.179733
10-60.27312831.437806172.11579295.308474165.929113132.034914114.523783126.31655895.328899166.605100...118.28754235.45625129.438717103.1921512.380087127.04493828.57785376.38942794.50492319.715298
1139.931126-29.521506139.19973032.15978649.44074585.200944127.277634109.72767299.80653466.718059...34.17020596.543753124.450615113.353321119.15110412.228572106.08625256.49367599.196287110.970068
1257.99211062.80958853.12404767.739189127.67831517.84664242.691441189.89166631.557530159.012444...125.50407086.902225147.03083735.549018122.582092104.762143133.31456559.37037032.004987131.272965
13-60.51574389.891059159.728596126.854891205.254899136.89507594.050826184.61441190.008480214.722389...166.49868659.86397783.47342889.69939856.216213167.56859487.021354102.28953389.58821578.017229
14-4.67992333.320070121.47258847.398608125.95193576.96185274.603291135.00488448.156250139.201679...92.81390122.72776478.00853460.23403756.39924988.90831564.10184223.40034247.24008762.136653
15-57.73759361.505265160.668816106.587745182.948985128.69706897.850218156.11136285.253574189.146277...140.66291437.74913357.05625489.37613828.027384144.49651458.72935383.46144184.59648749.871252
16-36.062776-41.212805192.93558178.509679114.009092140.246209155.12362654.586750127.45231499.611656...57.03175682.72023065.352699140.45775078.95786780.14311050.69245181.076899126.55288459.571347
17-39.61636465.577880142.09560694.949802173.198517111.15611179.556659160.16918866.873109183.298716...135.33071728.12978069.20316170.81037138.447726135.53307566.28072770.60371566.23980058.542554
1810.506738-61.664519181.26449265.73526765.243228126.758990160.85532167.542303132.17822050.167978...9.877943108.908434113.354726146.129710119.22462339.41031596.38878883.205405131.414712104.013969
1942.85134827.97407188.63084129.76501297.32436335.32480370.268165152.72506844.362306123.890225...87.75661270.157849123.01961657.045691104.08577369.009729106.92821228.17738643.931320107.075533
206.64076614.505583123.55357127.002147104.29504373.02722587.222021122.06286558.656932117.574315...72.00577442.06206585.46021772.31644470.38043066.97252468.72127511.18497957.79804069.701862
21-97.53349339.047596204.828975133.133829201.395046168.338555143.256758142.076751128.333542197.869084...150.58397271.45326936.023443133.99208636.848832162.68370851.351098114.357458127.58976543.908023
226.931842-84.173057202.80297788.34875671.789888148.471716183.64347956.460868154.95935441.957394...30.194834129.368139126.038572168.910884136.06665556.936572110.469669105.910143154.193375118.848436
23-97.272500-44.429392241.682957135.238395174.055293192.488336192.85950469.709024168.673588153.379595...116.788665111.11704056.032174179.56637286.085715141.39361059.553834130.536369167.76393165.941462
2499.477056-54.879872151.36834689.15923225.317636108.849770166.233837153.250426143.85102266.384153...80.596406157.941751188.987345155.139934183.35859657.103298170.635735113.479903143.553342175.682608
25-78.996929-96.739844262.768240146.517791158.219947209.461105224.39376630.485314197.203121126.235072...105.859884147.458288105.140954209.914619131.642742135.100458101.208433151.249294196.296113109.823061
26-31.67451061.639612135.31537386.482119165.004877102.77923274.182119156.89472859.336275176.142212...128.46182021.46897270.96845764.36860840.576327127.58811665.71809361.97372158.65582058.777123
27-56.53607586.920994155.906450121.935621200.394455132.23369990.386990181.43318185.481724210.165367...162.00347055.16778681.56196685.55384153.436712162.76892384.17260597.34609985.04277375.286305
2897.699575-97.796661194.289484119.79903540.624081150.315118205.740751146.346747181.53163250.502624...88.992079185.918792205.814807193.708387206.14516078.090334188.010037144.813096181.125415194.589012
2956.98055137.07272472.81650244.929107102.43344318.68568465.004371168.57514344.037785133.268118...100.97954483.173313138.58025454.064519118.10739579.155743122.92288844.38874043.940802122.602601
..................................................................
12089.841848-55.198411151.96720182.10088616.448190106.998208162.932381143.874477139.53988558.881007...70.956942150.597316180.049519151.294774175.14499147.906471161.716165106.770982139.190062166.938975
12115.997366-84.731802199.35583886.96938063.340601145.343471183.12261465.314090154.59663533.153364...28.430318132.504217132.749649168.527388141.28488351.951419116.690174106.126929153.861518124.831864
12257.065474-1.286114106.41756627.09507465.07692553.029397101.700144140.80245876.81580995.062127...67.08149493.259771136.01193389.137095123.20858341.806413118.22718147.39161776.412903120.801706
1238.39740445.994062103.78989749.862068128.64771762.18015057.246772151.46861230.036557147.053904...102.98023734.87497994.77354642.58647170.55870594.05064581.77927125.59216829.12093979.125814
124-95.97135991.328450195.116154155.017749231.280675171.466411129.342815191.613623125.041868235.668275...187.17066286.16168484.727760125.17025067.388112192.69607195.086406131.553313124.57783785.982245
12533.324722-82.136313190.34161583.29041546.257927137.277982179.71543382.834751151.72474119.525516...29.461763136.642539143.959295165.492170149.41471542.495197127.044144105.061245151.056109134.634173
12634.78326152.53143677.88327151.655794123.16563635.75116345.074759168.89254618.574746148.891587...110.13300961.987590121.66989631.31613397.69776694.016941107.96994736.64985518.205969105.895608
12745.139227-60.250218165.75912663.16360230.767480113.144091158.27871199.750986130.95502235.650012...26.517063123.726290141.508196144.459031141.79052519.254651123.56661786.873475130.350663130.017410
12835.98759783.21965564.46910782.341544152.13179347.84515714.623649196.68029916.354579179.411128...140.71117175.139046136.8520897.047020108.977736124.507642126.01426565.14683817.262478122.006644
12967.31426413.00385189.32210039.09856677.09544837.15567591.188634157.98743269.290986110.679059...84.66475297.496483145.98795980.085853130.04610558.812516128.80198852.53542469.076799130.331715
130-36.886862-35.034144189.16689376.084644116.181782136.952243150.00429560.451548122.553957103.796215...59.75235576.70161860.221539135.40958072.84589781.18828744.97296676.964773121.64955553.727826
13142.06879965.33457964.96474265.353914133.28462032.24305333.436564183.62344415.485298161.255931...123.96925072.417874133.42686422.248948107.895462106.378312120.59464851.37308315.897325117.881047
132-69.769883-43.590844219.388219109.386510146.864011168.597197174.74534354.984118148.907085128.140356...89.59339595.11235352.735908160.76244877.755833113.87891347.266508107.172530147.99085555.914424
13333.143474-27.783041140.67853629.03632155.65905486.290015125.362154105.41678897.43042069.499101...32.01196090.710902117.456747111.160293112.49493717.17416999.09281452.26188396.775793104.028296
134-41.79793475.064175142.494221103.139593181.788752115.00298178.324082169.53975169.168959192.703401...144.84345737.59097276.14812171.21650545.657050144.40107374.72548678.47418068.63395866.618645
1355.32471537.861594110.57331844.324593123.51711666.24850965.911644142.77781438.401684140.114958...95.28265931.57619388.94664551.20719666.53443487.89215675.06680819.33325137.49069373.097992
13668.512768-50.739106150.36605364.52751314.770714100.784918152.459316124.971254127.12124349.866912...49.944368131.723944158.555876139.756261154.66081326.293040140.25197189.545938126.661060145.685918
137-64.144841-50.464696219.627654107.402542140.483568167.968724177.19181646.544432150.718768120.267375...83.25617598.97295260.614178162.96434384.198224108.63587853.423778107.235560149.80416262.388769
138-26.84576679.781956127.02568397.119896176.316042101.74059862.647221175.47525354.966966190.605668...143.70577138.88321588.16998255.89745557.423469140.08830484.18892272.09978254.51659176.903493
1390.06868088.95768199.29384092.849238170.31372280.42780334.254355189.64931033.363548190.720508...146.62111654.717565112.59846729.42186082.430589137.231302105.59841469.29334533.29740699.574313
14018.895939-56.449630172.68130958.61616457.276463118.299618154.69335777.408944126.18195748.524090...0.000000107.252558117.094805140.107971120.49383829.53253499.58177477.919459125.450782106.689342
141-26.10439040.905810136.96585668.857475145.93289896.98091282.044400137.10377560.422310155.169851...107.2525580.00000061.74551069.47716035.712774107.88176851.67683445.91019459.56851246.933719
142-78.6010958.400365198.314834108.921496170.304863154.751471143.027539107.028154122.108842163.402757...117.09480561.7455100.000000131.05315030.757098132.26306218.36452294.947451121.24456415.979246
14328.94101083.29778671.36509482.214930154.19679053.19439114.908663193.82556513.951546180.068054...140.10797169.477160131.0531500.000000102.789037125.240877120.72157863.35641914.726106116.409592
144-61.07803833.677658172.03089796.802687167.877766132.536951113.910385128.62255395.203204168.837654...120.49383835.71277430.757098102.7890370.000000128.99101430.80607077.51070594.38952621.849691
14544.080113-41.024718148.10247144.38303438.88686494.792692139.028595106.925989111.77771854.904573...29.532534107.881768132.263062125.240877128.9910140.000000113.96200768.670000111.183576119.462961
146-61.0885772.871590185.51791091.180819152.116365139.732470133.65508298.008518110.540955146.552646...99.58177451.67683418.364522120.72157830.806070113.9620070.00000078.750857109.6458519.244708
14715.45309721.393731112.39228425.020186104.21853262.00133778.229684132.27860949.469965121.463982...77.91945945.91019494.94745163.35641977.51070568.67000078.7508570.00000048.65545679.036577
14826.54659068.76764677.64671667.768880141.23635447.73273829.574281179.5875190.916230165.901224...125.45078259.568512121.24456414.72610694.389526111.183576109.64585148.6554560.000000106.081385
149-63.01289311.913804182.82718193.714821157.968538138.795084128.838562107.224850106.958325154.159518...106.68934246.93371915.979246116.40959221.849691119.4629619.24470879.036577106.0813850.000000

150 rows × 152 columns

Scenario: farthest depot, one van, nearest neighbor heuristic

Apply a greedy NN algorithm

%timeroute=nearest_neighbor(list(range(size+1)))print('cost={}\nroute={}'.format(estimate_cost(route),route))
CPU times: user 111 ms, sys: 13.4 ms, total: 124 msWall time: 118 mscost=2598.9001149206997route=[0, 84, 77, 83, 3, 49, 12, 73, 104, 128, 89, 143, 9, 85, 131, 31, 70, 126, 148, 6, 71, 41, 139, 76, 101, 32, 99, 96, 138, 87, 78, 26, 17, 134, 27, 13, 109, 88, 59, 118, 40, 15, 56, 144, 10, 105, 82, 93, 114, 86, 97, 44, 64, 53, 60, 20, 66, 147, 55, 51, 1, 119, 103, 122, 74, 92, 129, 42, 29, 19, 90, 106, 81, 11, 133, 116, 57, 30, 34, 67, 75, 46, 39, 130, 16, 113, 72, 95, 38, 22, 121, 91, 36, 125, 112, 7, 58, 110, 28, 107, 63, 2, 136, 50, 127, 69, 140, 18, 145, 37, 54, 94, 24, 120, 35, 123, 135, 33, 14, 52, 65, 141, 149, 146, 142, 108, 111, 23, 47, 100, 45, 137, 98, 132, 8, 48, 62, 80, 102, 43, 68, 61, 124, 21, 25, 117, 5, 79, 115, 4, 0]

Plot result path

coords=get_coords(route)plt.plot(coords[0],coords[1],'b-',linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's improve it with a 2-opt heuristic

%timetwoOpt(route)
exchanging 84-4,77-0 with 84-77,4-0 => save=15.967192548480725 => cost: 2582.932922372219...exchanging 3-49,12-83 with 3-12,49-83 => save=1.0654890882007315 => cost: 2111.9918767877866CPU times: user 3min 36s, sys: 1.67 s, total: 3min 38sWall time: 3min 44s

Plot improved path

coords=get_coords(route)plt.plot(coords[0],coords[1],'b-',linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

A great optimized route too.

Scenario: farthest depot, one van, 2-opt heuristic

Just for fun, let's pass a random route to 2-opt heuristic

route= [xforxinrange(size+1)]+[0]print('cost={}\nroute={}'.format(estimate_cost(route),route))
cost=16502.514122712284route=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 0]

Plot result path

coords=get_coords(route)plt.plot(coords[0],coords[1],'b-',linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's see what 2-opt heuristic can do

%timetwoOpt(route)
exchanging 0-2,1-3 with 0-1,2-3 => save=6.263294946633721 => cost: 16496.25082776565...exchanging 31-70,126-131 with 31-126,70-131 => save=1.7915970025161734 => cost: 2147.6785119636334CPU times: user 30min 1s, sys: 27.7 s, total: 30min 29sWall time: 33min 24s

Plot improved path

coords=get_coords(route)plt.plot(coords[0],coords[1],'b-',linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Yes, it's beautiful what this heuristic can do.

Only again extremely slower. It's best suited to improve a path, not to build one.

Scenario: farthest depot, several vans, no clustering

Let's apply a sequential NN algorithm, but including a max cost constraint

%timeresult_paths=sequential_nn(400)
van 1: 385.64822798111754van 2: 396.65132151762236van 3: 356.93203827425646van 4: 396.0161561312648van 5: 387.061496278307van 6: 395.6147269472501van 7: 369.199156243418van 8: 394.6734767469922van 9: 397.84112119751114van 10: 398.459895479128van 11: 398.88707002367426van 12: 393.1275886242274van 13: 399.0252974546198van 14: 369.5354695202354van 15: 399.04095929693733van 16: 398.1246462968952van 17: 390.2323088432243van 18: 391.6922432861831van 19: 396.62966713208937van 20: 397.37088575736055van 21: 398.69882895151926van 22: 398.71167524672734van 23: 401.9265755043099van 24: 405.5614801848182van 25: 405.6059533951813van 26: 406.6405588064364van 27: 409.6579507334966van 28: 416.22815658152126van 29: 426.0013563494297van 30: 427.5654816187484van 31: 428.5781354316992van 32: 438.7764370279704van 33: 439.2553078706148van 34: 445.7400163235422van 35: 447.1961602342642van 36: 461.09599384348525van 37: 464.4374453992292van 38: 471.7901899743616van 39: 482.2725996546565van 40: 482.59749534967307van 41: 483.3659132930797van 42: 494.26350457825447van 43: 525.5364809069648van 44: 551.3280289580272CPU times: user 1.22 s, sys: 48.8 ms, total: 1.27 sWall time: 1.43 s

Plot result paths

forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Max cost 400 doesn't quite work here, as the distances are much larger

%timeresult_paths=sequential_nn(500)
van 1: 493.1613509811791van 2: 498.37402590912467van 3: 499.57415652250484van 4: 490.0564767221547van 5: 497.67366460125766van 6: 491.0474410501304van 7: 476.1857519522164van 8: 487.87440569372217van 9: 462.17325815341474van 10: 486.2976818275437van 11: 498.3091236408933van 12: 466.4397450832517van 13: 464.4374453992292van 14: 494.26350457825447van 15: 525.5364809069648van 16: 551.3280289580272CPU times: user 573 ms, sys: 20.1 ms, total: 593 msWall time: 588 ms

Plot result paths

forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's improve them with a 2-opt heuristic

%timeimprove_result_paths()
Starting path 1/16exchanging 84-134,77-0 with 84-77,134-0 => save=17.58671573136749 => cost: 475.57463524981165...exchanging 131-9,85-73 with 131-85,9-73 => save=0.22871091978264957 => cost: 415.7579280346732Starting path 2/16exchanging 114-10,93-0 with 114-93,10-0 => save=4.063910634805211 => cost: 494.31011527431946exchanging 114-105,10-82 with 114-10,105-82 => save=2.900319415888454 => cost: 491.409795858431Starting path 3/16exchanging 4-13,123-0 with 4-123,13-0 => save=19.13464498164811 => cost: 480.43951154085676exchanging 65-14,52-33 with 65-52,14-33 => save=1.587339787656024 => cost: 478.8521717532007Starting path 4/16exchanging 19-16,55-0 with 19-55,16-0 => save=0.7537655368855951 => cost: 489.3027111852691...exchanging 46-130,39-75 with 46-39,130-75 => save=1.7704562419552907 => cost: 455.4046302803684Starting path 5/16exchanging 37-112,94-0 with 37-94,112-0 => save=11.94699370814547 => cost: 485.7266708931122exchanging 58-110,28-107 with 58-28,110-107 => save=9.304516959883209 => cost: 476.422153933229Starting path 6/16exchanging 62-142,144-0 with 62-144,142-0 => save=23.25445433373693 => cost: 467.79298671639344Starting path 7/16Starting path 8/16exchanging 44-100,48-0 with 44-48,100-0 => save=1.0746379367560621 => cost: 486.7997677569661...exchanging 44-98,8-48 with 44-8,98-48 => save=6.17952602155286 => cost: 476.7280985588525Starting path 9/16Starting path 10/16exchanging 113-5,72-0 with 113-72,5-0 => save=1.4836706701319144 => cost: 484.8140111574118Starting path 11/16Starting path 12/16Starting path 13/16Starting path 14/16Starting path 15/16Starting path 16/16CPU times: user 2.16 s, sys: 40 ms, total: 2.2 sWall time: 2.24 s

Plot improved paths

forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Nice! Several clean paths, but some vans had to do only one distant delivery.

As we see, it is better to increase the max cost again.

In a real system, there should be a way to estimate it based on a full day of work (generally 8 hours).Let's try again.

%timeresult_paths=sequential_nn(800)
van 1: 792.9220639985251van 2: 793.9102149907774van 3: 749.3577273914822van 4: 769.1224381882283CPU times: user 1.03 s, sys: 18.5 ms, total: 1.05 sWall time: 1.05 s

Plot result paths

forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's improve them with a 2-opt heuristic

%timeimprove_result_paths()
Starting path 1/4exchanging 84-51,77-0 with 84-77,51-0 => save=11.952005586800027 => cost: 780.9700584117251...exchanging 66-51,147-126 with 66-147,51-126 => save=6.004072981449653 => cost: 732.2204189744828Starting path 2/4exchanging 35-69,29-0 with 35-29,69-0 => save=15.964056951390774 => cost: 777.9461580393865...exchanging 35-129,90-42 with 35-90,129-42 => save=14.532045586175727 => cost: 750.7675349695343Starting path 3/4exchanging 4-124,123-0 with 4-123,124-0 => save=19.23021437016152 => cost: 730.1275130213206...exchanging 65-14,52-33 with 65-52,14-33 => save=1.587339787656024 => cost: 701.2274950765787Starting path 4/4exchanging 19-21,54-0 with 19-54,21-0 => save=12.646642051225456 => cost: 756.4757961370028CPU times: user 3.71 s, sys: 44.8 ms, total: 3.75 sWall time: 3.81 s

Plot improved paths

forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],linewidth=2)plt.scatter(data.x,data.y,c='r',s=30)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Nice, clean paths. But with several overlaps.

In a real system, it should be analysed not only the cost (distance), but also the number of points in the route. Each one introduces a delay, as the driver has to park, walk, wait the customer, etc.

Scenario: farthest depot, several vans, KMeans clustering

Compute delivery clusters

fromsklearn.clusterimportKMeanswork_per_van=20#how many deliveries each van will start, ie granularity; a better approach would be capacity.model=KMeans(n_clusters=1+size//work_per_van,init='k-means++',random_state=0)%timemodel.fit(data[['x','y']])
CPU times: user 91.4 ms, sys: 10.6 ms, total: 102 msWall time: 111 msKMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,    n_clusters=8, n_init=10, n_jobs=1, precompute_distances='auto',    random_state=0, tol=0.0001, verbose=0)

Plot clusters

plt.scatter(data.x,data.y,marker='o',c=model.labels_,s=200)plt.scatter(model.cluster_centers_[:,0],model.cluster_centers_[:,1],marker='+',c='r',s=500)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's apply a greedy NN algorithm sequentially, on each of the clusters

%timeresult_paths=greedy_nn_clusters()
van 1: 390.25298877225174van 2: 687.7516428319581van 3: 382.7248611037869van 4: 505.82568809746317van 5: 478.6115152097322van 6: 644.9250908928291van 7: 595.3986361541317van 8: 665.6387979350595CPU times: user 154 ms, sys: 16.6 ms, total: 170 msWall time: 165 ms

Plot result paths

plt.scatter(data.x,data.y,marker='o',c=model.labels_,s=200)plt.scatter(model.cluster_centers_[:,0],model.cluster_centers_[:,1],marker='+',c='r',s=500)forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],'k-',linewidth=2)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Let's improve them with a 2-opt heuristic

%timeimprove_result_paths()
Starting path 1/8exchanging 77-4,83-29 with 77-83,4-29 => save=0.8194748732687884 => cost: 389.43351389898294...exchanging 6-143,128-89 with 6-128,143-89 => save=5.407506824441722 => cost: 324.93008877322006Starting path 2/8exchanging 30-5,34-0 with 30-34,5-0 => save=4.481243727960788 => cost: 683.2703991039973...exchanging 115-95,121-18 with 115-121,95-18 => save=8.322195150021315 => cost: 594.5702251203929Starting path 3/8exchanging 32-59,99-41 with 32-99,59-41 => save=4.114432829335271 => cost: 378.61042827445164exchanging 17-78,26-87 with 17-26,78-87 => save=0.17155006280330554 => cost: 378.43887821164833Starting path 4/8exchanging 42-19,129-106 with 42-129,19-106 => save=3.8728268587745447 => cost: 501.9528612386886...exchanging 37-94,54-0 with 37-54,94-0 => save=1.3747236340181104 => cost: 495.2855372216575Starting path 5/8exchanging 0-147,123-65 with 0-123,147-65 => save=11.990854640529989 => cost: 466.6206605692022...exchanging 135-65,123-0 with 135-123,65-0 => save=3.919229108305899 => cost: 435.93197839715947Starting path 6/8exchanging 56-21,15-0 with 56-15,21-0 => save=6.923848236857282 => cost: 638.0012426559717...exchanging 102-43,118-40 with 102-118,43-40 => save=15.652843812142969 => cost: 602.6373464143627Starting path 7/8exchanging 63-24,107-110 with 63-107,24-110 => save=11.657922849361384 => cost: 583.7407133047703...exchanging 112-36,91-125 with 112-91,36-125 => save=4.722248082275083 => cost: 559.3163570654888Starting path 8/8exchanging 48-111,98-137 with 48-98,111-137 => save=11.5162692633929 => cost: 654.1225286716666...exchanging 8-132,137-98 with 8-137,132-98 => save=7.784370993699362 => cost: 615.4951078634247CPU times: user 2.17 s, sys: 49.8 ms, total: 2.22 sWall time: 2.39 s

Plot improved paths

plt.scatter(data.x,data.y,marker='o',c=model.labels_,s=200)plt.scatter(model.cluster_centers_[:,0],model.cluster_centers_[:,1],marker='+',c='r',s=500)forpathinresult_paths:coords=get_coords(path)plt.plot(coords[0],coords[1],'k-',linewidth=2)plt.scatter(data.iloc[0].x,data.iloc[0].y,marker='*',c='r',s=400)plt.axis([-100,100,-100,100])plt.grid(True)

png

Very cool subsections! Aproximately the same number of deliveries in each route!

But some routes are way longer than others. Some vans maybe will have to do an extra hour or two.


That's it!

Thank you for getting this far, hope you liked! :)


[8]ページ先頭

©2009-2025 Movatter.jp