MATLAB实现共享电动车充电站选址优化问题

MATLAB实现共享电动车充电站选址优化问题

一、MATLAB解决方案

1.1 主程序入口

%% 共享电动车充电站选址优化问题求解
% 使用多目标遗传算法NSGA-II求解
clear; clc; close all;
fprintf('=== 共享电动车充电站选址优化问题求解 ===\n');

%% 1. 参数设置
% 问题规模
nCandidates = 20;      % 候选站点数量
nDemand = 50;          % 需求点数量
citySize = 20;         % 城市区域大小(km)

% 优化参数
budget = 500;          % 预算(万元)
nSelected = 5;         % 需选站点数
maxRadius = 3.0;       % 最大服务半径(km)
minDistance = 1.5;     % 最小站间距(km)

% 算法参数
popSize = 100;         % 种群大小
maxGen = 200;          % 最大代数
crossoverRate = 0.8;   % 交叉概率
mutationRate = 0.1;    % 变异概率

% 目标权重
weights = [0.4, 0.3, 0.3];  % [成本, 覆盖率, 平均距离]

%% 2. 生成模拟数据
fprintf('生成模拟数据...\n');
rng(42);  % 设置随机种子

% 2.1 候选站点位置
candidateLoc = citySize * rand(nCandidates, 2);

% 2.2 需求点位置和需求量
demandLoc = citySize * rand(nDemand, 2);
demandVal = 50 + 150 * rand(nDemand, 1);  % 50-200 kWh/天
totalDemand = sum(demandVal);

% 2.3 建站成本(与位置相关)
center = citySize/2;
distToCenter = sqrt(sum((candidateLoc - center).^2, 2));
maxDist = max(distToCenter);
costFactor = 1.5 - distToCenter/maxDist;  % 市中心成本高
baseCost = 80 + 40 * rand(nCandidates, 1);
constructionCost = baseCost .* costFactor;

% 2.4 电网容量
gridCapacity = 500 + 1000 * rand(nCandidates, 1);

% 2.5 计算距离矩阵
fprintf('计算距离矩阵...\n');
allPoints = [candidateLoc; demandLoc];
nTotal = size(allPoints, 1);
distMatrix = zeros(nTotal, nTotal);

% 并行计算距离矩阵
parfor i = 1:nTotal
    for j = 1:nTotal
        distMatrix(i, j) = norm(allPoints(i, :) - allPoints(j, :));
    end
end

% 提取子矩阵
stationToStationDist = distMatrix(1:nCandidates, 1:nCandidates);
stationToDemandDist = distMatrix(1:nCandidates, nCandidates+1:end);

%% 3. 定义目标函数
fprintf('定义目标函数...\n');

% 创建问题数据结构
problemData.nCandidates = nCandidates;
problemData.nDemand = nDemand;
problemData.constructionCost = constructionCost;
problemData.demandVal = demandVal;
problemData.totalDemand = totalDemand;
problemData.stationToStationDist = stationToStationDist;
problemData.stationToDemandDist = stationToDemandDist;
problemData.gridCapacity = gridCapacity;
problemData.budget = budget;
problemData.nSelected = nSelected;
problemData.maxRadius = maxRadius;
problemData.minDistance = minDistance;

% 目标函数
function objectives = chargingStationObjectives(x, data)
    % 计算充电站选址问题的目标函数
    % 输入: x - 二进制决策向量
    % 输出: objectives - [成本, 覆盖率, 平均距离]
    
    % 确保x是行向量
    x = x(:)';
    
    % 解码
    selectedIdx = find(x == 1);
    nSelected = length(selectedIdx);
    
    % 1. 检查约束
    penalty = 0;
    
    % 数量约束
    if nSelected ~= data.nSelected
        penalty = penalty + 1e6 * abs(nSelected - data.nSelected);
    end
    
    % 预算约束
    totalCost = sum(data.constructionCost(selectedIdx));
    if totalCost > data.budget
        penalty = penalty + 1e4 * (totalCost - data.budget);
    end
    
    % 站间距约束
    for i = 1:nSelected-1
        for j = i+1:nSelected
            idx1 = selectedIdx(i);
            idx2 = selectedIdx(j);
            if data.stationToStationDist(idx1, idx2) < data.minDistance
                penalty = penalty + 1e5;
            end
        end
    end
    
    % 如果违反约束,返回大惩罚值
    if penalty > 0
        objectives = [totalCost + penalty, 0, 1e6];
        return;
    end
    
    % 2. 计算目标值
    coveredDemand = 0;
    totalWeightedDist = 0;
    stationDemands = zeros(nSelected, 1);
    
    for d = 1:data.nDemand
        minDist = inf;
        bestStationIdx = 0;
        bestLocalIdx = 0;
        
        for s = 1:nSelected
            stationIdx = selectedIdx(s);
            dist = data.stationToDemandDist(stationIdx, d);
            
            if dist <= data.maxRadius && dist < minDist
                % 检查电网容量
                if stationDemands(s) + data.demandVal(d) <= data.gridCapacity(stationIdx)
                    minDist = dist;
                    bestStationIdx = stationIdx;
                    bestLocalIdx = s;
                end
            end
        end
        
        if bestStationIdx > 0
            demand = data.demandVal(d);
            coveredDemand = coveredDemand + demand;
            totalWeightedDist = totalWeightedDist + demand * minDist;
            stationDemands(bestLocalIdx) = stationDemands(bestLocalIdx) + demand;
        end
    end
    
    % 计算目标
    coverage = coveredDemand / data.totalDemand;
    
    if coveredDemand > 0
        avgDist = totalWeightedDist / coveredDemand;
    else
        avgDist = 1e6;
    end
    
    objectives = [totalCost, coverage, avgDist];
end

%% 4. NSGA-II算法实现
fprintf('实现NSGA-II算法...\n');

