682 lines
34 KiB
C#
682 lines
34 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Configuration;
|
||
using System.Data;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Forms;
|
||
using SuperMap.RealEstate.ServiceModel;
|
||
using HWSB = SuperMap.RealEstate.HighWay.Storage.Business;
|
||
using Google.OrTools.LinearSolver; // 引用 OR-Tools 库
|
||
using Microsoft.ML.Data;
|
||
using Microsoft.ML;
|
||
using Transmission.SDK;
|
||
using HZQR.Common;
|
||
|
||
namespace DataBaseProcessing
|
||
{
|
||
public partial class TestForm : Form
|
||
{
|
||
public TestForm()
|
||
{
|
||
InitializeComponent();
|
||
}
|
||
|
||
#region 第一版单车价值估算方法
|
||
public class RevenueData
|
||
{
|
||
public float TotalRevenue { get; set; } // 服务区总营收
|
||
public Dictionary<string, float> ProvinceVehicleCounts { get; set; } // 每个省份的车辆数
|
||
}
|
||
|
||
public class VehicleValuePrediction
|
||
{
|
||
[ColumnName("Score")]
|
||
public float EstimatedVehicleValue { get; set; } // 估算每辆车的价值
|
||
}
|
||
|
||
public class RevenueInput
|
||
{
|
||
public Dictionary<string, float> ProvinceVehicleCounts { get; set; } = new Dictionary<string, float>(); // 动态省份车辆数据
|
||
public float TotalRevenue { get; set; } // 总营收
|
||
}
|
||
|
||
static async Task<RevenueData> GetRevenueDataFromApiAsync(DateTime date, string serviceAreaId)
|
||
{
|
||
Transaction transaction = new Transaction();
|
||
|
||
string SQLString = string.Format(@"SELECT
|
||
SUM(REVENUE_AMOUNT)
|
||
FROM
|
||
PLATFORM_DASHBOARD.T_REVENUEMONTHLY
|
||
WHERE
|
||
SERVERPART_ID = {0} AND STATISTICS_MONTH = {1}",
|
||
serviceAreaId, date.ToString("yyyyMM"));
|
||
DataTable dtRevenue = new HWSB.SERVERPART(transaction).ExecuteDataTable(SQLString);
|
||
|
||
SQLString = string.Format(@"SELECT * FROM HIGHWAY_SELLDATA.T_BAYONETPROVINCEMONTH_AH
|
||
WHERE SERVERPART_ID = {0} AND STATISTICS_MONTH = {1} AND SERVERPART_REGION IS NULL AND VEHICLE_TYPE IS NULL",
|
||
serviceAreaId, date.ToString("yyyyMM"));
|
||
DataTable dtBayonet = new HWSB.SERVERPART(transaction).ExecuteDataTable(SQLString);
|
||
|
||
var result = new RevenueData();
|
||
result.TotalRevenue = float.Parse(dtRevenue.Rows[0][0].TryParseToDecimal() + "");
|
||
result.ProvinceVehicleCounts = new Dictionary<string, float>();
|
||
|
||
foreach (DataRow drBayonet in dtBayonet.Select("", "VEHICLE_COUNT desc"))
|
||
{
|
||
result.ProvinceVehicleCounts.Add(drBayonet["PROVINCE_NAME"] + "",
|
||
float.Parse(drBayonet["VEHICLE_COUNT"].TryParseToDecimal() + ""));
|
||
}
|
||
|
||
transaction.Release();
|
||
transaction.Dispose();
|
||
|
||
return result;
|
||
}
|
||
|
||
static Dictionary<string, float> provinceSingleVehicleValues = new Dictionary<string, float>();
|
||
|
||
static void TrainAndPredict(List<RevenueInput> revenueInputs)
|
||
{
|
||
var mlContext = new MLContext();
|
||
|
||
// 动态获取所有省份名称并清理名称
|
||
//var provinceNames = revenueInputs.SelectMany(r => r.ProvinceVehicleCounts.Keys).Distinct().Select(p => p.Trim()).ToList();
|
||
var provinceNames = new List<string> { "北京市", "天津市", "上海市", "重庆市", "河北省", "河南省", "云南省", "辽宁省", "黑龙江省",
|
||
"湖南省", "安徽省", "山东省", "新疆维吾尔自治区", "江苏省", "浙江省", "江西省", "湖北省", "广西壮族自治区",
|
||
"甘肃省", "山西省", "内蒙古自治区", "陕西省", "吉林省", "福建省", "贵州省", "广东省", "青海省", "西藏自治区",
|
||
"四川省", "宁夏回族自治区", "海南省" };
|
||
int maxFeatureLength = provinceNames.Count; // 确定特征向量的最大长度
|
||
LogUtil.WriteLog("动态获取的省份列名:" + string.Join(", ", provinceNames));
|
||
LogUtil.WriteLog($"特征向量的最大长度:{maxFeatureLength}");
|
||
|
||
// 手动构建特征数组,确保每个输入数据的特征长度一致
|
||
var list = revenueInputs.Select(r =>
|
||
{
|
||
// 构建特征数组,填充缺失的省份数据为 0
|
||
var featureArray = provinceNames.Select(p => r.ProvinceVehicleCounts.ContainsKey(p) ? r.ProvinceVehicleCounts[p] : 0).ToList();
|
||
|
||
// 确保所有特征向量长度一致,如果不够最大长度,用0填充
|
||
while (featureArray.Count < maxFeatureLength)
|
||
{
|
||
featureArray.Add(0);
|
||
}
|
||
|
||
return new ModelInput(maxFeatureLength)
|
||
{
|
||
Features = featureArray.ToArray(), // 转换为固定长度数组
|
||
TotalRevenue = r.TotalRevenue
|
||
};
|
||
}).ToList();
|
||
|
||
|
||
var data = mlContext.Data.LoadFromEnumerable(list);
|
||
|
||
// 输出数据预览
|
||
var preview = data.Preview();
|
||
LogUtil.WriteLog("模型数据预览: ");
|
||
foreach (var column in preview.Schema)
|
||
{
|
||
LogUtil.WriteLog($"列名: {column.Name}, 类型: {column.Type}");
|
||
}
|
||
|
||
// 定义线性回归模型,使用动态生成的 Features 列
|
||
var pipeline = mlContext.Transforms.CopyColumns("Features", "Features")
|
||
.Append(mlContext.Regression.Trainers.Sdca(
|
||
labelColumnName: "TotalRevenue",
|
||
featureColumnName: "Features",
|
||
l2Regularization: (float?)1.0)); // 你可以调整 L2 正则化权重
|
||
|
||
// 训练模型
|
||
var model = pipeline.Fit(data);
|
||
|
||
// 获取每个省份的单车价值
|
||
var coefficients = model.LastTransformer.Model.Weights.ToArray();
|
||
|
||
if (coefficients.Length != provinceNames.Count)
|
||
{
|
||
throw new Exception("权重与省份数量不匹配,无法继续。");
|
||
}
|
||
|
||
for (int i = 0; i < provinceNames.Count; i++)
|
||
{
|
||
provinceSingleVehicleValues[provinceNames[i]] = coefficients[i];
|
||
}
|
||
|
||
// 输出各省份的单车价值
|
||
var sb = new StringBuilder();
|
||
sb.AppendLine("各省份的单车价值:");
|
||
foreach (var kvp in provinceSingleVehicleValues)
|
||
{
|
||
sb.AppendLine($"{kvp.Key}: {kvp.Value}");
|
||
}
|
||
LogUtil.WriteLog(sb.ToString());
|
||
}
|
||
|
||
// 定义用于模型输入的类
|
||
public class ModelInput
|
||
{
|
||
public ModelInput(int featureLength)
|
||
{
|
||
Features = new float[featureLength]; // 确保特征向量的长度在构造时定义
|
||
}
|
||
|
||
[VectorType(31)] // 请将 13 替换为实际的最大特征长度
|
||
public float[] Features { get; set; }
|
||
|
||
public float TotalRevenue { get; set; }
|
||
}
|
||
|
||
static void CalculateAndCompare(RevenueData revenueData)
|
||
{
|
||
float predictedTotalRevenue = 0;
|
||
float actualTotalRevenue = revenueData.TotalRevenue;
|
||
|
||
// 计算预测的总营收
|
||
foreach (var province in revenueData.ProvinceVehicleCounts)
|
||
{
|
||
if (provinceSingleVehicleValues.ContainsKey(province.Key))
|
||
{
|
||
predictedTotalRevenue += province.Value * provinceSingleVehicleValues[province.Key];
|
||
}
|
||
}
|
||
|
||
// 计算偏差值和偏差比例
|
||
float deviation = actualTotalRevenue - predictedTotalRevenue;
|
||
float deviationPercentage = deviation / actualTotalRevenue * 100;
|
||
|
||
// 输出结果
|
||
LogUtil.WriteLog($"实际总营收: {actualTotalRevenue}");
|
||
LogUtil.WriteLog($"预测总营收: {predictedTotalRevenue}");
|
||
LogUtil.WriteLog($"偏差值: {deviation}");
|
||
LogUtil.WriteLog($"偏差比例: {deviationPercentage}%");
|
||
}
|
||
|
||
public static async Task AnalyzeMultipleServiceAreas(List<string> serviceAreaIds, List<DateTime> dates)
|
||
{
|
||
try
|
||
{
|
||
var allRevenueDataList = new List<RevenueInput>();
|
||
foreach (var serviceAreaId in serviceAreaIds)
|
||
{
|
||
LogUtil.WriteLog($"服务区内码:{ serviceAreaId }");
|
||
foreach (var date in dates)
|
||
{
|
||
LogUtil.WriteLog($"统计日期:{ date }");
|
||
// 获取服务区和日期的营收和车辆数据
|
||
RevenueData revenueData = await GetRevenueDataFromApiAsync(date, serviceAreaId);
|
||
|
||
// 转换为模型输入格式
|
||
var revenueInput = new RevenueInput
|
||
{
|
||
ProvinceVehicleCounts = revenueData.ProvinceVehicleCounts,
|
||
TotalRevenue = revenueData.TotalRevenue
|
||
};
|
||
|
||
allRevenueDataList.Add(revenueInput);
|
||
|
||
// 比较预测和实际的营收
|
||
CalculateAndCompare(revenueData);
|
||
}
|
||
}
|
||
|
||
// 进行模型训练和预测
|
||
TrainAndPredict(allRevenueDataList);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogUtil.WriteLog(ex, "", DateTime.Now.ToString("yyyyMMdd") + "_error");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 第二版单车价值估算方法
|
||
// 模拟从API中获取数据的函数
|
||
public static Dictionary<string, Dictionary<string, object>> FetchDataForMonth(
|
||
string startMonth, string endMonth, string serverpartId)
|
||
{
|
||
Transaction transaction = new Transaction();
|
||
|
||
string SQLString = string.Format(@"SELECT
|
||
*
|
||
FROM
|
||
PLATFORM_DASHBOARD.T_REVENUEMONTHLY
|
||
WHERE
|
||
SERVERPART_ID = {0} AND STATISTICS_MONTH BETWEEN {1} AND {2}",
|
||
serverpartId, startMonth, endMonth);
|
||
DataTable dtRevenue = new HWSB.SERVERPART(transaction).ExecuteDataTable(SQLString);
|
||
|
||
SQLString = string.Format(@"SELECT * FROM HIGHWAY_SELLDATA.T_BAYONETPROVINCEMONTH_AH
|
||
WHERE SERVERPART_ID = {0} AND STATISTICS_MONTH BETWEEN {1} AND {2} AND SERVERPART_REGION IS NULL",
|
||
serverpartId, startMonth, endMonth);
|
||
DataTable dtBayonet = new HWSB.SERVERPART(transaction).ExecuteDataTable(SQLString);
|
||
|
||
// 示例数据:省份内部具体地区的小型车、大型车和中型车的车辆数,以及总收入
|
||
Dictionary<string, Dictionary<string, object>> provinceData = new Dictionary<string, Dictionary<string, object>>();
|
||
foreach (string month in dtRevenue.AsEnumerable().Select(o => o["STATISTICS_MONTH"] + "").Distinct().OrderBy(o => o))
|
||
{
|
||
Dictionary<string, Dictionary<string, int>> regionData = new Dictionary<string, Dictionary<string, int>>();
|
||
foreach (DataRow drBayonet in dtBayonet.Select("VEHICLE_TYPE is null and STATISTICS_MONTH = " + month, "VEHICLE_COUNT desc"))
|
||
{
|
||
regionData.Add(drBayonet["PROVINCE_NAME"].ToString(), new Dictionary<string, int>
|
||
{
|
||
{ "Small", dtBayonet.Compute("sum(VEHICLE_COUNT)", "VEHICLE_TYPE = '小型车' and PROVINCE_NAME = '" +
|
||
drBayonet["PROVINCE_NAME"] + "'").TryParseToInt() },
|
||
{ "Medium", dtBayonet.Compute("sum(VEHICLE_COUNT)", "VEHICLE_TYPE = '中型车' and PROVINCE_NAME = '" +
|
||
drBayonet["PROVINCE_NAME"] + "'").TryParseToInt() },
|
||
{ "Large", dtBayonet.Compute("sum(VEHICLE_COUNT)", "VEHICLE_TYPE = '大型车' and PROVINCE_NAME = '" +
|
||
drBayonet["PROVINCE_NAME"] + "'").TryParseToInt() }
|
||
});
|
||
}
|
||
|
||
provinceData.Add(month, new Dictionary<string, object>
|
||
{
|
||
{ "Regions", regionData },
|
||
{ "TotalRevenue", dtRevenue.Compute("sum(REVENUE_AMOUNT)", "STATISTICS_MONTH = " + month).TryParseToDecimal() }
|
||
});
|
||
}
|
||
//{
|
||
// { "Anhui", new Dictionary<string, object>
|
||
// {
|
||
// { "Regions", new Dictionary<string, Dictionary<string, int>>
|
||
// {
|
||
// { "Hefei", new Dictionary<string, int> { { "Small", 50000 }, { "Medium", 1000 }, { "Large", 10000 } } },
|
||
// { "Wuhu", new Dictionary<string, int> { { "Small", 30000 }, { "Medium", 800 }, { "Large", 8000 } } },
|
||
// { "Others", new Dictionary<string, int> { { "Small", 20000 }, { "Medium", 500 }, { "Large", 5000 } } }
|
||
// // 增加安徽省其他地区的数据
|
||
// }
|
||
// },
|
||
// { "TotalRevenue", 1200000.0 } // 更新安徽省的总收入数据
|
||
// }
|
||
// },
|
||
// { "Jiangsu", new Dictionary<string, object>
|
||
// {
|
||
// { "Regions", new Dictionary<string, Dictionary<string, int>>
|
||
// {
|
||
// { "Nanjing", new Dictionary<string, int> { { "Small", 12000 }, { "Medium", 200 }, { "Large", 2000 } } },
|
||
// { "Suzhou", new Dictionary<string, int> { { "Small", 11000 }, { "Medium", 150 }, { "Large", 1800 } } },
|
||
// { "Others", new Dictionary<string, int> { { "Small", 8000 }, { "Medium", 100 }, { "Large", 1200 } } } // 增加江苏省其他地区的数据
|
||
// }
|
||
// },
|
||
// { "TotalRevenue", 500000.0 } // 更新江苏省的总收入数据
|
||
// }
|
||
// }
|
||
// // 可以继续添加其他省份的数据
|
||
//};
|
||
|
||
transaction.Release();
|
||
transaction.Dispose();
|
||
|
||
return provinceData;
|
||
}
|
||
|
||
// 计算单车价值的函数
|
||
public static void CalculateSingleVehicleValue(Dictionary<string, Dictionary<string, object>> provinceData)
|
||
{
|
||
// 设置迭代参数:容忍误差范围、最大迭代次数
|
||
decimal tolerance = (decimal)0.01; // 允许的误差范围(百分之一)
|
||
int maxIterations = 1000; // 最大迭代次数,防止无限循环
|
||
|
||
// 遍历每个省份的数据
|
||
foreach (var province in provinceData)
|
||
{
|
||
string provinceName = province.Key;
|
||
var regions = (Dictionary<string, Dictionary<string, int>>)province.Value["Regions"];
|
||
decimal totalRevenue = (decimal)province.Value["TotalRevenue"];
|
||
|
||
// 计算初始的平均单车价值(将总营收除以车辆数的总和)
|
||
int totalVehicleCount = regions.Values.Sum(region => region["Small"] + region["Medium"] + region["Large"]);
|
||
decimal initialVehicleValue = totalRevenue / totalVehicleCount;
|
||
|
||
// 初始化每个地区的小型车和大型车的单车价值为平均值
|
||
Dictionary<string, decimal> smallVehicleValues = regions.ToDictionary(region => region.Key, region => initialVehicleValue);
|
||
Dictionary<string, decimal> largeVehicleValues = regions.ToDictionary(region => region.Key, region => initialVehicleValue);
|
||
|
||
int iteration = 0;
|
||
bool converged = false;
|
||
|
||
// 迭代计算以优化单车价值
|
||
while (!converged && iteration < maxIterations)
|
||
{
|
||
// 使用当前单车价值计算总营收
|
||
decimal calculatedRevenue = regions.Sum(region =>
|
||
{
|
||
string regionName = region.Key;
|
||
int smallCount = region.Value["Small"] + region.Value["Medium"]; // 中型车归属于小型车
|
||
int largeCount = region.Value["Large"];
|
||
return smallCount * smallVehicleValues[regionName] + largeCount * largeVehicleValues[regionName];
|
||
});
|
||
|
||
// 计算计算出的总营收与实际总营收之间的误差
|
||
decimal error = totalRevenue - calculatedRevenue;
|
||
|
||
// 如果误差在容忍范围内,则停止迭代
|
||
if (Math.Abs(error) <= tolerance)
|
||
{
|
||
converged = true;
|
||
break;
|
||
}
|
||
|
||
// 根据误差比例调整小型车和大型车的单车价值
|
||
decimal adjustmentRatio = 1 + (error / calculatedRevenue);
|
||
foreach (var regionName in regions.Keys.ToList())
|
||
{
|
||
smallVehicleValues[regionName] *= adjustmentRatio;
|
||
largeVehicleValues[regionName] *= adjustmentRatio;
|
||
}
|
||
|
||
iteration++;
|
||
}
|
||
|
||
// 显示结果
|
||
LogUtil.WriteLog($"{provinceName} 小型车和大型车单车价值和营收分析:\n");
|
||
foreach (var region in regions)
|
||
{
|
||
string regionName = region.Key;
|
||
int smallCount = region.Value["Small"] + region.Value["Medium"]; // 中型车归属于小型车
|
||
int largeCount = region.Value["Large"];
|
||
decimal smallVehicleRevenue = smallCount * smallVehicleValues[regionName];
|
||
decimal largeVehicleRevenue = largeCount * largeVehicleValues[regionName];
|
||
LogUtil.WriteLog($"地区: {regionName}");
|
||
LogUtil.WriteLog($" 小型车车辆数: {smallCount}");
|
||
LogUtil.WriteLog($" 大型车车辆数: {largeCount}");
|
||
LogUtil.WriteLog($" 小型车单车价值: {smallVehicleValues[regionName]:F2} 元/车");
|
||
LogUtil.WriteLog($" 大型车单车价值: {largeVehicleValues[regionName]:F2} 元/车");
|
||
LogUtil.WriteLog($" 小型车计算的营收: {smallVehicleRevenue:F2} 元");
|
||
LogUtil.WriteLog($" 大型车计算的营收: {largeVehicleRevenue:F2} 元");
|
||
LogUtil.WriteLog($" 合计营收: {smallVehicleRevenue + largeVehicleRevenue:F2} 元\n");
|
||
}
|
||
|
||
// 显示迭代细节和收敛状态
|
||
LogUtil.WriteLog($"月份: {provinceName}");
|
||
LogUtil.WriteLog($" 迭代次数: {iteration}");
|
||
LogUtil.WriteLog($" 是否收敛: {converged}\n");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 第三版单车价值估算方法
|
||
public class ProvinceData
|
||
{
|
||
public string Name { get; set; } // 省份名称
|
||
public double PerCapitaIncome { get; set; } // 人均可支配收入
|
||
public Dictionary<string, int> VehicleCounts { get; set; } // 各种车辆类型的车辆数量
|
||
public double ConsumptionCoefficient { get; set; } // 消费能力系数 c_i
|
||
public double InitialVehicleValueSmall { get; set; } // 小型车初始单车价值 y_i^(0)
|
||
public double InitialVehicleValueMedium { get; set; } // 中型车初始单车价值
|
||
public double InitialVehicleValueLarge { get; set; } // 大型车初始单车价值
|
||
public double AdjustedVehicleValueSmall { get; set; } // 小型车调整后单车价值 y_i
|
||
public double AdjustedVehicleValueMedium { get; set; } // 中型车调整后单车价值
|
||
public double AdjustedVehicleValueLarge { get; set; } // 大型车调整后单车价值
|
||
public double RevenueContributionSmall { get; set; } // 小型车营收贡献
|
||
public double RevenueContributionMedium { get; set; } // 中型车营收贡献
|
||
public double RevenueContributionLarge { get; set; } // 大型车营收贡献
|
||
public double AdjustmentSmall { get; set; } // 小型车调整量
|
||
public double AdjustmentMedium { get; set; } // 中型车调整量
|
||
public double AdjustmentLarge { get; set; } // 大型车调整量
|
||
public double MaxAdjustmentSmall { get; set; } // 小型车最大调整量 ±10%
|
||
public double MaxAdjustmentMedium { get; set; } // 中型车最大调整量 ±10%
|
||
public double MaxAdjustmentLarge { get; set; } // 大型车最大调整量 ±10%
|
||
}
|
||
|
||
public static void CalculateVehicleValues(string month, double totalRevenue, int totalVehicleCount,
|
||
Dictionary<string, Dictionary<string, int>> provinceVehicleCounts, ref List<Model.VEHICLEAMOUNTModel> resultList)
|
||
{
|
||
// 初始化省份数据,包括各类型车辆的数量和人均可支配收入
|
||
List<ProvinceData> provinces = InitializeProvinceData(provinceVehicleCounts);
|
||
|
||
// 全国人均可支配收入(根据最新数据)
|
||
double nationalPerCapitaIncome = 35100; // 2022年全国居民人均可支配收入(举例值,需替换为实际数据)
|
||
|
||
// 指数参数 gamma
|
||
double gamma = 0.5;
|
||
|
||
// 计算全国平均单车价值
|
||
double averageVehicleValue = totalRevenue / totalVehicleCount;
|
||
|
||
// 计算各省份的消费能力系数 c_i 和初始单车价值 y_i^(0)
|
||
foreach (var province in provinces)
|
||
{
|
||
province.ConsumptionCoefficient = Math.Pow(province.PerCapitaIncome / nationalPerCapitaIncome, gamma);
|
||
province.InitialVehicleValueSmall = averageVehicleValue * province.ConsumptionCoefficient;
|
||
province.InitialVehicleValueMedium = averageVehicleValue * province.ConsumptionCoefficient;
|
||
province.InitialVehicleValueLarge = averageVehicleValue * province.ConsumptionCoefficient;
|
||
|
||
// 最大调整量 ±10%
|
||
province.MaxAdjustmentSmall = 0.10 * province.InitialVehicleValueSmall;
|
||
province.MaxAdjustmentMedium = 0.10 * province.InitialVehicleValueMedium;
|
||
province.MaxAdjustmentLarge = 0.10 * province.InitialVehicleValueLarge;
|
||
}
|
||
|
||
// 使用 OR-Tools 进行线性规划求解
|
||
Solver solver = Solver.CreateSolver("CBC_MIXED_INTEGER_PROGRAMMING");
|
||
|
||
if (solver == null)
|
||
{
|
||
LogUtil.WriteLog("无法创建求解器。");
|
||
return;
|
||
}
|
||
|
||
// 创建决策变量 delta_i,并设置上下界
|
||
Variable[] deltasSmall = new Variable[provinces.Count];
|
||
Variable[] deltasMedium = new Variable[provinces.Count];
|
||
Variable[] deltasLarge = new Variable[provinces.Count];
|
||
|
||
for (int i = 0; i < provinces.Count; i++)
|
||
{
|
||
deltasSmall[i] = solver.MakeNumVar(-provinces[i].MaxAdjustmentSmall, provinces[i].MaxAdjustmentSmall, $"delta_small_{i}");
|
||
deltasMedium[i] = solver.MakeNumVar(-provinces[i].MaxAdjustmentMedium, provinces[i].MaxAdjustmentMedium, $"delta_medium_{i}");
|
||
deltasLarge[i] = solver.MakeNumVar(-provinces[i].MaxAdjustmentLarge, provinces[i].MaxAdjustmentLarge, $"delta_large_{i}");
|
||
}
|
||
|
||
// 创建约束:sum(N_i * (y_i^(0) + delta_i)) = totalRevenue
|
||
Google.OrTools.LinearSolver.Constraint revenueConstraint = solver.MakeConstraint(0, 0); // 只优化目标
|
||
|
||
for (int i = 0; i < provinces.Count; i++)
|
||
{
|
||
revenueConstraint.SetCoefficient(deltasSmall[i], provinces[i].VehicleCounts["Small"]);
|
||
revenueConstraint.SetCoefficient(deltasMedium[i], provinces[i].VehicleCounts["Medium"]);
|
||
revenueConstraint.SetCoefficient(deltasLarge[i], provinces[i].VehicleCounts["Large"]);
|
||
}
|
||
|
||
// 目标函数:最小化调整量的平方和(为了平衡各省份的调整量)
|
||
Objective objective = solver.Objective();
|
||
for (int i = 0; i < provinces.Count; i++)
|
||
{
|
||
objective.SetCoefficient(deltasSmall[i], 1);
|
||
objective.SetCoefficient(deltasMedium[i], 1);
|
||
objective.SetCoefficient(deltasLarge[i], 1);
|
||
}
|
||
objective.SetMinimization();
|
||
|
||
// 求解问题
|
||
Solver.ResultStatus resultStatus = solver.Solve();
|
||
|
||
// 检查求解状态并更新各省份的调整量和调整后单车价值
|
||
if (resultStatus == Solver.ResultStatus.OPTIMAL)
|
||
{
|
||
for (int i = 0; i < provinces.Count; i++)
|
||
{
|
||
provinces[i].AdjustmentSmall = deltasSmall[i].SolutionValue();
|
||
provinces[i].AdjustmentMedium = deltasMedium[i].SolutionValue();
|
||
provinces[i].AdjustmentLarge = deltasLarge[i].SolutionValue();
|
||
|
||
provinces[i].AdjustedVehicleValueSmall = provinces[i].InitialVehicleValueSmall + provinces[i].AdjustmentSmall;
|
||
provinces[i].AdjustedVehicleValueMedium = provinces[i].InitialVehicleValueMedium + provinces[i].AdjustmentMedium;
|
||
provinces[i].AdjustedVehicleValueLarge = provinces[i].InitialVehicleValueLarge + provinces[i].AdjustmentLarge;
|
||
|
||
provinces[i].RevenueContributionSmall = provinces[i].AdjustedVehicleValueSmall * provinces[i].VehicleCounts["Small"];
|
||
provinces[i].RevenueContributionMedium = provinces[i].AdjustedVehicleValueMedium * provinces[i].VehicleCounts["Medium"];
|
||
provinces[i].RevenueContributionLarge = provinces[i].AdjustedVehicleValueLarge * provinces[i].VehicleCounts["Large"];
|
||
}
|
||
|
||
// 输出结果
|
||
LogUtil.WriteLog($"月份:{month}");
|
||
LogUtil.WriteLog("各省份单车价值和营收贡献计算结果:");
|
||
LogUtil.WriteLog("-----------------------------------------------------------------------------------------------------------------");
|
||
LogUtil.WriteLog("省份\t车辆类型\t车辆数\t人均收入\t消费系数\t初始单车价值\t调整量\t调整后单车价值\t营收贡献");
|
||
|
||
foreach (var province in provinces)
|
||
{
|
||
LogUtil.WriteLog($"{province.Name}\t小型车\t{province.VehicleCounts["Small"]}\t" +
|
||
$"{province.PerCapitaIncome}\t{province.ConsumptionCoefficient:F3}\t" +
|
||
$"{province.InitialVehicleValueSmall:F2}\t{province.AdjustmentSmall:F2}\t" +
|
||
$"{province.AdjustedVehicleValueSmall:F2}\t{province.RevenueContributionSmall:F2}");
|
||
|
||
resultList.Add(new Model.VEHICLEAMOUNTModel
|
||
{
|
||
PROVINCE_NAME = province.Name,
|
||
VEHICLE_TYPE = "小型车",
|
||
VEHICLE_COUNT = province.VehicleCounts["Small"],
|
||
PERCAPITA_INCOME = province.PerCapitaIncome,
|
||
CONSUMPTION_COEFFICIENT = province.ConsumptionCoefficient,
|
||
VEHICLE_AMOUNT = province.InitialVehicleValueSmall,
|
||
ADJUST_COUNT = province.AdjustmentSmall,
|
||
VEHICLE_ADJAMOUNT = province.AdjustedVehicleValueSmall,
|
||
REVENUE_ADJAMOUNT = province.RevenueContributionSmall,
|
||
VEHICLEAMOUNT_STATE = 1,
|
||
RECORD_DATE = DateTime.Now,
|
||
});
|
||
|
||
LogUtil.WriteLog($"{province.Name}\t中型车\t{province.VehicleCounts["Medium"]}\t" +
|
||
$"{province.PerCapitaIncome}\t{province.ConsumptionCoefficient:F3}\t" +
|
||
$"{province.InitialVehicleValueMedium:F2}\t{province.AdjustmentMedium:F2}\t" +
|
||
$"{province.AdjustedVehicleValueMedium:F2}\t{province.RevenueContributionMedium:F2}");
|
||
|
||
resultList.Add(new Model.VEHICLEAMOUNTModel
|
||
{
|
||
PROVINCE_NAME = province.Name,
|
||
VEHICLE_TYPE = "中型车",
|
||
VEHICLE_COUNT = province.VehicleCounts["Medium"],
|
||
PERCAPITA_INCOME = province.PerCapitaIncome,
|
||
CONSUMPTION_COEFFICIENT = province.ConsumptionCoefficient,
|
||
VEHICLE_AMOUNT = province.InitialVehicleValueMedium,
|
||
ADJUST_COUNT = province.AdjustmentMedium,
|
||
VEHICLE_ADJAMOUNT = province.AdjustedVehicleValueMedium,
|
||
REVENUE_ADJAMOUNT = province.RevenueContributionMedium,
|
||
VEHICLEAMOUNT_STATE = 1,
|
||
RECORD_DATE = DateTime.Now,
|
||
});
|
||
|
||
LogUtil.WriteLog($"{province.Name}\t大型车\t{province.VehicleCounts["Large"]}\t" +
|
||
$"{province.PerCapitaIncome}\t{province.ConsumptionCoefficient:F3}\t" +
|
||
$"{province.InitialVehicleValueLarge:F2}\t{province.AdjustmentLarge:F2}\t" +
|
||
$"{province.AdjustedVehicleValueLarge:F2}\t{province.RevenueContributionLarge:F2}");
|
||
|
||
resultList.Add(new Model.VEHICLEAMOUNTModel
|
||
{
|
||
PROVINCE_NAME = province.Name,
|
||
VEHICLE_TYPE = "大型车",
|
||
VEHICLE_COUNT = province.VehicleCounts["Large"],
|
||
PERCAPITA_INCOME = province.PerCapitaIncome,
|
||
CONSUMPTION_COEFFICIENT = province.ConsumptionCoefficient,
|
||
VEHICLE_AMOUNT = province.InitialVehicleValueLarge,
|
||
ADJUST_COUNT = province.AdjustmentLarge,
|
||
VEHICLE_ADJAMOUNT = province.AdjustedVehicleValueLarge,
|
||
REVENUE_ADJAMOUNT = province.RevenueContributionLarge,
|
||
VEHICLEAMOUNT_STATE = 1,
|
||
RECORD_DATE = DateTime.Now,
|
||
});
|
||
}
|
||
|
||
Model.VEHICLEAMOUNTModel ServerpartModel = new Model.VEHICLEAMOUNTModel
|
||
{
|
||
REVENUE_ADJAMOUNT = provinces.Sum(p => p.RevenueContributionSmall + p.RevenueContributionMedium + p.RevenueContributionLarge),
|
||
REVENUE_ACTAMOUNT = totalRevenue,
|
||
VEHICLE_TOTALCOUNT = totalVehicleCount,
|
||
VEHICLEAMOUNT_STATE = 1,
|
||
RECORD_DATE = DateTime.Now,
|
||
};
|
||
resultList.Insert(0, ServerpartModel);
|
||
|
||
LogUtil.WriteLog("-----------------------------------------------------------------------------------------------------------------");
|
||
LogUtil.WriteLog($"调整后总营收: {ServerpartModel.REVENUE_ADJAMOUNT:F2}");
|
||
LogUtil.WriteLog($"实际总营收: {totalRevenue:F2}");
|
||
LogUtil.WriteLog($"总营收差异: {totalRevenue - ServerpartModel.REVENUE_ADJAMOUNT:F2}");
|
||
LogUtil.WriteLog($"误差率: {(1 - ServerpartModel.REVENUE_ADJAMOUNT / totalRevenue) * 100:F2}");
|
||
}
|
||
else
|
||
{
|
||
LogUtil.WriteLog($"求解失败,状态码: {resultStatus}");
|
||
if (resultStatus == Solver.ResultStatus.FEASIBLE)
|
||
{
|
||
LogUtil.WriteLog("找到可行解,但不是最优解。");
|
||
}
|
||
else if (resultStatus == Solver.ResultStatus.INFEASIBLE)
|
||
{
|
||
LogUtil.WriteLog("模型不可行,请检查约束设置。");
|
||
}
|
||
else if (resultStatus == Solver.ResultStatus.UNBOUNDED)
|
||
{
|
||
LogUtil.WriteLog("模型无界,请检查变量的上下界设置。");
|
||
}
|
||
}
|
||
}
|
||
|
||
static List<ProvinceData> InitializeProvinceData(Dictionary<string, Dictionary<string, int>> provinceVehicleCounts)
|
||
{
|
||
// 省份人均可支配收入(根据最新数据,替换为实际值)
|
||
Dictionary<string, double> provincePerCapitaIncomes = new Dictionary<string, double>
|
||
{
|
||
{ "北京市", 75002 },
|
||
{ "天津市", 45337 },
|
||
{ "河北省", 27243 },
|
||
{ "山西省", 27682 },
|
||
{ "辽宁省", 34798 },
|
||
{ "上海市", 72232 },
|
||
{ "江苏省", 48126 },
|
||
{ "浙江省", 57541 },
|
||
{ "安徽省", 30156 },
|
||
{ "福建省", 38051 },
|
||
{ "江西省", 29056 },
|
||
{ "山东省", 35756 },
|
||
{ "河南省", 26125 },
|
||
{ "湖北省", 33215 },
|
||
{ "湖南省", 28763 },
|
||
{ "广东省", 44527 },
|
||
{ "其他省份", 35100 }
|
||
};
|
||
|
||
List<ProvinceData> provinces = new List<ProvinceData>();
|
||
|
||
foreach (var kvp in provinceVehicleCounts)
|
||
{
|
||
string provinceName = kvp.Key;
|
||
var vehicleCounts = kvp.Value;
|
||
|
||
double perCapitaIncome = provincePerCapitaIncomes.ContainsKey(provinceName) ?
|
||
provincePerCapitaIncomes[provinceName] : 35100; // 默认全国平均值
|
||
|
||
ProvinceData province = new ProvinceData
|
||
{
|
||
Name = provinceName,
|
||
PerCapitaIncome = perCapitaIncome,
|
||
VehicleCounts = vehicleCounts, // 车辆类型及其数量
|
||
InitialVehicleValueSmall = 0, // 初始值将通过后续计算确定
|
||
InitialVehicleValueMedium = 0,
|
||
InitialVehicleValueLarge = 0,
|
||
AdjustedVehicleValueSmall = 0,
|
||
AdjustedVehicleValueMedium = 0,
|
||
AdjustedVehicleValueLarge = 0,
|
||
RevenueContributionSmall = 0,
|
||
RevenueContributionMedium = 0,
|
||
RevenueContributionLarge = 0,
|
||
AdjustmentSmall = 0,
|
||
AdjustmentMedium = 0,
|
||
AdjustmentLarge = 0,
|
||
MaxAdjustmentSmall = 0,
|
||
MaxAdjustmentMedium = 0,
|
||
MaxAdjustmentLarge = 0,
|
||
};
|
||
|
||
provinces.Add(province);
|
||
}
|
||
|
||
return provinces;
|
||
}
|
||
#endregion
|
||
}
|
||
} |