调整表达式运算错误,重新排版代码,优化单纯形搜索过程

This commit is contained in:
LU
2026-10-09 16:44:19 +08:00
parent 2d11592fd3
commit d469796009
8 changed files with 485 additions and 489 deletions
+4 -4
View File
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16.0) cmake_minimum_required(VERSION 3.16.0)
project(Solver) project(solver)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -22,10 +22,10 @@ file(GLOB HEADERS "${INCLUDE_DIR}/*.hpp")
aux_source_directory(${SOURCE_DIR} src) aux_source_directory(${SOURCE_DIR} src)
aux_source_directory(${INCLUDE_DIR} inc) aux_source_directory(${INCLUDE_DIR} inc)
add_executable(Solver ${SOURCES} ${HEADERS}) add_executable(solver ${SOURCES} ${HEADERS})
#target_link_libraries(Solver #target_link_libraries(solver
# ${GUROBI_LIBRARIES} # ${GUROBI_LIBRARIES}
#) #)
target_include_directories(Solver PRIVATE ${SOURCE_DIR} ${INCLUDE_DIR}) target_include_directories(solver PRIVATE ${SOURCE_DIR} ${INCLUDE_DIR})
+19
View File
@@ -0,0 +1,19 @@
## P0 基础修复
1. 补齐变量约束能力:完善变量 LB/UB 上下界定义与校验;对 BINARY 类型严格强制 0-1 取值
2. 增加测试与回归用例:搭建基础测试基线,覆盖整数、二进制、简单 MIP 算例,建立回归机制
## P1 核心能力
3. 完善终止条件与工程可控性:实现 TimeLimit 时间限制、MIPGap 收敛间隙终止条件;完善运行日志输出
4. 迭代 MIP 分支与节点选择策略:按顺序落地:best-bound 选节点 → pseudocost 分支策略
## P2 进阶算法
5. 增加启发式、割平面与预处理能力:实现一种rounding舍入启发式、root层Gomory割平面、简单预处理逻辑
## P3 底层求解架构
6. 学习稀疏单纯形,对接HiGHS等成熟求解器架构:调研学习稀疏单纯形、稀疏基分解、对偶单纯形热启动机制
## P4 工程收口与标准化(待定)
+144 -152
View File
@@ -4,171 +4,163 @@
#define MDL_MINIMIZE 1 #define MDL_MINIMIZE 1
#define MDL_MAXIMIZE -1 #define MDL_MAXIMIZE -1
#define LOADED 0 #define LOADED 0
#define OPTIMAL 1 #define OPTIMAL 1
#define INFEASIBLE 2 #define INFEASIBLE 2
#define INF_OR_UNBD 3 #define INF_OR_UNBD 3
#define UNBOUNDED 4 #define UNBOUNDED 4
#define CUTOFF 5 #define CUTOFF 5
#define ITERATION_LIMIT 6 #define ITERATION_LIMIT 6
#define NODE_LIMIT 7 #define NODE_LIMIT 7
#define TIME_LIMIT 8 #define TIME_LIMIT 8
#define SOLUTION_LIMIT 9 #define SOLUTION_LIMIT 9
#define INTERRUPTED 10 #define INTERRUPTED 10
#define NUMERIC 11 #define NUMERIC 11
#define SUBOPTIMAL 12 #define SUBOPTIMAL 12
#define INPROGRESS 13 #define INPROGRESS 13
#define USER_OBJ_LIMIT 14 #define USER_OBJ_LIMIT 14
#define WORK_LIMIT 15 #define WORK_LIMIT 15
#define MEM_LIMIT 16 #define MEM_LIMIT 16
#define PIVOT 17 #define PIVOT 17
namespace sv { namespace sv {
using matrix = std::vector<std::vector<double>>; using matrix = std::vector<std::vector<double>>;
using rtn = int; using rtn = int;
class Expr; class Expr;
class Var; class Var;
enum class IntAttr { enum class IntAttr {
NumConstrs, NumConstrs,
NumVars, NumVars,
NumIntVars, NumIntVars,
NumBinVars, NumBinVars,
ModelSense, ModelSense,
IsMIP, IsMIP,
IsMultiObj, IsMultiObj,
Status, Status,
SolCount, SolCount,
Lazy, Lazy,
NumObj, NumObj,
NumCol NumCol
}; };
enum class DoubleAttr { enum class DoubleAttr {
Runtime, Runtime,
Work, Work,
ObjCon, ObjCon,
LB, LB,
UB, UB,
Obj, Obj,
Start, Start,
RHS, RHS,
Coeff, Coeff,
MaxCoeff, MaxCoeff,
MinCoeff, MinCoeff,
MaxBound, MaxBound,
MinBound, MinBound,
ObjVal, ObjVal,
MIPGap, MIPGap,
IterCount, IterCount,
NodeCount, NodeCount,
X, X,
Slack, Slack,
}; };
enum class StringAttr { enum class StringAttr {
ModelName, ModelName,
VarName, VarName,
ConstrName, ConstrName,
QCName, QCName,
GenConstrName, GenConstrName,
ObjNName, ObjNName,
ScenNName, ScenNName,
BatchID, BatchID,
VTag, VTag,
CTag, CTag,
QCTag, QCTag,
BatchErrorMessage BatchErrorMessage
}; };
enum class ConstrOper { enum class ConstrOper { LESS_EQUAL, GREATER_EQUAL, EQUAL };
LESS_EQUAL,
GREATER_EQUAL,
EQUAL
};
enum class VarType {
CONTINUOUS,
BINARY,
INTEGER,
};
enum class VarType { class Var {
CONTINUOUS, public:
BINARY, friend class Model;
INTEGER, friend class LinSolver;
}; friend class Expr;
class Var { Var(double coef = 1, VarType type_ = VarType::CONTINUOUS);
public: double get(DoubleAttr attr);
friend class Model; int get(IntAttr attr);
friend class LinSolver;
friend class Expr;
Var(double coef = 1, VarType type_ = VarType::CONTINUOUS); private:
double get(DoubleAttr attr); double coeffs;
int get(IntAttr attr); double val;
int col;
VarType type;
};
private: Expr operator+(const Expr &x, const Expr &y);
double coeffs; Expr operator-(const Expr &x, const Expr &y);
double val; Expr operator+(const Expr &x);
int col; Expr operator+(Var x, Var y);
VarType type; Expr operator+(Var x, double a);
}; Expr operator+(double a, Var x);
Expr operator-(const Expr &x);
Expr operator-(Var x);
Expr operator-(Var x, Var y);
Expr operator-(Var x, double a);
Expr operator-(double a, Var x);
Expr operator*(double a, Var x);
Expr operator*(Var x, double a);
Expr operator*(const Expr &x, double a);
Expr operator*(double a, const Expr &x);
Expr operator/(Var x, double a);
Expr operator/(const Expr &x, double a);
Expr operator+(const Expr& x, const Expr& y); class Expr {
Expr operator-(const Expr& x, const Expr& y); private:
Expr operator+(const Expr& x); double constant;
Expr operator+(Var x, Var y); std::vector<double> coeffs;
Expr operator+(Var x, double a); std::vector<Var> vars;
Expr operator+(double a, Var x);
Expr operator-(const Expr& x);
Expr operator-(Var x);
Expr operator-(Var x, Var y);
Expr operator-(Var x, double a);
Expr operator-(double a, Var x);
Expr operator*(double a, Var x);
Expr operator*(Var x, double a);
Expr operator*(const Expr& x, double a);
Expr operator*(double a, const Expr& x);
Expr operator/(Var x, double a);
Expr operator/(const Expr& x, double a);
public:
Expr(const Expr &expr) = default;
Expr(double constant = 0.0);
Expr(Var var, double coeff = 1.0);
class Expr friend class LinSolver;
{
private:
double constant;
std::vector<double> coeffs;
std::vector<Var> vars;
public: friend Expr operator+(const Expr &x, const Expr &y);
Expr(const Expr& expr) = default; friend Expr operator+(const Expr &x);
Expr(double constant = 0.0); friend Expr operator+(Var x, Var y);
Expr(Var var, double coeff = 1.0); friend Expr operator+(Var x, double a);
friend Expr operator+(double a, Var x);
friend Expr operator-(const Expr &x, const Expr &y);
friend Expr operator-(const Expr &x);
friend Expr operator-(Var x);
friend Expr operator-(Var x, Var y);
friend Expr operator-(Var x, double a);
friend Expr operator-(double a, Var x);
friend Expr operator*(double a, Var x);
friend Expr operator*(Var x, double a);
friend Expr operator*(const Expr &x, double a);
friend Expr operator*(double a, const Expr &x);
friend Expr operator/(Var x, double a);
friend Expr operator/(const Expr &x, double a);
friend class LinSolver; Expr &operator=(const Expr &rhs);
void operator+=(const Expr &expr);
friend Expr operator+(const Expr& x, const Expr& y); void operator-=(const Expr &expr);
friend Expr operator+(const Expr& x); void operator*=(double mult);
friend Expr operator+(Var x, Var y); void operator/=(double a);
friend Expr operator+(Var x, double a); Expr operator+(const Expr &rhs);
friend Expr operator+(double a, Var x); Expr operator-(const Expr &rhs);
friend Expr operator-(const Expr& x, const Expr& y); };
friend Expr operator-(const Expr& x); } // namespace sv
friend Expr operator-(Var x);
friend Expr operator-(Var x, Var y);
friend Expr operator-(Var x, double a);
friend Expr operator-(double a, Var x);
friend Expr operator*(double a, Var x);
friend Expr operator*(Var x, double a);
friend Expr operator*(const Expr& x, double a);
friend Expr operator*(double a, const Expr& x);
friend Expr operator/(Var x, double a);
friend Expr operator/(const Expr& x, double a);
Expr operator=(const Expr& rhs);
void operator+=(const Expr& expr);
void operator-=(const Expr& expr);
void operator*=(double mult);
void operator/=(double a);
Expr operator+(const Expr& rhs);
Expr operator-(const Expr& rhs);
};
}
+42 -46
View File
@@ -3,60 +3,56 @@
namespace sv { namespace sv {
class LinSolver { class LinSolver {
public: public:
friend class Model; friend class Model;
LinSolver(); LinSolver();
~LinSolver(); ~LinSolver();
LinSolver(const LinSolver& solver); LinSolver(const LinSolver &solver);
LinSolver& operator=(const LinSolver& solver); LinSolver &operator=(const LinSolver &solver);
Var* addVars(int col, VarType type); Var *addVars(int col, VarType type);
Var& getVar(int idx); Var &getVar(int idx);
void addConstr(const Expr& expr, ConstrOper sense, double rhs); void addConstr(const Expr &expr, ConstrOper sense, double rhs);
void setObjective(Expr obje, int sense = MDL_MAXIMIZE); void setObjective(Expr obje, int sense = MDL_MAXIMIZE);
void print(); void print();
double get(DoubleAttr attr); double get(DoubleAttr attr);
int get(IntAttr attr); int get(IntAttr attr);
rtn optimize(); rtn optimize();
protected:
double _simplex(); protected:
rtn _pivot(std::pair<size_t, size_t>& p); double _simplex();
rtn feasible_solution(); rtn _pivot(std::pair<size_t, size_t> &p);
void _gaussian(std::pair<size_t, size_t> p); rtn feasible_solution();
void _gaussian(std::pair<size_t, size_t> p);
std::vector<std::pair<int, Var*>> vars; std::vector<std::pair<int, Var *>> vars;
matrix table; matrix table;
matrix ope_table; matrix ope_table;
size_t cn, bn; size_t cn, bn;
std::vector<int> basic; std::vector<int> basic;
rtn rtn_; rtn rtn_;
double obj_; double obj_;
int sense; int sense;
}; };
class Model class Model {
{ public:
public: Model();
Model(); rtn optimize();
rtn optimize();
Var* addVars(int col, VarType type);
void addConstr(const Expr& expr, ConstrOper sense, double rhs);
void setObjective(Expr obje, int sense = MDL_MAXIMIZE);
double get(DoubleAttr attr);
int get(IntAttr attr);
private:
LinSolver solver;
};
}
Var *addVars(int col, VarType type);
void addConstr(const Expr &expr, ConstrOper sense, double rhs);
void setObjective(Expr obje, int sense = MDL_MAXIMIZE);
double get(DoubleAttr attr);
int get(IntAttr attr);
private:
LinSolver solver;
};
} // namespace sv
Executable
BIN
View File
Binary file not shown.
+44 -71
View File
@@ -2,17 +2,9 @@
#include <iostream> #include <iostream>
using namespace sv; using namespace sv;
Var::Var(double coef, VarType type_) : col(0), val(0), coeffs(coef), type(type_){};
Var::Var(double coef, VarType type_) : double Var::get(DoubleAttr attr) {
col(0),
val(0),
coeffs(coef),
type(type_)
{
};
double Var::get(DoubleAttr attr)
{
switch (attr) { switch (attr) {
case DoubleAttr::Coeff: case DoubleAttr::Coeff:
return coeffs; return coeffs;
@@ -22,13 +14,11 @@ double Var::get(DoubleAttr attr)
return -1; return -1;
} }
int Var::get(IntAttr attr) int Var::get(IntAttr attr) {
{
return col; return col;
} }
Expr sv::operator+(const Expr& x, const Expr& y) Expr sv::operator+(const Expr &x, const Expr &y) {
{
Expr exp; Expr exp;
exp.coeffs.resize(std::max(x.coeffs.size(), y.coeffs.size()), 0); exp.coeffs.resize(std::max(x.coeffs.size(), y.coeffs.size()), 0);
for (int c = 0; c < exp.coeffs.size(); c++) { for (int c = 0; c < exp.coeffs.size(); c++) {
@@ -43,13 +33,11 @@ Expr sv::operator+(const Expr& x, const Expr& y)
return exp; return exp;
} }
Expr sv::operator+(const Expr& x) Expr sv::operator+(const Expr &x) {
{
return x; return x;
} }
Expr sv::operator+(Var x, Var y) Expr sv::operator+(Var x, Var y) {
{
Expr exp; Expr exp;
exp.coeffs.resize(std::max(x.get(IntAttr::NumCol) + 1, y.get(IntAttr::NumCol) + 1), 0); exp.coeffs.resize(std::max(x.get(IntAttr::NumCol) + 1, y.get(IntAttr::NumCol) + 1), 0);
exp.coeffs.at(x.get(IntAttr::NumCol)) = x.get(DoubleAttr::Coeff); exp.coeffs.at(x.get(IntAttr::NumCol)) = x.get(DoubleAttr::Coeff);
@@ -57,21 +45,19 @@ Expr sv::operator+(Var x, Var y)
return exp; return exp;
} }
Expr sv::operator+(Var x, double a) Expr sv::operator+(Var x, double a) {
{
Expr exp; Expr exp;
exp.coeffs.resize(x.get(IntAttr::NumCol) + 1); exp.coeffs.resize(x.get(IntAttr::NumCol) + 1, 0);
exp.coeffs.at(x.get(IntAttr::NumCol)) = x.get(DoubleAttr::Coeff);
exp.constant = a; exp.constant = a;
return exp; return exp;
} }
Expr sv::operator+(double a, Var x) Expr sv::operator+(double a, Var x) {
{
return x + a; return x + a;
} }
Expr sv::operator-(const Expr& x, const Expr& y) Expr sv::operator-(const Expr &x, const Expr &y) {
{
Expr exp; Expr exp;
exp.coeffs.resize(std::max(x.coeffs.size(), y.coeffs.size()), 0); exp.coeffs.resize(std::max(x.coeffs.size(), y.coeffs.size()), 0);
for (int c = 0; c < exp.coeffs.size(); c++) { for (int c = 0; c < exp.coeffs.size(); c++) {
@@ -79,15 +65,14 @@ Expr sv::operator-(const Expr& x, const Expr& y)
exp.coeffs.at(c) = x.coeffs.at(c) - y.coeffs.at(c); exp.coeffs.at(c) = x.coeffs.at(c) - y.coeffs.at(c);
} }
else { else {
exp.coeffs.at(c) = c < x.coeffs.size() ? x.coeffs.at(c) : y.coeffs.at(c); exp.coeffs.at(c) = c < x.coeffs.size() ? x.coeffs.at(c) : -y.coeffs.at(c);
} }
} }
exp.constant = x.constant + y.constant; exp.constant = x.constant - y.constant;
return exp; return exp;
} }
Expr sv::operator-(const Expr& x) Expr sv::operator-(const Expr &x) {
{
Expr expr(x); Expr expr(x);
for (int c = 0; c < expr.coeffs.size(); c++) { for (int c = 0; c < expr.coeffs.size(); c++) {
expr.coeffs.at(c) = -expr.coeffs.at(c); expr.coeffs.at(c) = -expr.coeffs.at(c);
@@ -96,13 +81,11 @@ Expr sv::operator-(const Expr& x)
return expr; return expr;
} }
Expr sv::operator-(Var x) Expr sv::operator-(Var x) {
{
return -Expr(x); return -Expr(x);
} }
Expr sv::operator-(Var x, Var y) Expr sv::operator-(Var x, Var y) {
{
Expr exp; Expr exp;
exp.coeffs.resize(std::max(x.get(IntAttr::NumCol) + 1, y.get(IntAttr::NumCol) + 1), 0); exp.coeffs.resize(std::max(x.get(IntAttr::NumCol) + 1, y.get(IntAttr::NumCol) + 1), 0);
exp.coeffs.at(x.get(IntAttr::NumCol)) = x.get(DoubleAttr::Coeff); exp.coeffs.at(x.get(IntAttr::NumCol)) = x.get(DoubleAttr::Coeff);
@@ -110,31 +93,30 @@ Expr sv::operator-(Var x, Var y)
return exp; return exp;
} }
Expr sv::operator-(Var x, double a) Expr sv::operator-(Var x, double a) {
{ Expr exp;
return x - Var(a); exp.coeffs.resize(x.get(IntAttr::NumCol) + 1, 0);
exp.coeffs.at(x.get(IntAttr::NumCol)) = x.get(DoubleAttr::Coeff);
exp.constant = -a;
return exp;
} }
Expr sv::operator-(double a, Var x) Expr sv::operator-(double a, Var x) {
{ return a + (-x);
return x - a;
} }
Expr sv::operator*(double a, Var x) Expr sv::operator*(double a, Var x) {
{
Expr exp; Expr exp;
exp.coeffs.resize(x.get(IntAttr::NumCol) + 1, 0); exp.coeffs.resize(x.get(IntAttr::NumCol) + 1, 0);
exp.coeffs.at(x.get(IntAttr::NumCol)) = a * x.get(DoubleAttr::Coeff); exp.coeffs.at(x.get(IntAttr::NumCol)) = a * x.get(DoubleAttr::Coeff);
return exp; return exp;
} }
Expr sv::operator*(Var x, double a) Expr sv::operator*(Var x, double a) {
{
return a * x; return a * x;
} }
Expr sv::operator*(const Expr& x, double a) Expr sv::operator*(const Expr &x, double a) {
{
Expr exp = x; Expr exp = x;
for (int c = 0; c < exp.coeffs.size(); c++) { for (int c = 0; c < exp.coeffs.size(); c++) {
exp.coeffs.at(c) *= a; exp.coeffs.at(c) *= a;
@@ -143,18 +125,15 @@ Expr sv::operator*(const Expr& x, double a)
return exp; return exp;
} }
Expr sv::operator*(double a, const Expr& x) Expr sv::operator*(double a, const Expr &x) {
{
return x * a; return x * a;
} }
Expr sv::operator/(Var x, double a) Expr sv::operator/(Var x, double a) {
{
return Expr(x) / a; return Expr(x) / a;
} }
Expr sv::operator/(const Expr& x, double a) Expr sv::operator/(const Expr &x, double a) {
{
Expr exp = x; Expr exp = x;
for (int c = 0; c < exp.coeffs.size(); c++) { for (int c = 0; c < exp.coeffs.size(); c++) {
exp.coeffs.at(c) /= a; exp.coeffs.at(c) /= a;
@@ -163,25 +142,24 @@ Expr sv::operator/(const Expr& x, double a)
return exp; return exp;
} }
Expr::Expr(double constant) Expr::Expr(double constant) : constant(constant) {}
:constant(constant)
{
}
Expr::Expr(Var var, double coeff) Expr::Expr(Var var, double coeff) {
{
this->coeffs.resize(var.col + 1); this->coeffs.resize(var.col + 1);
this->coeffs.at(var.col) = coeff; this->coeffs.at(var.col) = coeff;
this->constant = 0; this->constant = 0;
} }
Expr Expr::operator=(const Expr& rhs) Expr &Expr::operator=(const Expr &rhs) {
{ if (this != &rhs) {
constant = rhs.constant;
coeffs = rhs.coeffs;
vars = rhs.vars;
}
return *this; return *this;
} }
void Expr::operator+=(const Expr& expr) void Expr::operator+=(const Expr &expr) {
{
coeffs.resize(std::max(coeffs.size(), expr.coeffs.size())); coeffs.resize(std::max(coeffs.size(), expr.coeffs.size()));
for (int c = 0; c < expr.coeffs.size(); c++) { for (int c = 0; c < expr.coeffs.size(); c++) {
coeffs.at(c) += expr.coeffs.at(c); coeffs.at(c) += expr.coeffs.at(c);
@@ -189,8 +167,7 @@ void Expr::operator+=(const Expr& expr)
constant += expr.constant; constant += expr.constant;
} }
void Expr::operator-=(const Expr& expr) void Expr::operator-=(const Expr &expr) {
{
coeffs.resize(std::max(coeffs.size(), expr.coeffs.size())); coeffs.resize(std::max(coeffs.size(), expr.coeffs.size()));
for (int c = 0; c < expr.coeffs.size(); c++) { for (int c = 0; c < expr.coeffs.size(); c++) {
coeffs.at(c) -= expr.coeffs.at(c); coeffs.at(c) -= expr.coeffs.at(c);
@@ -198,24 +175,21 @@ void Expr::operator-=(const Expr& expr)
constant -= expr.constant; constant -= expr.constant;
} }
void Expr::operator*=(double mult) void Expr::operator*=(double mult) {
{
for (int c = 0; c < coeffs.size(); c++) { for (int c = 0; c < coeffs.size(); c++) {
coeffs.at(c) *= mult; coeffs.at(c) *= mult;
} }
constant *= mult; constant *= mult;
} }
void Expr::operator/=(double a) void Expr::operator/=(double a) {
{
for (int c = 0; c < coeffs.size(); c++) { for (int c = 0; c < coeffs.size(); c++) {
coeffs.at(c) /= a; coeffs.at(c) /= a;
} }
constant /= a; constant /= a;
} }
Expr Expr::operator+(const Expr& rhs) Expr Expr::operator+(const Expr &rhs) {
{
coeffs.resize(std::max(coeffs.size(), rhs.coeffs.size())); coeffs.resize(std::max(coeffs.size(), rhs.coeffs.size()));
for (int c = 0; c < rhs.coeffs.size(); c++) { for (int c = 0; c < rhs.coeffs.size(); c++) {
coeffs.at(c) += rhs.coeffs.at(c); coeffs.at(c) += rhs.coeffs.at(c);
@@ -224,8 +198,7 @@ Expr Expr::operator+(const Expr& rhs)
return *this; return *this;
} }
Expr Expr::operator-(const Expr& rhs) Expr Expr::operator-(const Expr &rhs) {
{
coeffs.resize(std::max(coeffs.size(), rhs.coeffs.size())); coeffs.resize(std::max(coeffs.size(), rhs.coeffs.size()));
for (int c = 0; c < rhs.coeffs.size(); c++) { for (int c = 0; c < rhs.coeffs.size(); c++) {
coeffs.at(c) -= rhs.coeffs.at(c); coeffs.at(c) -= rhs.coeffs.at(c);
+20 -21
View File
@@ -6,36 +6,35 @@
using namespace std; using namespace std;
using namespace sv; using namespace sv;
int main(int argc, char *argv[]) {
int main(int argc, char* argv[])
{
Model mdl; Model mdl;
Var* int_var = mdl.addVars(3, VarType::INTEGER);
Var* con_var = mdl.addVars(2, VarType::CONTINUOUS);
mdl.addConstr(2 * int_var[0] + int_var[1], ConstrOper::LESS_EQUAL, 10); Var *x = mdl.addVars(3, VarType::INTEGER);
mdl.addConstr(3 * int_var[0] + 6 * int_var[1], ConstrOper::LESS_EQUAL, 40); Var *y = mdl.addVars(2, VarType::CONTINUOUS);
mdl.addConstr(3 * int_var[0] + 6 * int_var[1] + 4 * int_var[2], ConstrOper::LESS_EQUAL, 50);
mdl.addConstr(2.3 * con_var[0] + 2.6 * con_var[1], ConstrOper::LESS_EQUAL, 80);
mdl.addConstr(con_var[0] + 2 * con_var[1], ConstrOper::LESS_EQUAL, 70);
mdl.setObjective(100 * int_var[0] + 150 * int_var[1] + 120 * int_var[2] + 82.6 * con_var[0] + 90.4 * con_var[1], MDL_MAXIMIZE);
switch(mdl.optimize()) { mdl.addConstr(3 * x[0] + 6 * x[1], ConstrOper::LESS_EQUAL, 28);
case OPTIMAL: mdl.addConstr(3 * x[0] + 6 * x[1] + 4 * x[2], ConstrOper::LESS_EQUAL, 30);
cout << "OPTIMAL SOLUTION: " << mdl.get(DoubleAttr::Obj) << endl; mdl.addConstr(3 * y[0] + 2 * y[1], ConstrOper::LESS_EQUAL, 80);
mdl.addConstr(y[0] + 2 * y[1], ConstrOper::LESS_EQUAL, 70);
mdl.addConstr(2 * x[1] + x[2], ConstrOper::EQUAL, 7);
mdl.setObjective(100 * x[0] + 150 * x[1] + 120 * x[2] + 80 * y[0] + 90 * y[1] + 15,
MDL_MAXIMIZE);
switch (mdl.optimize()) {
case OPTIMAL:
cout << "OPTIMAL SOLUTION: " << mdl.get(DoubleAttr::Obj) << endl;
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
cout << "integer var [" << i << "] : " << int_var[i].get(DoubleAttr::X) << endl; cout << "integer var [" << i << "] : " << x[i].get(DoubleAttr::X) << endl;
} }
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
cout << "continuous var [" << i << "] : " << con_var[i].get(DoubleAttr::X) << endl; cout << "continuous var [" << i << "] : " << y[i].get(DoubleAttr::X) << endl;
} }
break; break;
case INFEASIBLE: case INFEASIBLE:
cout << "INFEASIBLE MODEL" << endl; cout << "INFEASIBLE MODEL" << endl;
break; break;
case UNBOUNDED: case UNBOUNDED:
cout << "UNBOUNDED SOLUTION" << endl; cout << "UNBOUNDED SOLUTION" << endl;
break; break;
default: default:
assert(false); assert(false);
+212 -195
View File
@@ -5,68 +5,53 @@
#include <cassert> #include <cassert>
#include <algorithm> #include <algorithm>
#include <stack> #include <stack>
#include <cassert> #include <cmath>
#include <cstring>
#include <cfloat> #include <cfloat>
#include <math.h> #include <limits>
using namespace sv; using namespace sv;
using std::make_pair;
using std::pair;
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::make_pair;
using std::pair;
using std::vector; using std::vector;
struct Node {
struct Node
{
LinSolver solver; LinSolver solver;
double lower_bound; double bound; // LP bound in true-objective space
double upper_bound;
}; };
LinSolver::LinSolver() : LinSolver::LinSolver() : obj_(0), rtn_(LOADED), cn(0), bn(1), sense(0) {}
obj_(0),
rtn_(LOADED),
cn(0),
bn(1),
sense(0)
{
} sv::LinSolver::~LinSolver() {
for (auto &var : vars) {
sv::LinSolver::~LinSolver()
{
for (auto& var : vars) {
delete[] var.second; delete[] var.second;
} }
} }
sv::LinSolver::LinSolver(const LinSolver& solver) sv::LinSolver::LinSolver(const LinSolver &solver) {
{
*this = solver; *this = solver;
} }
LinSolver& sv::LinSolver::operator=(const LinSolver& solver) LinSolver &sv::LinSolver::operator=(const LinSolver &solver) {
{
if (this == &solver) { if (this == &solver) {
return *this; return *this;
} }
for (auto& var : vars) { for (auto &var : vars) {
delete[] var.second; delete[] var.second;
} }
vars.clear(); vars.clear();
vars.reserve(solver.vars.size()); vars.reserve(solver.vars.size());
for (auto& var : solver.vars) { for (auto &var : solver.vars) {
vars.push_back(std::make_pair(var.first, new Var[var.first])); vars.push_back(std::make_pair(var.first, new Var[var.first]));
for (int i = 0; i < var.first; i++) { for (int i = 0; i < var.first; i++) {
vars.back().second[i] = var.second[i]; vars.back().second[i] = var.second[i];
} }
} }
cn = solver.cn; cn = solver.cn;
bn = solver.bn;
table = solver.table; table = solver.table;
cn = solver.cn, bn = solver.bn;
basic = solver.basic; basic = solver.basic;
rtn_ = solver.rtn_; rtn_ = solver.rtn_;
obj_ = solver.obj_; obj_ = solver.obj_;
@@ -75,85 +60,73 @@ LinSolver& sv::LinSolver::operator=(const LinSolver& solver)
return *this; return *this;
} }
Var* LinSolver::addVars(int num, VarType type) Var *LinSolver::addVars(int num, VarType type) {
{ Var *var = new Var[num];
Var* var = new Var[num];
for (int c = 0; c < num; c++) { for (int c = 0; c < num; c++) {
var[c].col = c + cn, var[c].type = type; var[c].col = c + cn;
var[c].type = type;
} }
vars.push_back(std::make_pair(num, var)); vars.push_back(std::make_pair(num, var));
cn += num; cn += num;
return vars.back().second; return vars.back().second;
} }
Var& sv::LinSolver::getVar(int idx) Var &sv::LinSolver::getVar(int idx) {
{ assert(idx >= 0 && idx < static_cast<int>(cn));
assert(idx >= 0 && idx < cn);
static Var err_var; static Var err_var;
for (auto& var : vars) { int offset = 0;
if (var.first <= idx) { for (auto &var : vars) {
idx -= var.first; if (idx < offset + var.first) {
} return var.second[idx - offset];
else {
return var.second[idx];
} }
offset += var.first;
} }
return err_var; return err_var;
} }
void LinSolver::addConstr(const Expr& expr, ConstrOper sense, double rhs) void LinSolver::addConstr(const Expr &expr, ConstrOper sense, double rhs) {
{ if (sense == ConstrOper::EQUAL) {
addConstr(expr, ConstrOper::LESS_EQUAL, rhs);
addConstr(expr, ConstrOper::GREATER_EQUAL, rhs);
return;
}
bn++;
if (sense == ConstrOper::LESS_EQUAL) { if (sense == ConstrOper::LESS_EQUAL) {
bn++;
table.push_back(vector<double>(1, rhs - expr.constant)); table.push_back(vector<double>(1, rhs - expr.constant));
table.back().insert(table.back().end(), expr.coeffs.begin(), expr.coeffs.end()); table.back().insert(table.back().end(), expr.coeffs.begin(), expr.coeffs.end());
} }
else if (sense == ConstrOper::GREATER_EQUAL) { else {
bn++;
table.push_back(vector<double>(1, expr.constant - rhs)); table.push_back(vector<double>(1, expr.constant - rhs));
for (int coeff : expr.coeffs) { for (double coeff : expr.coeffs) {
table.back().push_back(-coeff); table.back().push_back(-coeff);
} }
} }
else {
addConstr(expr, ConstrOper::LESS_EQUAL, rhs); for (size_t c = table.back().size(); c <= cn; c++) {
addConstr(expr, ConstrOper::GREATER_EQUAL, rhs);
}
for (int c = table.back().size(); c <= cn; c++) {
table.back().push_back(0); table.back().push_back(0);
} }
} }
void LinSolver::setObjective(Expr obje, int _sense) void LinSolver::setObjective(Expr obje, int _sense) {
{
assert(_sense == 1 || _sense == -1); assert(_sense == 1 || _sense == -1);
if (sense == 0) { if (sense == 0) {
table.insert(table.begin(), obje.coeffs); table.insert(table.begin(), obje.coeffs);
table.front().insert(table.front().begin(), -obje.constant);
for (int c = obje.coeffs.size() + 1; c <= cn; c++) {
table.front().push_back(0);
}
} }
else { else {
table.front().front() = -obje.constant; table.front() = obje.coeffs;
for (int col = 0; col < cn; col++) {
if (col < obje.coeffs.size()) {
table.front().at(col + 1) = obje.coeffs.at(col);
}
else {
table.front().at(col) = 0;
}
}
} }
for (int row = 0; row < table.front().size(); row++) { table.front().insert(table.front().begin(), -obje.constant);
table.front().at(row) = _sense * table.front().at(row); for (size_t c = obje.coeffs.size() + 1; c <= cn; c++) {
table.front().push_back(0);
}
for (size_t col = 0; col < table.front().size(); col++) {
table.front().at(col) = _sense * table.front().at(col);
} }
sense = _sense; sense = _sense;
} }
rtn LinSolver::optimize() rtn LinSolver::optimize() {
{
assert(sense); assert(sense);
ope_table = table; ope_table = table;
rtn_ = LOADED; rtn_ = LOADED;
@@ -163,18 +136,22 @@ rtn LinSolver::optimize()
} }
if (rtn_ == OPTIMAL) { if (rtn_ == OPTIMAL) {
cn = ope_table.front().size() - bn; const size_t num_vars = ope_table.front().size() - bn;
for (int row = 1; row < bn; row++) { cn = num_vars;
if (basic.at(row - 1) - 1 < cn) { for (size_t i = 0; i < num_vars; i++) {
getVar(basic.at(row - 1) - 1).val = ope_table.at(row).front(); getVar(static_cast<int>(i)).val = 0;
}
for (size_t row = 1; row < bn; row++) {
int var_idx = basic.at(row - 1) - 1;
if (var_idx >= 0 && static_cast<size_t>(var_idx) < num_vars) {
getVar(var_idx).val = ope_table.at(row).front();
} }
} }
} }
return rtn_; return rtn_;
} }
void LinSolver::print() void LinSolver::print() {
{
for (size_t row = 0; row < ope_table.size(); row++) { for (size_t row = 0; row < ope_table.size(); row++) {
for (size_t col = 0; col < ope_table.front().size(); col++) { for (size_t col = 0; col < ope_table.front().size(); col++) {
cout << ope_table.at(row).at(col) << "\t"; cout << ope_table.at(row).at(col) << "\t";
@@ -183,163 +160,206 @@ void LinSolver::print()
} }
} }
Model::Model() Model::Model() {}
{
namespace {
bool is_integer_type(VarType type) {
return type == VarType::INTEGER || type == VarType::BINARY;
} }
rtn Model::optimize() bool is_fractional(double val, double eps = 1e-6) {
{ return fabs(val - std::round(val)) > eps;
}
bool is_better(double candidate, double incumbent, int sense, double eps = 1e-10) {
if (sense == MDL_MAXIMIZE) {
return candidate > incumbent + eps;
}
return candidate < incumbent - eps;
}
} // namespace
rtn Model::optimize() {
solver.optimize(); solver.optimize();
if (solver.rtn_ != OPTIMAL) { if (solver.rtn_ != OPTIMAL) {
return solver.rtn_; return solver.rtn_;
} }
double global_upper_bound = solver.obj_, global_lower_bound = 0; const int sense = solver.sense;
const int num_vars = solver.get(IntAttr::NumVars);
std::stack<Node> list_; bool has_integer = false;
for (int i = 0; i < num_vars; i++) {
Node root_node = { solver, 0, solver.obj_ }; if (is_integer_type(solver.getVar(i).type)) {
Node incumbent_node = root_node; has_integer = true;
break;
}
}
if (!has_integer) {
return solver.rtn_;
}
double best_obj = (sense == MDL_MAXIMIZE) ? -std::numeric_limits<double>::infinity()
: std::numeric_limits<double>::infinity();
bool found_integer = false;
Node incumbent_node{solver, solver.get(DoubleAttr::Obj)};
std::stack<Node> open_nodes;
open_nodes.push(Node{solver, solver.get(DoubleAttr::Obj)});
while (!open_nodes.empty()) {
Node current_node = std::move(open_nodes.top());
open_nodes.pop();
if (found_integer && !is_better(current_node.bound, best_obj, sense)) {
continue;
}
list_.push(root_node);
while (list_.size() && global_upper_bound - global_lower_bound > 1e-10) {
Node current_node = list_.top();
list_.pop();
current_node.solver.optimize(); current_node.solver.optimize();
if (current_node.solver.get(IntAttr::Status) != OPTIMAL) {
if (current_node.solver.get(IntAttr::Status) == OPTIMAL) { continue;
int branch_var_index = -1; }
for (int i = 0; i < current_node.solver.get(IntAttr::NumVars); i++) { const double lp_obj = current_node.solver.get(DoubleAttr::Obj);
if (current_node.solver.getVar(i).type == VarType::INTEGER) { current_node.bound = lp_obj;
if (fabs(int(current_node.solver.getVar(i).val) - current_node.solver.getVar(i).val) > 1e-10) {
branch_var_index = i; if (found_integer && !is_better(lp_obj, best_obj, sense)) {
break; continue;
} }
}
int branch_var_index = -1;
for (int i = 0; i < current_node.solver.get(IntAttr::NumVars); i++) {
Var &var = current_node.solver.getVar(i);
if (!is_integer_type(var.type)) {
continue;
} }
if (is_fractional(var.val)) {
if (branch_var_index == -1) { if (branch_var_index < 0) {
current_node.lower_bound = current_node.solver.obj_; branch_var_index = i;
current_node.upper_bound = current_node.solver.obj_;
if (current_node.lower_bound > global_lower_bound) {
global_lower_bound = current_node.lower_bound;
incumbent_node = current_node;
} }
} }
else { else {
if (current_node.upper_bound >= global_lower_bound) { // Snap near-integer values so later checks / output stay clean
const Var& branch_var = current_node.solver.getVar(branch_var_index); var.val = std::round(var.val);
int left_var_bound = branch_var.val;
int right_var_bound = branch_var.val + 1;
Node left_node = current_node;
left_node.solver.addConstr(branch_var, ConstrOper::LESS_EQUAL, left_var_bound);
list_.push(left_node);
Node right_node = current_node;
right_node.solver.addConstr(branch_var, ConstrOper::GREATER_EQUAL, right_var_bound);
list_.push(right_node);
}
} }
} }
if (branch_var_index == -1) {
if (!found_integer || is_better(lp_obj, best_obj, sense)) {
best_obj = lp_obj;
found_integer = true;
incumbent_node = current_node;
}
continue;
}
const Var &branch_var = current_node.solver.getVar(branch_var_index);
const int left_bound = static_cast<int>(std::floor(branch_var.val));
const int right_bound = left_bound + 1;
Node left_node = current_node;
left_node.solver.addConstr(branch_var, ConstrOper::LESS_EQUAL, left_bound);
open_nodes.push(std::move(left_node));
Node right_node = current_node;
right_node.solver.addConstr(branch_var, ConstrOper::GREATER_EQUAL, right_bound);
open_nodes.push(std::move(right_node));
} }
if (!found_integer) {
return solver.rtn_ = INFEASIBLE;
}
solver.rtn_ = incumbent_node.solver.rtn_; solver.rtn_ = incumbent_node.solver.rtn_;
solver.obj_ = incumbent_node.solver.obj_; solver.obj_ = incumbent_node.solver.obj_;
for (int i = 0; i < solver.cn; i++) { for (int i = 0; i < solver.get(IntAttr::NumVars); i++) {
solver.getVar(i).val = incumbent_node.solver.getVar(i).val; solver.getVar(i).val = incumbent_node.solver.getVar(i).val;
} }
return solver.rtn_; return solver.rtn_;
} }
Var* sv::Model::addVars(int col, VarType type) Var *sv::Model::addVars(int col, VarType type) {
{
return solver.addVars(col, type); return solver.addVars(col, type);
} }
void sv::Model::addConstr(const Expr& expr, ConstrOper sense, double rhs) void sv::Model::addConstr(const Expr &expr, ConstrOper sense, double rhs) {
{ solver.addConstr(expr, sense, rhs);
return solver.addConstr(expr, sense, rhs);
} }
void sv::Model::setObjective(Expr obje, int sense) void sv::Model::setObjective(Expr obje, int sense) {
{ solver.setObjective(obje, sense);
return solver.setObjective(obje, sense);
} }
double sv::Model::get(DoubleAttr attr) double sv::Model::get(DoubleAttr attr) {
{
return solver.get(attr); return solver.get(attr);
} }
int sv::Model::get(IntAttr attr) int sv::Model::get(IntAttr attr) {
{
return solver.get(attr); return solver.get(attr);
} }
double LinSolver::get(DoubleAttr attr) double LinSolver::get(DoubleAttr attr) {
{
return -sense * obj_; return -sense * obj_;
} }
int LinSolver::get(IntAttr attr) int LinSolver::get(IntAttr attr) {
{
switch (attr) { switch (attr) {
case IntAttr::NumVars: case IntAttr::NumVars:
return cn; return static_cast<int>(cn);
case IntAttr::Status: case IntAttr::Status:
return rtn_; return rtn_;
default:
return -1;
} }
return -1;
} }
double LinSolver::_simplex() double LinSolver::_simplex() {
{
pair<size_t, size_t> t; pair<size_t, size_t> t;
while (1) { while (true) {
rtn_ = _pivot(t); rtn_ = _pivot(t);
if (rtn_ == OPTIMAL || rtn_ == UNBOUNDED) { if (rtn_ == OPTIMAL || rtn_ == UNBOUNDED) {
break; break;
} }
_gaussian(t); _gaussian(t);
} }
return obj_ = ope_table.front().front(); return obj_ = ope_table.front().front();
} }
rtn LinSolver::feasible_solution() rtn LinSolver::feasible_solution() {
{ for (size_t row = 1; row < bn; row++) {
for (int row = 1; row < bn; row++) {
ope_table.front().push_back(0); ope_table.front().push_back(0);
for (int col = 1; col < bn; col++) { for (size_t col = 1; col < bn; col++) {
ope_table.at(row).push_back(col == row ? 1 : 0); ope_table.at(row).push_back(col == row ? 1.0 : 0.0);
} }
} }
cn = ope_table.front().size(); cn = ope_table.front().size();
basic.clear(); basic.clear();
basic.reserve(bn - 1);
for (size_t i = 1; i < bn; i++) { for (size_t i = 1; i < bn; i++) {
basic.push_back(cn - bn + i); basic.push_back(static_cast<int>(cn - bn + i));
} }
// === 判断初始解是否为可行解 === // Check whether the initial basic solution is feasible
bool initial_feasible = true; bool initial_feasible = true;
for (int row = 1; row < bn; row++) { for (size_t row = 1; row < bn; row++) {
if (ope_table.at(row).front() < 0) { if (ope_table.at(row).front() < 0) {
initial_feasible = false; initial_feasible = false;
break; break;
} }
} }
// === 构造初始可行解 === // Two-phase method when the initial basis is infeasible
if (!initial_feasible) { if (!initial_feasible) {
vector<double> coeff = ope_table.front(); vector<double> coeff = ope_table.front();
ope_table.front() = vector<double>(cn, .0); ope_table.front() = vector<double>(cn, 0.0);
ope_table.front().push_back(1); ope_table.front().push_back(1);
pair<size_t, size_t> t = { -1 ,cn }; pair<size_t, size_t> t = {static_cast<size_t>(-1), cn};
for (int row = 1; row < bn; row++) { for (size_t row = 1; row < bn; row++) {
ope_table.at(row).push_back(-1); ope_table.at(row).push_back(-1);
if (t.first == -1 || ope_table.at(row).front() < ope_table.at(t.first).front()) { if (t.first == static_cast<size_t>(-1) ||
ope_table.at(row).front() < ope_table.at(t.first).front()) {
t.first = row; t.first = row;
} }
} }
@@ -349,29 +369,30 @@ rtn LinSolver::feasible_solution()
return rtn_ = INFEASIBLE; return rtn_ = INFEASIBLE;
} }
rtn_ = LOADED; rtn_ = LOADED;
// if the x0 in B, we should pivot it.
auto iter = find(basic.begin(), basic.end(), cn); // If artificial variable remains basic, pivot it out
auto iter = find(basic.begin(), basic.end(), static_cast<int>(cn));
if (iter != basic.end()) { if (iter != basic.end()) {
for (int col = 1; col < ope_table.front().size(); col++) { for (size_t col = 1; col < ope_table.front().size(); col++) {
if (fabs(ope_table.front().at(col)) > 1e-10) { if (fabs(ope_table.front().at(col)) > 1e-10) {
t = make_pair(iter - basic.begin() + 1, col); t = make_pair(static_cast<size_t>(iter - basic.begin() + 1), col);
_gaussian(t); _gaussian(t);
break; break;
} }
} }
} }
for (int row = 0; row < bn; row++) { for (size_t row = 0; row < bn; row++) {
ope_table.at(row).pop_back(); ope_table.at(row).pop_back();
} }
// recover the coefficient line // Restore original objective row and re-express in current basis
for (int col = 0; col < cn; col++) { for (size_t col = 0; col < cn; col++) {
ope_table.front().at(col) = coeff.at(col); ope_table.front().at(col) = coeff.at(col);
} }
for (int row = 1; row <= basic.size(); row++) { for (size_t row = 1; row <= basic.size(); row++) {
int norm = ope_table.front().at(basic.at(row - 1)); double norm = ope_table.front().at(basic.at(row - 1));
for (int col = 0; col < cn; col++) { for (size_t col = 0; col < cn; col++) {
ope_table.front().at(col) -= norm * ope_table.at(row).at(col); ope_table.front().at(col) -= norm * ope_table.at(row).at(col);
} }
} }
@@ -379,15 +400,15 @@ rtn LinSolver::feasible_solution()
return rtn_; return rtn_;
} }
rtn LinSolver::_pivot(pair<size_t, size_t>& p) rtn LinSolver::_pivot(pair<size_t, size_t> &p) {
{
p = make_pair(0, 0); p = make_pair(0, 0);
double cmin = DBL_MAX; double cmin = DBL_MAX;
vector<double> coef = ope_table.front(); const vector<double> &coef = ope_table.front();
// === 非主轴元素中找最小值 === // Entering variable: most negative reduced cost
for (size_t col = 1; col < coef.size(); col++) { for (size_t col = 1; col < coef.size(); col++) {
if (cmin > coef.at(col) && find(basic.begin(), basic.end(), col) == basic.end()) { if (cmin > coef.at(col) &&
find(basic.begin(), basic.end(), static_cast<int>(col)) == basic.end()) {
cmin = coef.at(col); cmin = coef.at(col);
p.second = col; p.second = col;
} }
@@ -395,51 +416,47 @@ rtn LinSolver::_pivot(pair<size_t, size_t>& p)
if (cmin >= 0) { if (cmin >= 0) {
return OPTIMAL; return OPTIMAL;
} }
double bmin = DBL_MAX; double bmin = DBL_MAX;
for (size_t row = 1; row < bn; row++) { for (size_t row = 1; row < bn; row++) {
double tmp = ope_table.at(row).front() / ope_table.at(row).at(p.second); const double pivot_col = ope_table.at(row).at(p.second);
if (ope_table.at(row).at(p.second) > 0 && bmin > tmp) { if (pivot_col > 0) {
bmin = tmp; const double tmp = ope_table.at(row).front() / pivot_col;
p.first = row; if (bmin > tmp) {
bmin = tmp;
p.first = row;
}
} }
} }
if (abs(bmin - DBL_MAX) < 1e-10) { if (bmin >= DBL_MAX / 2) {
return UNBOUNDED; return UNBOUNDED;
} }
for (auto iter = basic.begin(); iter != basic.end(); iter++) { basic.at(p.first - 1) = static_cast<int>(p.second);
if (ope_table.at(p.first).at(*iter) != 0) {
*iter = p.second;
break;
}
}
assert(basic.at(p.first - 1) == p.second);
return PIVOT; return PIVOT;
} }
void LinSolver::_gaussian(pair<size_t, size_t> p) void LinSolver::_gaussian(pair<size_t, size_t> p) {
{
size_t x = p.first, y = p.second; size_t x = p.first, y = p.second;
// === 主行归一化 === // Normalize pivot row
double norm = ope_table.at(x).at(y); double norm = ope_table.at(x).at(y);
for (size_t col = 0; col < ope_table.at(x).size(); col++) { for (size_t col = 0; col < ope_table.at(x).size(); col++) {
ope_table.at(x).at(col) /= norm; ope_table.at(x).at(col) /= norm;
} }
// === 其余行变换 === // Eliminate pivot column in other rows
for (size_t row = 0; row < bn; row++) { for (size_t row = 0; row < bn; row++) {
if (row == x) { if (row == x) {
continue; continue;
} }
if (ope_table.at(row).at(y) != 0) { if (ope_table.at(row).at(y) != 0) {
double norm = ope_table.at(row).at(y); double row_norm = ope_table.at(row).at(y);
for (size_t col = 0; col < ope_table.at(x).size(); col++) { for (size_t col = 0; col < ope_table.at(x).size(); col++) {
ope_table.at(row).at(col) = ope_table.at(row).at(col) - norm * ope_table.at(x).at(col); ope_table.at(row).at(col) -= row_norm * ope_table.at(x).at(col);
} }
} }
} }
basic.at(x - 1) = y; // 换元 basic.at(x - 1) = static_cast<int>(y);
} }