classdef NSGA2Solver
    % NSGA-II算法求解器
    properties
        problemData
        popSize
        maxGen
        pc
        pm
    end
    
    methods
        function obj = NSGA2Solver(data, popSize, maxGen, pc, pm)
            obj.problemData = data;
            obj.popSize = popSize;
            obj.maxGen = maxGen;
            obj.pc = pc;
            obj.pm = pm;
        end
        
        function [population, objectives] = run(obj)
            % 运行NSGA-II算法
            fprintf('初始化种群...\n');
            population = obj.initializePopulation();
            
            for gen = 1:obj.maxGen
                % 评估种群
                objectives = obj.evaluatePopulation(population);
                
                % 非支配排序
                fronts = obj.fastNonDominatedSort(objectives);
                
                % 计算拥挤距离
                crowdingDist = obj.calculateCrowdingDistance(objectives, fronts);
                
                % 选择
                parents = obj.tournamentSelection(population, objectives, fronts, crowdingDist);
                
                % 交叉和变异
                offspring = obj.generateOffspring(parents);
                
                % 合并
                combinedPop = [population; offspring];
                combinedObj = [objectives; obj.evaluatePopulation(offspring)];
                
                % 环境选择
                [population, objectives] = obj.environmentalSelection(...
                    combinedPop, combinedObj, obj.popSize);
                
                % 显示进度
                if mod(gen, 20) == 0
                    fprintf('Generation %d: Pareto front size = %d\n', ...
                        gen, length(fronts{1}));
                end
            end
        end
        
        function population = initializePopulation(obj)
            % 初始化种群
            population = zeros(obj.popSize, obj.problemData.nCandidates);
            
            for i = 1:obj.popSize
                % 随机选择nSelected个站点
                idx = randperm(obj.problemData.nCandidates, obj.problemData.nSelected);
                population(i, idx) = 1;
            end
        end
        
        function objectives = evaluatePopulation(obj, population)
            % 评估整个种群
            n = size(population, 1);
            objectives = zeros(n, 3);
            
            parfor i = 1:n
                objVec = chargingStationObjectives(population(i, :), obj.problemData);
                objectives(i, :) = objVec;
            end
        end
        
        function fronts = fastNonDominatedSort(~, objectives)
            % 快速非支配排序
            n = size(objectives, 1);
            S = cell(n, 1);
            nDom = zeros(n, 1);
            rank = zeros(n, 1);
            
            fronts = {};
            F1 = [];
            
            % 计算支配关系
            for i = 1:n
                S{i} = [];
                for j = 1:n
                    if i ~= j
                        % 判断i是否支配j
                        if all(objectives(i, :) <= objectives(j, :)) && ...
                           any(objectives(i, :) < objectives(j, :))
                            S{i} = [S{i}, j];
                        elseif all(objectives(j, :) <= objectives(i, :)) && ...
                               any(objectives(j, :) < objectives(i, :))
                            nDom(i) = nDom(i) + 1;
                        end
                    end
                end
                
                if nDom(i) == 0
                    rank(i) = 1;
                    F1 = [F1, i];
                end
            end
            
            fronts{1} = F1;
            i = 1;
            
            while ~isempty(fronts{i})
                Q = [];
                for p = fronts{i}
                    for q = S{p}
                        nDom(q) = nDom(q) - 1;
                        if nDom(q) == 0
                            rank(q) = i + 1;
                            Q = [Q, q];
                        end
                    end
                end
                i = i + 1;
                if ~isempty(Q)
                    fronts{i} = Q;
                else
                    break;
                end
            end
        end
        
        function distances = calculateCrowdingDistance(~, objectives, fronts)
            % 计算拥挤距离
            n = size(objectives, 1);
            distances = zeros(n, 1);
            
            for f = 1:length(fronts)
                front = fronts{f};
                m = length(front);
                
                if m <= 2
                    distances(front) = inf;
                    continue;
                end
                
                for objIdx = 1:size(objectives, 2)
                    [~, order] = sort(objectives(front, objIdx));
                    
                    % 边界个体距离设为inf
                    distances(front(order(1))) = inf;
                    distances(front(order(end))) = inf;
                    
                    fmin = objectives(front(order(1)), objIdx);
                    fmax = objectives(front(order(end)), objIdx);
                    
                    if fmax - fmin < eps
                        continue;
                    end
                    
                    for i = 2:m-1
                        idx = front(order(i));
                        nextIdx = front(order(i+1));
                        prevIdx = front(order(i-1));
                        
                        distances(idx) = distances(idx) + ...
                            (objectives(nextIdx, objIdx) - objectives(prevIdx, objIdx)) / ...
                            (fmax - fmin);
                    end
                end
            end
        end
        
        function parents = tournamentSelection(~, population, objectives, fronts, distances)
            % 锦标赛选择
            n = size(population, 1);
            parents = zeros(size(population));
            
            for i = 1:n
                % 随机选择两个个体
                idx1 = randi(n);
                idx2 = randi(n);
                
                % 比较前沿等级
                rank1 = find(cellfun(@(x) ismember(idx1, x), fronts), 1);
                rank2 = find(cellfun(@(x) ismember(idx2, x), fronts), 1);
                
                if rank1 < rank2
                    parents(i, :) = population(idx1, :);
                elseif rank1 > rank2
                    parents(i, :) = population(idx2, :);
                else
                    % 前沿相同,比较拥挤距离
                    if distances(idx1) > distances(idx2)
                        parents(i, :) = population(idx1, :);
                    else
                        parents(i, :) = population(idx2, :);
                    end
                end
            end
        end
        
        function offspring = generateOffspring(obj, parents)
            % 生成子代
            n = size(parents, 1);
            offspring = zeros(size(parents));
            
            for i = 1:2:n
                if i+1 <= n
                    p1 = parents(i, :);
                    p2 = parents(i+1, :);
                    
                    % 交叉
                    if rand() < obj.pc
                        [c1, c2] = obj.crossover(p1, p2);
                    else
                        c1 = p1;
                        c2 = p2;
                    end
                    
                    % 变异
                    c1 = obj.mutate(c1);
                    c2 = obj.mutate(c2);
                    
                    % 修复解
                    c1 = obj.repairSolution(c1);
                    c2 = obj.repairSolution(c2);
                    
                    offspring(i, :) = c1;
                    offspring(i+1, :) = c2;
                end
            end
        end
        
        function [c1, c2] = crossover(~, p1, p2)
            % 单点交叉
            n = length(p1);
            point = randi(n-1);
            
            c1 = [p1(1:point), p2(point+1:end)];
            c2 = [p2(1:point), p1(point+1:end)];
        end
        
        function mutated = mutate(obj, individual)
            % 位变异
            mutated = individual;
            n = length(individual);
            
            for i = 1:n
                if rand() < obj.pm
                    mutated(i) = 1 - mutated(i);
                end
            end
        end
        
        function repaired = repairSolution(obj, individual)
            % 修复解以满足数量约束
            nSelected = obj.problemData.nSelected;
            currentSelected = sum(individual);
            
            if currentSelected < nSelected
                % 需要增加站点
                zeroIdx = find(individual == 0);
                toAdd = nSelected - currentSelected;
                addIdx = zeroIdx(randperm(length(zeroIdx), toAdd));
                individual(addIdx) = 1;
                
            elseif currentSelected > nSelected
                % 需要减少站点
                oneIdx = find(individual == 1);
                toRemove = currentSelected - nSelected;
                removeIdx = oneIdx(randperm(length(oneIdx), toRemove));
                individual(removeIdx) = 0;
            end
            
            repaired = individual;
        end
        
        function [newPop, newObj] = environmentalSelection(obj, population, objectives, popSize)
            % 环境选择
            n = size(population, 1);
            
            % 非支配排序
            fronts = obj.fastNonDominatedSort(objectives);
            
            % 计算拥挤距离
            distances = obj.calculateCrowdingDistance(objectives, fronts);
            
            % 选择个体
            newPop = [];
            newObj = [];
            currentFront = 1;
            
            while length(newPop) + length(fronts{currentFront}) <= popSize
                frontIdx = fronts{currentFront};
                newPop = [newPop; population(frontIdx, :)];
                newObj = [newObj; objectives(frontIdx, :)];
                currentFront = currentFront + 1;
            end
            
            % 如果还需要个体,从下一个前沿按拥挤距离选择
            if length(newPop) < popSize
                remaining = popSize - length(newPop);
                frontIdx = fronts{currentFront};
                
                [~, order] = sort(distances(frontIdx), 'descend');
                selected = frontIdx(order(1:remaining));
                
                newPop = [newPop; population(selected, :)];
                newObj = [newObj; objectives(selected, :)];
            end
        end
    end
