You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django实现TSP最近邻算法:设置原点出现异常点问题排查

问题描述

我正在开发基于最近邻法的旅行商问题(TSP)程序,采用Django框架。当前遇到的问题是:尝试将某一Coordinate对象设为计算原点时,程序反而添加了另一个无关点作为原点。试过多种设置方式、更换不同原点,也咨询过朋友,但均未解决。

相关代码如下:

calcRoute.py

from django.db import models
from .models import Coordinate, SortedPoint
import math

class Point:
  def __init__(self, longitude, latitude, isPassed, isOrigin):
    self.longitude = longitude
    self.latitude = latitude
    self.isPassed = isPassed
    self.isOrigin = isOrigin

def haversine(lat1, lon1, lat2, lon2): # haversine formula to calculate distance between points on a globe
    # convert degrees to radians
    lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])

    # calculate distance
    dlat = lat2 - lat1
    dlon = lon2 - lon1
    a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
    c = 2 * math.asin(math.sqrt(a))
    r = 6378  # radius of earth in kilometers
    return c * r

def nearest_neighbor(points, origin): # return the nearest neighbor
  nearestNeighbor = None
  nearestDistance = float('inf')
  
  for point in points:
    # skip the origin point
    if point == origin:
      continue
      
    distance = haversine(origin.longitude, origin.latitude, point.longitude, point.latitude)
    
    if distance < nearestDistance:
      nearestNeighbor = point
      nearestDistance = distance
      
  return nearestNeighbor

def save_points(points): # save points to db
  for point in points:
    sorted_point = SortedPoint(longitude=point.longitude, latitude=point.latitude, isPassed=point.isPassed)
    sorted_point.save()

def calc_route():
  SortedPoint.objects.all().delete()
  # load coordinates from db
  coordinates = Coordinate.objects.all()
 
  points = [Point(coordinate.longitude, coordinate.latitude, False, coordinate.isOrigin) for coordinate in coordinates]
  origin = points[0]
  for i in range(len(points)):
    if points[i].isOrigin is True:
      origin = points[i]
  ending = origin
  passedPoints = [origin]
  currentPoint = origin
  points.remove(origin)

  while len(points) > 0:
    nearestNeighbor = nearest_neighbor(points, currentPoint)
    passedPoints.append(nearestNeighbor)
    points.remove(nearestNeighbor)
    currentPoint = nearestNeighbor

  # append origin to the list of passed points
  passedPoints.append(ending)
  save_points(passedPoints) #save

views.py

def set_origin(request, id):
    point = SortedPoint.objects.get(id=id)
    point.isOrigin = True
    return redirect('editRoute')

@login_required(login_url='administration')
def update_coords(request):
    Coordinate.objects.all().delete()
    sorted_points = SortedPoint.objects.all()
    for sorted_point in sorted_points:
        coord = Coordinate(latitude=sorted_point.latitude, longitude=sorted_point.longitude, routeName=sorted_point.routeName, isPassed=sorted_point.isPassed, isOrigin=sorted_point.isOrigin)
        coord.save()
    
    calc_route()
    return redirect('editRoute')

models.py

from django.db import models
# Create your models here.

class Coordinate(models.Model):
    latitude = models.FloatField()
    longitude = models.FloatField()
    routeName = models.CharField(max_length=200, default='abc')
    isPassed = models.BooleanField(default=False)
    isOrigin = models.BooleanField(default=False)

class SortedPoint(models.Model):
  longitude = models.FloatField()
  latitude = models.FloatField()
  routeName = models.CharField(max_length=200, default='abc')
  isPassed = models.BooleanField()
  isOrigin = models.BooleanField(default=False)

urls.py(urlpatterns内)

path('edit-route/set_origin/<int:id>/', views.set_origin, name='set_origin'),
path('edit-route/update_coords/', views.update_coords, name='update_coords'),

问题分析与修复方案

1. 原点设置未持久化到数据库

set_origin视图中修改了SortedPoint的isOrigin属性,但未调用save()方法,导致修改仅停留在内存中,数据库内的isOrigin值并未更新。后续update_coords同步数据到Coordinate时,无法获取正确的原点标记。

修复代码:

def set_origin(request, id):
    # 清除所有点的原点标记,避免多个原点冲突
    SortedPoint.objects.update(isOrigin=False)
    point = SortedPoint.objects.get(id=id)
    point.isOrigin = True
    point.save()  # 将修改持久化到数据库
    return redirect('editRoute')

2. 保存路径时丢失原点标记

save_points函数创建SortedPoint对象时,未传入isOrigin字段,导致新保存的路径点全部默认isOrigin=False,引发数据不一致问题。

修复代码:

def save_points(points): # save points to db
  for point in points:
    sorted_point = SortedPoint(
        longitude=point.longitude, 
        latitude=point.latitude, 
        isPassed=point.isPassed,
        isOrigin=point.isOrigin  # 保存原点标记
    )
    sorted_point.save()

3. Haversine函数参数顺序错误

haversine函数定义的参数顺序为(lat1, lon1, lat2, lon2),但nearest_neighbor调用时传入的是(origin.longitude, origin.latitude, ...),经度与纬度顺序颠倒,导致距离计算完全错误,直接影响最近邻选择和路径生成。

修复代码:

def nearest_neighbor(points, current_point):
  nearestNeighbor = None
  nearestDistance = float('inf')
  
  for point in points:
    # 用坐标匹配替代对象引用比较,避免自定义类==判断失效
    if point.longitude == current_point.longitude and point.latitude == current_point.latitude:
      continue
      
    # 修正参数顺序:纬度在前,经度在后
    distance = haversine(current_point.latitude, current_point.longitude, point.latitude, point.longitude)
    
    if distance < nearestDistance:
      nearestNeighbor = point
      nearestDistance = distance
      
  return nearestNeighbor

4. 原点查找逻辑优化

原代码通过循环遍历查找原点,可改用next函数简化逻辑,同时确保无标记原点时默认取第一个点:

修复代码(calc_route函数内):

# 替换原有的origin赋值逻辑
origin = next((p for p in points if p.isOrigin), points[0])

内容的提问来源于stack exchange,提问作者Michael V

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 10:05:38