end

%% 5. 运行优化
fprintf('运行NSGA-II优化...\n');

% 创建求解器
solver = NSGA2Solver(problemData, popSize, maxGen, crossoverRate, mutationRate);

% 运行优化
[population, objectives] = solver.run();

%% 6. 结果分析
fprintf('分析优化结果...\n');

% 提取Pareto前沿
isPareto = true(size(objectives, 1), 1);
for i = 1:size(objectives, 1)
    for j = 1:size(objectives, 1)
        if i ~= j
            if all(objectives(j, :) <= objectives(i, :)) && ...
               any(objectives(j, :) < objectives(i, :))
                isPareto(i) = false;
                break;
            end
        end
    end
end

paretoSolutions = population(isPareto, :);
paretoObjectives = objectives(isPareto, :);

fprintf('Pareto前沿包含 %d 个解\n', sum(isPareto));

%% 7. 可视化结果
fprintf('可视化优化结果...\n');

% 7.1 Pareto前沿图
figure('Position', [100, 100, 1200, 400]);

% 子图1: 成本-覆盖率
subplot(1, 3, 1);
scatter(paretoObjectives(:, 1), paretoObjectives(:, 2), ...
    50, paretoObjectives(:, 3), 'filled');
colorbar;
xlabel('总成本 (万元)');
ylabel('服务覆盖率');
title('Pareto前沿: 成本 vs 覆盖率');
grid on;
colormap(jet);

% 子图2: 成本-平均距离
subplot(1, 3, 2);
scatter(paretoObjectives(:, 1), paretoObjectives(:, 3), ...
    50, paretoObjectives(:, 2), 'filled');
colorbar;
xlabel('总成本 (万元)');
ylabel('平均距离 (km)');
title('Pareto前沿: 成本 vs 平均距离');
grid on;
colormap(jet);

% 子图3: 覆盖率-平均距离
subplot(1, 3, 3);
scatter(paretoObjectives(:, 2), paretoObjectives(:, 3), ...
    50, paretoObjectives(:, 1), 'filled');
colorbar;
xlabel('服务覆盖率');
ylabel('平均距离 (km)');
title('Pareto前沿: 覆盖率 vs 平均距离');
grid on;
colormap(jet);

%% 8. 选择最佳折中解
fprintf('选择最佳折中解...\n');

% 使用TOPSIS方法
nPareto = size(paretoObjectives, 1);
decisionMatrix = paretoObjectives;

% 标准化决策矩阵
normalizedMatrix = zeros(size(decisionMatrix));
for i = 1:3
    if i == 2
        % 覆盖率最大化
        normalizedMatrix(:, i) = decisionMatrix(:, i) / max(decisionMatrix(:, i));
    else
        % 成本和距离最小化
        normalizedMatrix(:, i) = min(decisionMatrix(:, i)) ./ decisionMatrix(:, i);
    end
end

% 加权标准化
weightedMatrix = normalizedMatrix .* weights;

% 理想解和负理想解
idealSolution = max(weightedMatrix);
negativeIdealSolution = min(weightedMatrix);

% 计算距离
distToIdeal = sqrt(sum((weightedMatrix - idealSolution).^2, 2));
distToNegative = sqrt(sum((weightedMatrix - negativeIdealSolution).^2, 2));

% 相对接近度
relativeCloseness = distToNegative ./ (distToIdeal + distToNegative);

% 选择最佳解
[~, bestIdx] = max(relativeCloseness);
bestSolution = paretoSolutions(bestIdx, :);
bestObjectives = paretoObjectives(bestIdx, :);

fprintf('最佳解选择完成\n');
fprintf('总成本: %.1f 万元\n', bestObjectives(1));
fprintf('覆盖率: %.1f%%\n', bestObjectives(2)*100);
fprintf('平均距离: %.2f km\n', bestObjectives(3));

%% 9. 详细分析最佳解
fprintf('详细分析最佳解...\n');

selectedIdx = find(bestSolution == 1);
nSelected = length(selectedIdx);

% 计算详细指标
coveredDemand = 0;
totalWeightedDist = 0;
stationDemands = zeros(nSelected, 1);
demandAssignments = cell(nDemand, 1);

for d = 1:nDemand
    minDist = inf;
    bestStationIdx = 0;
    bestLocalIdx = 0;
    
    for s = 1:nSelected
        stationIdx = selectedIdx(s);
        dist = stationToDemandDist(stationIdx, d);
        
        if dist <= maxRadius && dist < minDist
            if stationDemands(s) + demandVal(d) <= gridCapacity(stationIdx)
                minDist = dist;
                bestStationIdx = stationIdx;
                bestLocalIdx = s;
            end
        end
    end
    
    if bestStationIdx > 0
        demand = demandVal(d);
        coveredDemand = coveredDemand + demand;
        totalWeightedDist = totalWeightedDist + demand * minDist;
        stationDemands(bestLocalIdx) = stationDemands(bestLocalIdx) + demand;
        
        demandAssignments{d} = struct(...
            'stationIdx', bestStationIdx, ...
            'distance', minDist, ...
            'isCovered', true);
    else
        demandAssignments{d} = struct(...
            'stationIdx', 0, ...
            'distance', inf, ...
            'isCovered', false);
    end
end

% 计算经济指标
annualRevenuePerKWH = 0.5;  % 元/kWh
annualOperatingCostPerStation = 10;  % 万元/年
totalCost = bestObjectives(1);
annualRevenue = coveredDemand * 365 * annualRevenuePerKWH / 10000;  % 万元
annualOperatingCost = nSelected * annualOperatingCostPerStation;
annualProfit = annualRevenue - annualOperatingCost;
paybackPeriod = totalCost / annualProfit;

fprintf('\n=== 详细分析结果 ===\n');
fprintf('选中站点: ');
fprintf('%d ', selectedIdx);
fprintf('\n');
fprintf('总成本: %.1f 万元\n', totalCost);
fprintf('预算: %.0f 万元\n', budget);
fprintf('剩余预算: %.1f 万元\n', budget - totalCost);
fprintf('覆盖率: %.1f%%\n', bestObjectives(2)*100);
fprintf('平均服务距离: %.2f km\n', bestObjectives(3));
fprintf('覆盖需求量: %.0f kWh/天\n', coveredDemand);
fprintf('年收入: %.1f 万元\n', annualRevenue);
fprintf('年运营成本: %.1f 万元\n', annualOperatingCost);
fprintf('年利润: %.1f 万元\n', annualProfit);
fprintf('投资回收期: %.1f 年\n', paybackPeriod);

%% 10. 可视化最佳方案
fprintf('可视化最佳方案...\n');

figure('Position', [100, 100, 1200, 500]);

% 子图1: 空间分布
subplot(1, 2, 1);
hold on;
grid on;

% 绘制需求点
colors = zeros(nDemand, 3);
sizes = zeros(nDemand, 1);
for d = 1:nDemand
    if demandAssignments{d}.isCovered
        % 被覆盖的需求点
        stationId = find(selectedIdx == demandAssignments{d}.stationIdx);
        colors(d, :) = hsv2rgb([stationId/nSelected, 1, 0.8]);
        sizes(d) = demandVal(d) / 5;
    else
        % 未被覆盖的需求点
        colors(d, :) = [0.7, 0.7, 0.7];
        sizes(d) = demandVal(d) / 10;
    end
end
scatter(demandLoc(:, 1), demandLoc(:, 2), sizes, colors, 'filled');

% 绘制候选站点
scatter(candidateLoc(:, 1), candidateLoc(:, 2), 50, 'k', 's', 'filled');

% 绘制选中的站点
selectedLoc = candidateLoc(selectedIdx, :);
scatter(selectedLoc(:, 1), selectedLoc(:, 2), 150, 'r', '^', 'filled');

% 绘制服务范围
for i = 1:nSelected
    center = selectedLoc(i, :);
    theta = linspace(0, 2*pi, 100);
    x = center(1) + maxRadius * cos(theta);
    y = center(2) + maxRadius * sin(theta);
    fill(x, y, 'r', 'FaceAlpha', 0.1, 'EdgeColor', 'r', 'EdgeAlpha', 0.3);
end

% 连接需求点和充电站
for d = 1:nDemand
    if demandAssignments{d}.isCovered
        stationIdx = demandAssignments{d}.stationIdx;
        stationPos = candidateLoc(stationIdx, :);
        demandPos = demandLoc(d, :);
        plot([stationPos(1), demandPos(1)], [stationPos(2), demandPos(2)], ...
            'k:', 'LineWidth', 0.5);
    end
end

xlabel('X坐标 (km)');
ylabel('Y坐标 (km)');
title('最佳选址方案空间分布');
axis equal;
xlim([0, citySize]);
ylim([0, citySize]);

% 子图2: 详细信息
subplot(1, 2, 2);
axis off;

% 创建信息文本
infoText = sprintf('=== 最佳选址方案详情 ===\n\n');
infoText = [infoText, sprintf('选中站点编号: ')];
for i = 1:length(selectedIdx)
    infoText = [infoText, sprintf('%d ', selectedIdx(i))];
end
infoText = [infoText, sprintf('\n\n')];

infoText = [infoText, sprintf('站点成本详情:\n')];
totalCost = 0;
for i = 1:length(selectedIdx)
    idx = selectedIdx(i);
    cost = constructionCost(idx);
    totalCost = totalCost + cost;
    capacity = gridCapacity(idx);
    demand = stationDemands(i);
    loadRatio = demand / capacity * 100;
    
    infoText = [infoText, sprintf('  站点%d: %.1f万元, 容量:%.0fkW, 负载:%.0fkW(%.1f%%)\n', ...
        idx, cost, capacity, demand, loadRatio)];
end
infoText = [infoText, sprintf('  总成本: %.1f万元\n\n', totalCost)];

infoText = [infoText, sprintf('性能指标:\n')];
infoText = [infoText, sprintf('  服务覆盖率: %.1f%%\n', bestObjectives(2)*100)];
infoText = [infoText, sprintf('  平均服务距离: %.2fkm\n', bestObjectives(3))];
infoText = [infoText, sprintf('  覆盖需求量: %.0f kWh/天\n\n', coveredDemand)];

infoText = [infoText, sprintf('经济指标:\n')];
infoText = [infoText, sprintf('  年收入: %.1f万元\n', annualRevenue)];
infoText = [infoText, sprintf('  年运营成本: %.1f万元\n', annualOperatingCost)];
infoText = [infoText, sprintf('  年利润: %.1f万元\n', annualProfit)];
infoText = [infoText, sprintf('  投资回收期: %.1f年\n', paybackPeriod)];

text(0.1, 0.5, infoText, 'FontSize', 10, 'VerticalAlignment', 'middle', ...
    'BackgroundColor', [0.95, 0.95, 0.95], 'EdgeColor', 'k');

title('方案详情');

%% 11. 敏感性分析
fprintf('进行敏感性分析...\n');

% 分析不同参数的影响
parameters = {'budget', 'maxRadius', 'nSelected'};
paramValues = {[300, 400, 500, 600, 700], ...
               [2.0, 2.5, 3.0, 3.5, 4.0], ...
               [3, 4, 5, 6, 7]};
paramNames = {'预算(万元)', '服务半径(km)', '需选站点数'};

results = cell(3, 1);

for p = 1:3
    fprintf('\n分析参数: %s\n', paramNames{p});
    
    paramName = parameters{p};
    values = paramValues{p};
    nValues = length(values);
    
    paramResults = zeros(nValues, 4);  % [参数值, 平均成本, 平均覆盖率, 平均距离]
    
    for v = 1:nValues
        % 修改参数
        switch paramName
            case 'budget'
                problemData.budget = values(v);
            case 'maxRadius'
                problemData.maxRadius = values(v);
            case 'nSelected'
                problemData.nSelected = values(v);
        end
        
        % 运行简化优化
        simpleSolver = NSGA2Solver(problemData, 50, 50, crossoverRate, mutationRate);
        [~, simpleObj] = simpleSolver.run();
        
        % 提取Pareto前沿
        isParetoSimple = true(size(simpleObj, 1), 1);
        for i = 1:size(simpleObj, 1)
            for j = 1:size(simpleObj, 1)
                if i ~= j
                    if all(simpleObj(j, :) <= simpleObj(i, :)) && ...
                       any(simpleObj(j, :) < simpleObj(i, :))
                        isParetoSimple(i) = false;
                        break;
                    end
                end
            end
        end
        
        paretoObjSimple = simpleObj(isParetoSimple, :);
        
        if ~isempty(paretoObjSimple)
            avgCost = mean(paretoObjSimple(:, 1));
            avgCoverage = mean(paretoObjSimple(:, 2));
            avgDistance = mean(paretoObjSimple(:, 3));
        else
            avgCost = NaN;
            avgCoverage = NaN;
            avgDistance = NaN;
        end
        
        paramResults(v, :) = [values(v), avgCost, avgCoverage, avgDistance];
        
        fprintf('  %s=%.1f: 成本=%.1f, 覆盖率=%.1f%%, 距离=%.2f\n', ...
            paramNames{p}, values(v), avgCost, avgCoverage*100, avgDistance);
    end
    
    results{p} = paramResults;
    
    % 恢复原始参数
    problemData.budget = budget;
    problemData.maxRadius = maxRadius;
    problemData.nSelected = nSelected;
end

% 可视化敏感性分析
figure('Position', [100, 100, 1200, 300]);

for p = 1:3
    subplot(1, 3, p);
    paramResults = results{p};
    
    yyaxis left;
    plot(paramResults(:, 1), paramResults(:, 2), 'b-o', 'LineWidth', 2);
    ylabel('平均成本 (万元)', 'Color', 'b');
    
    yyaxis right;
    plot(paramResults(:, 1), paramResults(:, 3)*100, 'r-s', 'LineWidth', 2);
    ylabel('平均覆盖率 (%)', 'Color', 'r');
    
    xlabel(paramNames{p});
    title(sprintf('%s敏感性分析', paramNames{p}));
    grid on;
    
    legend('平均成本', '平均覆盖率', 'Location', 'best');
end

%% 12. 多目标权重分析
fprintf('\n多目标权重分析...\n');

% 不同权重组合
weightCombinations = {
    [0.6, 0.2, 0.2],  % 成本优先
    [0.2, 0.6, 0.2],  % 覆盖率优先
    [0.2, 0.2, 0.6],  % 距离优先
    [0.4, 0.3, 0.3]   % 平衡
};
weightNames = {'成本优先', '覆盖率优先', '距离优先', '平衡方案'};

weightResults = cell(length(weightCombinations), 3);

for w = 1:length(weightCombinations)
    fprintf('分析权重组合: %s\n', weightNames{w});
    
    weights = weightCombinations{w};
    
    % 使用TOPSIS选择最佳解
    weightedMatrix = normalizedMatrix .* weights;
    idealSolution = max(weightedMatrix);
    negativeIdealSolution = min(weightedMatrix);
    
    distToIdeal = sqrt(sum((weightedMatrix - idealSolution).^2, 2));
    distToNegative = sqrt(sum((weightedMatrix - negativeIdealSolution).^2, 2));
    
    relativeCloseness = distToNegative ./ (distToIdeal + distToNegative);
    [~, bestIdx] = max(relativeCloseness);
    
    weightResults{w, 1} = weightNames{w};
    weightResults{w, 2} = paretoSolutions(bestIdx, :);
    weightResults{w, 3} = paretoObjectives(bestIdx, :);
    
    fprintf('  选中站点: ');
    selected = find(paretoSolutions(bestIdx, :) == 1);
    for i = 1:length(selected)
        fprintf('%d ', selected(i));
    end
    fprintf('\n');
    fprintf('  总成本: %.1f, 覆盖率: %.1f%%, 平均距离: %.2f\n', ...
        paretoObjectives(bestIdx, 1), paretoObjectives(bestIdx, 2)*100, ...
        paretoObjectives(bestIdx, 3));
end

% 可视化多权重对比
figure('Position', [100, 100, 800, 600]);

% 提取性能指标
costs = zeros(length(weightCombinations), 1);
coverages = zeros(length(weightCombinations), 1);
distances = zeros(length(weightCombinations), 1);

for w = 1:length(weightCombinations)
    costs(w) = weightResults{w, 3}(1);
    coverages(w) = weightResults{w, 3}(2) * 100;
    distances(w) = weightResults{w, 3}(3);
end

% 雷达图
subplot(2, 2, 1);
categories = {'成本', '覆盖率', '平均距离'};
angles = linspace(0, 2*pi, length(categories) + 1);
angles = angles(1:end-1);

% 标准化数据
normCosts = 1 - (costs - min(costs)) / (max(costs) - min(costs));
normCoverages = (coverages - min(coverages)) / (max(coverages) - min(coverages));
normDistances = 1 - (distances - min(distances)) / (max(distances) - min(distances));

for w = 1:length(weightCombinations)
    values = [normCosts(w), normCoverages(w), normDistances(w)];
    values = [values, values(1)];  % 闭合
    
    polarplot([angles, angles(1)], values, 'LineWidth', 2);
    hold on;
end

title('多权重方案对比');
legend(weightNames, 'Location', 'best');
rlim([0, 1]);

% 成本对比
subplot(2, 2, 2);
bar(costs);
ylabel('总成本 (万元)');
title('各方案成本对比');
set(gca, 'XTickLabel', weightNames);
grid on;

% 覆盖率对比
subplot(2, 2, 3);
bar(coverages);
ylabel('覆盖率 (%)');
title('各方案覆盖率对比');
set(gca, 'XTickLabel', weightNames);
grid on;

% 平均距离对比
subplot(2, 2, 4);
bar(distances);
ylabel('平均距离 (km)');
title('各方案平均距离对比');
set(gca, 'XTickLabel', weightNames);
grid on;

%% 13. 生成详细报告
fprintf('\n生成详细报告...\n');

% 创建结果表格
stationTable = table();
stationTable.站点编号 = selectedIdx';
stationTable.X坐标 = candidateLoc(selectedIdx, 1);
stationTable.Y坐标 = candidateLoc(selectedIdx, 2);
stationTable.建站成本_万元 = constructionCost(selectedIdx);
stationTable.电网容量_kW = gridCapacity(selectedIdx);
stationTable.服务需求_kW = stationDemands;
stationTable.负载率 = stationDemands ./ gridCapacity(selectedIdx) * 100;

% 需求点分配表
demandAssignmentTable = table();
coveredCount = 0;
for d = 1:nDemand
    if demandAssignments{d}.isCovered
        coveredCount = coveredCount + 1;
        demandAssignmentTable.需求点编号(coveredCount) = d;
        demandAssignmentTable.X坐标(coveredCount) = demandLoc(d, 1);
        demandAssignmentTable.Y坐标(coveredCount) = demandLoc(d, 2);
        demandAssignmentTable.日需求量_kWh(coveredCount) = demandVal(d);
        demandAssignmentTable.服务站点(coveredCount) = demandAssignments{d}.stationIdx;
        demandAssignmentTable.距离_km(coveredCount) = demandAssignments{d}.distance;
    end
end

% Pareto前沿表
paretoTable = table();
paretoTable.总成本_万元 = paretoObjectives(:, 1);
paretoTable.覆盖率 = paretoObjectives(:, 2);
paretoTable.平均距离_km = paretoObjectives(:, 3);

% 显示表格
fprintf('\n选中站点详情:\n');
disp(stationTable);

fprintf('\n需求点分配详情(前10个):\n');
if ~isempty(demandAssignmentTable)
    disp(demandAssignmentTable(1:min(10, height(demandAssignmentTable)), :));
end

fprintf('\nPareto前沿(前5个):\n');
disp(paretoTable(1:min(5, height(paretoTable)), :));

%% 14. 保存结果
fprintf('\n保存优化结果...\n');

% 保存工作空间变量
save('charging_station_results.mat', ...
    'candidateLoc', 'demandLoc', 'demandVal', ...
    'constructionCost', 'gridCapacity', ...
    'bestSolution', 'bestObjectives', ...
    'stationTable', 'demandAssignmentTable', 'paretoTable');

% 保存为CSV文件
writetable(stationTable, 'selected_stations.csv');
writetable(demandAssignmentTable, 'demand_assignments.csv');
writetable(paretoTable, 'pareto_front.csv');

fprintf('\n=== 优化完成 ===\n');
fprintf('结果已保存到文件:\n');
fprintf('  selected_stations.csv - 选中站点信息\n');
fprintf('  demand_assignments.csv - 需求点分配信息\n');
fprintf('  pareto_front.csv - Pareto前沿信息\n');
fprintf('  charging_station_results.mat - 完整MATLAB数据\n');

二、改进的优化算法

2.1 混合优化算法

%% 混合遗传-模拟退火算法
classdef HybridGASA
    % 混合遗传算法和模拟退火算法
    properties
        problemData
        gaPopSize
        saIterations
        initialTemp
        coolingRate
    end
    
    methods
        function obj = HybridGASA(data, gaPopSize, saIterations, initialTemp, coolingRate)
            obj.problemData = data;
            obj.gaPopSize = gaPopSize;
            obj.saIterations = saIterations;
            obj.initialTemp = initialTemp;
            obj.coolingRate = coolingRate;
        end
        
        function [bestSolution, bestObjectives] = run(obj)
            % 运行混合算法
            fprintf('运行混合遗传-模拟退火算法...\n');
            
            % 1. 遗传算法生成初始解
            gaSolver = NSGA2Solver(obj.problemData, obj.gaPopSize, 50, 0.8, 0.1);
            [population, objectives] = gaSolver.run();
            
            % 2. 从Pareto前沿选择多个解进行模拟退火优化
            paretoIdx = getParetoFront(objectives);
            paretoSolutions = population(paretoIdx, :);
            paretoObjectives = objectives(paretoIdx, :);
            
            % 3. 对每个Pareto解进行模拟退火优化
            improvedSolutions = cell(size(paretoSolutions, 1), 1);
            improvedObjectives = zeros(size(paretoObjectives));
            
            parfor i = 1:size(paretoSolutions, 1)
                [improvedSolutions{i}, improvedObjectives(i, :)] = ...
                    obj.simulatedAnnealing(paretoSolutions(i, :), paretoObjectives(i, :));
            end
            
            % 4. 合并结果并选择最优
            allSolutions = [population; cell2mat(improvedSolutions)];
            allObjectives = [objectives; improvedObjectives];
            
            % 找到Pareto前沿
            paretoIdxFinal = getParetoFront(allObjectives);
            paretoSolutionsFinal = allSolutions(paretoIdxFinal, :);
            paretoObjectivesFinal = allObjectives(paretoIdxFinal, :);
            
            % 使用TOPSIS选择最佳解
            [bestSolution, bestObjectives] = obj.selectBestSolution(...
                paretoSolutionsFinal, paretoObjectivesFinal);
        end
        
        function [newSolution, newObjectives] = simulatedAnnealing(obj, initSolution, initObjectives)
            % 模拟退火优化
            currentSolution = initSolution;
            currentObjectives = initObjectives;
            bestSolution = currentSolution;
            bestObjectives = currentObjectives;
            
            temperature = obj.initialTemp;
            
            for iter = 1:obj.saIterations
                % 生成邻域解
                neighbor = obj.generateNeighbor(currentSolution);
                neighborObjectives = chargingStationObjectives(neighbor, obj.problemData);
                
                % 计算目标改善
                delta = sum(neighborObjectives) - sum(currentObjectives);
                
                % 接受准则
                if delta < 0 || rand() < exp(-delta / temperature)
                    currentSolution = neighbor;
                    currentObjectives = neighborObjectives;
                    
                    % 更新最优解
                    if sum(currentObjectives) < sum(bestObjectives)
                        bestSolution = currentSolution;
                        bestObjectives = currentObjectives;
                    end
                end
                
                % 降温
                temperature = temperature * obj.coolingRate;
            end
        end
        
        function neighbor = generateNeighbor(~, solution)
            % 生成邻域解
            neighbor = solution;
            n = length(solution);
            
            % 随机交换两个站点的选择状态
            idx1 = randi(n);
            idx2 = randi(n);
            
            while idx1 == idx2
                idx2 = randi(n);
            end
            
            neighbor(idx1) = 1 - neighbor(idx1);
            neighbor(idx2) = 1 - neighbor(idx2);
            
            % 修复解
            nSelected = sum(neighbor);
            nRequired = 5;  % 这里需要从problemData获取
            
            if nSelected < nRequired
                zeroIdx = find(neighbor == 0);
                toAdd = nRequired - nSelected;
                addIdx = zeroIdx(randperm(length(zeroIdx), toAdd));
                neighbor(addIdx) = 1;
            elseif nSelected > nRequired
                oneIdx = find(neighbor == 1);
                toRemove = nSelected - nRequired;
                removeIdx = oneIdx(randperm(length(oneIdx), toRemove));
                neighbor(removeIdx) = 0;
            end
        end
        
        function [bestSolution, bestObjectives] = selectBestSolution(~, solutions, objectives)
            % TOPSIS选择最佳解
            n = size(objectives, 1);
            
            % 标准化
            normalized = zeros(size(objectives));
            for i = 1:3
                if i == 2
                    normalized(:, i) = objectives(:, i) / max(objectives(:, i));
                else
                    normalized(:, i) = min(objectives(:, i)) ./ objectives(:, i);
                end
            end
            
            % 权重
            weights = [0.4, 0.3, 0.3];
            weighted = normalized .* weights;
            
            % 理想解
            ideal = max(weighted);
            negativeIdeal = min(weighted);
            
            % 距离
            distToIdeal = sqrt(sum((weighted - ideal).^2, 2));
            distToNegative = sqrt(sum((weighted - negativeIdeal).^2, 2));
            
            % 相对接近度
            closeness = distToNegative ./ (distToIdeal + distToNegative);
            
            [~, bestIdx] = max(closeness);
            bestSolution = solutions(bestIdx, :);
            bestObjectives = objectives(bestIdx, :);
        end
    end
end

%% 辅助函数
function paretoIdx = getParetoFront(objectives)
    % 获取Pareto前沿索引
    n = size(objectives, 1);
    isPareto = true(n, 1);
    
    for i = 1:n
        for j = 1:n
            if i ~= j
                if all(objectives(j, :) <= objectives(i, :)) && ...
                   any(objectives(j, :) < objectives(i, :))
                    isPareto(i) = false;
                    break;
                end
            end
        end
    end
    
    paretoIdx = find(isPareto);
end

三、GUI决策支持系统

3.1 MATLAB App Designer界面

%% 充电站选址决策支持系统App
classdef ChargingStationDSSApp < matlab.apps.AppBase
    % 属性定义
    properties (Access = public)
        UIFigure               matlab.ui.Figure
        TabGroup              matlab.ui.container.TabGroup
        DataTab               matlab.ui.container.Tab
        OptimizationTab       matlab.ui.container.Tab
        ResultsTab           matlab.ui.container.Tab
        
        % 数据参数控件
        nCandidatesEdit       matlab.ui.control.NumericEditField
        nDemandEdit          matlab.ui.control.NumericEditField
        citySizeEdit         matlab.ui.control.NumericEditField
        generateDataButton   matlab.ui.control.Button
        
        % 优化参数控件
        budgetEdit           matlab.ui.control.NumericEditField
        nSelectedEdit        matlab.ui.control.NumericEditField
        maxRadiusEdit        matlab.ui.control.NumericEditField
        minDistanceEdit      matlab.ui.control.NumericEditField
        runOptimizationButton matlab.ui.control.Button
        
        % 算法参数控件
        popSizeEdit          matlab.ui.control.NumericEditField
        maxGenEdit           matlab.ui.control.NumericEditField
        crossoverRateEdit    matlab.ui.control.NumericEditField
        mutationRateEdit     matlab.ui.control.NumericEditField
        
        % 结果显示控件
        resultAxes           matlab.ui.control.UIAxes
        solutionText         matlab.ui.control.TextArea
        statusLabel          matlab.ui.control.Label
        
        % 数据存储
        problemData          struct
        optimizationResults  struct
    end
    
    % 方法定义
    methods (Access = private)
        % 生成数据
        function generateData(app)
            app.statusLabel.Text = '正在生成数据...';
            drawnow;
            
            % 获取参数
            nCandidates = app.nCandidatesEdit.Value;
            nDemand = app.nDemandEdit.Value;
            citySize = app.citySizeEdit.Value;
            
            % 生成数据
            rng(42);
            candidateLoc = citySize * rand(nCandidates, 2);
            demandLoc = citySize * rand(nDemand, 2);
            demandVal = 50 + 150 * rand(nDemand, 1);
            
            % 计算距离矩阵
            allPoints = [candidateLoc; demandLoc];
            nTotal = size(allPoints, 1);
            distMatrix = zeros(nTotal, nTotal);
            
            for i = 1:nTotal
                for j = 1:nTotal
                    distMatrix(i, j) = norm(allPoints(i, :) - allPoints(j, :));
                end
            end
            
            % 存储数据
            app.problemData.candidateLoc = candidateLoc;
            app.problemData.demandLoc = demandLoc;
            app.problemData.demandVal = demandVal;
            app.problemData.totalDemand = sum(demandVal);
            app.problemData.distMatrix = distMatrix;
            app.problemData.stationToStationDist = distMatrix(1:nCandidates, 1:nCandidates);
            app.problemData.stationToDemandDist = distMatrix(1:nCandidates, nCandidates+1:end);
            
            % 生成成本和容量
            center = citySize/2;
            distToCenter = sqrt(sum((candidateLoc - center).^2, 2));
            maxDist = max(distToCenter);
            costFactor = 1.5 - distToCenter/maxDist;
            baseCost = 80 + 40 * rand(nCandidates, 1);
            constructionCost = baseCost .* costFactor;
            gridCapacity = 500 + 1000 * rand(nCandidates, 1);
            
            app.problemData.constructionCost = constructionCost;
            app.problemData.gridCapacity = gridCapacity;
            
            % 可视化数据
            cla(app.resultAxes);
            hold(app.resultAxes, 'on');
            
            % 绘制需求点
            scatter(app.resultAxes, demandLoc(:, 1), demandLoc(:, 2), ...
                demandVal/5, 'b', 'filled', 'DisplayName', '需求点');
            
            % 绘制候选站点
            scatter(app.resultAxes, candidateLoc(:, 1), candidateLoc(:, 2), ...
                50, 'k', 's', 'filled', 'DisplayName', '候选站点');
            
            xlabel(app.resultAxes, 'X坐标 (km)');
            ylabel(app.resultAxes, 'Y坐标 (km)');
            title(app.resultAxes, '城市布局图');
            legend(app.resultAxes, 'show');
            grid(app.resultAxes, 'on');
            axis(app.resultAxes, 'equal');
            
            app.statusLabel.Text = '数据生成完成';
        end
        
        % 运行优化
        function runOptimization(app)
            app.statusLabel.Text = '正在运行优化...';
            drawnow;
            
            % 获取参数
            budget = app.budgetEdit.Value;
            nSelected = app.nSelectedEdit.Value;
            maxRadius = app.maxRadiusEdit.Value;
            minDistance = app.minDistanceEdit.Value;
            popSize = app.popSizeEdit.Value;
            maxGen = app.maxGenEdit.Value;
            crossoverRate = app.crossoverRateEdit.Value;
            mutationRate = app.mutationRateEdit.Value;
            
            % 更新问题数据
            app.problemData.budget = budget;
            app.problemData.nSelected = nSelected;
            app.problemData.maxRadius = maxRadius;
            app.problemData.minDistance = minDistance;
            
            % 创建求解器
            solver = NSGA2Solver(app.problemData, popSize, maxGen, crossoverRate, mutationRate);
            
            % 运行优化
            [population, objectives] = solver.run();
            
            % 提取Pareto前沿
            isPareto = true(size(objectives, 1), 1);
            for i = 1:size(objectives, 1)
                for j = 1:size(objectives, 1)
                    if i ~= j
                        if all(objectives(j, :) <= objectives(i, :)) && ...
                           any(objectives(j, :) < objectives(i, :))
                            isPareto(i) = false;
                            break;
                        end
                    end
                end
            end
            
            paretoSolutions = population(isPareto, :);
            paretoObjectives = objectives(isPareto, :);
            
            % 保存结果
            app.optimizationResults.paretoSolutions = paretoSolutions;
            app.optimizationResults.paretoObjectives = paretoObjectives;
            
            % 选择最佳解
            [bestSolution, bestObjectives] = selectBestSolution(paretoSolutions, paretoObjectives);
            app.optimizationResults.bestSolution = bestSolution;
            app.optimizationResults.bestObjectives = bestObjectives;
            
            % 显示结果
            app.displayResults();
            app.statusLabel.Text = '优化完成';
        end
        
        % 显示结果
        function displayResults(app)
            if ~isempty(app.optimizationResults)
                % 显示Pareto前沿
                cla(app.resultAxes);
                paretoObjectives = app.optimizationResults.paretoObjectives;
                
                scatter(app.resultAxes, paretoObjectives(:, 1), paretoObjectives(:, 2), ...
                    50, paretoObjectives(:, 3), 'filled');
                colorbar(app.resultAxes);
                xlabel(app.resultAxes, '总成本 (万元)');
                ylabel(app.resultAxes, '服务覆盖率');
                title(app.resultAxes, 'Pareto前沿');
                grid(app.resultAxes, 'on');
                
                % 显示最佳解信息
                bestSolution = app.optimizationResults.bestSolution;
                bestObjectives = app.optimizationResults.bestObjectives;
                
                infoText = sprintf('最佳选址方案:\n\n');
                selectedIdx = find(bestSolution == 1);
                
                infoText = [infoText, sprintf('选中站点: ')];
                for i = 1:length(selectedIdx)
                    infoText = [infoText, sprintf('%d ', selectedIdx(i))];
                end
                infoText = [infoText, sprintf('\n\n')];
                
                infoText = [infoText, sprintf('总成本: %.1f 万元\n', bestObjectives(1))];
                infoText = [infoText, sprintf('覆盖率: %.1f%%\n', bestObjectives(2)*100)];
                infoText = [infoText, sprintf('平均距离: %.2f km\n', bestObjectives(3))];
                
                app.solutionText.Value = infoText;
            end
        end
    end
    
    % 创建组件
    methods (Access = private)
        function createComponents(app)
            % 创建主窗口
            app.UIFigure = uifigure('Visible', 'off');
            app.UIFigure.Position = [100, 100, 1000, 700];
            app.UIFigure.Name = '充电站选址决策支持系统';
            
            % 创建标签页
            app.TabGroup = uitabgroup(app.UIFigure);
            app.TabGroup.Position = [10, 10, 980, 680];
            
            % 数据标签页
            app.DataTab = uitab(app.TabGroup, 'Title', '数据设置');
            createDataTab(app);
            
            % 优化标签页
            app.OptimizationTab = uitab(app.TabGroup, 'Title', '优化设置');
            createOptimizationTab(app);
            
            % 结果标签页
            app.ResultsTab = uitab(app.TabGroup, 'Title', '结果展示');
            createResultsTab(app);
            
            % 状态标签
            app.statusLabel = uilabel(app.UIFigure);
            app.statusLabel.Position = [10, 690, 980, 20];
            app.statusLabel.Text = '就绪';
            
            app.UIFigure.Visible = 'on';
        end
        
        function createDataTab(app)
            % 创建数据标签页内容
            uilabel(app.DataTab, 'Position', [20, 650, 200, 20], ...
                'Text', '候选站点数量:');
            app.nCandidatesEdit = uieditfield(app.DataTab, 'numeric', ...
                'Position', [220, 650, 100, 20], 'Value', 20);
            
            uilabel(app.DataTab, 'Position', [20, 620, 200, 20], ...
                'Text', '需求点数量:');
            app.nDemandEdit = uieditfield(app.DataTab, 'numeric', ...
                'Position', [220, 620, 100, 20], 'Value', 50);
            
            uilabel(app.DataTab, 'Position', [20, 590, 200, 20], ...
                'Text', '城市区域大小(km):');
            app.citySizeEdit = uieditfield(app.DataTab, 'numeric', ...
                'Position', [220, 590, 100, 20], 'Value', 20);
            
            app.generateDataButton = uibutton(app.DataTab, 'push', ...
                'Position', [20, 550, 200, 30], ...
                'Text', '生成数据', ...
                'ButtonPushedFcn', @(btn,event) generateData(app));
        end
        
        function createOptimizationTab(app)
            % 创建优化标签页内容
            uilabel(app.OptimizationTab, 'Position', [20, 650, 200, 20], ...
                'Text', '预算(万元):');
            app.budgetEdit = uieditfield(app.OptimizationTab, 'numeric', ...
                'Position', [220, 650, 100, 20], 'Value', 500);
            
            uilabel(app.OptimizationTab, 'Position', [20, 620, 200, 20], ...
                'Text', '需选站点数:');
            app.nSelectedEdit = uieditfield(app.OptimizationTab, 'numeric', ...
                'Position', [220, 620, 100, 20], 'Value', 5);
            
            uilabel(app.OptimizationTab, 'Position', [20, 590, 200, 20], ...
                'Text', '最大服务半径(km):');
            app.maxRadiusEdit = uieditfield(app.OptimizationTab, 'numeric', ...
                'Position', [220, 590, 100, 20], 'Value', 3.0);
            
            uilabel(app.OptimizationTab, 'Position', [20, 560, 200, 20], ...
                'Text', '最小站间距(km):');
            app.minDistanceEdit = uieditfield(app.OptimizationTab, 'numeric', ...
                'Position', [220, 560, 100, 20], 'Value', 1.5);
            
            uilabel(app.OptimizationTab, 'Position', [20, 530, 200, 20], ...
                'Text', '种群大小:');
            app.popSizeEdit = uieditfield(app.OptimizationTab, 'numeric', ...
                'Position', [220, 530, 100, 20], 'Value', 100);
            
            uilabel(app.OptimizationTab, 'Position', [20, 500, 200, 20], ...
                'Text', '最大代数:');
            app.maxGenEdit = uieditfield(app.OptimizationTab, 'numeric', ...
                'Position', [220, 500, 100, 20], 'Value', 200);
            
            uilabel(app.OptimizationTab, 'Position', [20, 470, 200, 20], ...
                'Text', '交叉概率:');
            app.crossoverRateEdit = uieditfield(app.OptimizationTab, 'numeric', ...
                'Position', [220, 470, 100, 20], 'Value', 0.8);
            
            uilabel(app.OptimizationTab, 'Position', [20, 440, 200, 20], ...
                'Text', '变异概率:');
            app.mutationRateEdit = uieditfield(app.OptimizationTab, 'numeric', ...
                'Position', [220, 440, 100, 20], 'Value', 0.1);
            
            app.runOptimizationButton = uibutton(app.OptimizationTab, 'push', ...
                'Position', [20, 400, 200, 30], ...
                'Text', '运行优化', ...
                'ButtonPushedFcn', @(btn,event) runOptimization(app));
        end
        
        function createResultsTab(app)
            % 创建结果标签页内容
            app.resultAxes = uiaxes(app.ResultsTab);
            app.resultAxes.Position = [50, 50, 500, 600];
            
            app.solutionText = uitextarea(app.ResultsTab);
            app.solutionText.Position = [600, 50, 350, 600];
            app.solutionText.Value = '优化结果将显示在这里...';
        end
    end
    
    % 构造方法
    methods
        function app = ChargingStationDSSApp
            createComponents(app);
        end
    end
end

参考代码 共享电动车充电站选址优化问题实例问题求解 www.youwenfan.com/contentcnu/59638.html

四、使用说明

4.1 运行步骤

  1. 数据生成:运行主程序生成模拟数据
  2. 参数设置:调整优化参数
  3. 运行优化:执行NSGA-II算法
  4. 结果分析:查看Pareto前沿和最佳方案
  5. 敏感性分析:分析参数影响
  6. 结果导出:保存结果到文件

4.2 主要输出

  1. Pareto前沿:包含多个非支配解
  2. 最佳选址方案:最优折中解
  3. 敏感性分析:关键参数影响
  4. 详细报告:站点信息、需求分配、经济指标

4.3 扩展功能

  1. 多种算法:支持NSGA-II、GA、SA等算法
  2. GUI界面:图形化操作界面
  3. 并行计算:加速大规模问题求解
  4. 数据导入:支持真实数据导入
  5. 场景分析:多种场景对比

专注于matlab/simulink,电子电路,编程