เริ่มต้นใช้งาน OR-Tools สำหรับ C++

ส่วนต่อไปนี้จะช่วยคุณในการเริ่มต้นใช้งาน "หรือ-เครื่องมือ" กับ C++:

ปัญหาในการเพิ่มประสิทธิภาพคืออะไร

เป้าหมายของการเพิ่มประสิทธิภาพคือการหาวิธีแก้ปัญหาที่ดีที่สุดจากชุดวิธีแก้ปัญหาขนาดใหญ่ที่เป็นไปได้ (บางครั้งคุณจะพอใจกับการค้นหาโซลูชัน ที่เป็นไปได้ หรือเครื่องมือก็สามารถทำได้เช่นกัน)

นี่คือปัญหาการเพิ่มประสิทธิภาพโดยทั่วไป สมมติว่าบริษัทจัดส่งแห่งหนึ่งส่งพัสดุภัณฑ์ให้ลูกค้าโดยใช้รถบรรทุกพาหนะ ในทุกๆ วัน บริษัทต้องกำหนดพัสดุให้กับรถบรรทุก แล้วเลือกเส้นทางที่ให้รถบรรทุกแต่ละคันเพื่อนำส่งพัสดุ การกำหนดแพ็กเกจและเส้นทางที่เป็นไปได้แต่ละรายการจะมีค่าใช้จ่ายตามระยะทางรวมในการเดินทางของรถบรรทุก และอาจมีปัจจัยอื่นๆ ด้วย ปัญหาคือการเลือกการกำหนดแพ็กเกจและเส้นทางที่มีค่าใช้จ่ายน้อยที่สุด

ปัญหานี้มีองค์ประกอบต่อไปนี้เช่นเดียวกับปัญหาในการเพิ่มประสิทธิภาพทั้งหมด

  • วัตถุประสงค์ ซึ่งก็คือจำนวนที่คุณต้องการเพิ่มประสิทธิภาพ ในตัวอย่างด้านบนมีวัตถุประสงค์เพื่อลดค่าใช้จ่าย ในการสร้างโจทย์การเพิ่มประสิทธิภาพ คุณต้องกำหนดฟังก์ชันที่คำนวณค่าของวัตถุประสงค์เพื่อหาวิธีแก้ปัญหาที่เป็นไปได้ ซึ่งเรียกว่าฟังก์ชันวัตถุประสงค์ ในตัวอย่างก่อนหน้านี้ ฟังก์ชันวัตถุประสงค์จะคำนวณต้นทุนรวมของการกำหนดแพ็กเกจและเส้นทาง

    วิธีแก้ปัญหาที่เหมาะสมที่สุดคือวิธีที่ค่าของฟังก์ชันวัตถุประสงค์นั้นดีที่สุด ("ดีที่สุด" อาจเป็นจำนวนสูงสุดหรือต่ำสุดก็ได้)

  • ข้อจำกัด - ข้อจำกัดในวิธีแก้ปัญหาที่เป็นไปได้โดยอิงตามข้อกำหนดเฉพาะของปัญหา เช่น หากบริษัทจัดส่งไม่สามารถกำหนดพัสดุที่มีน้ำหนักเกินกว่าที่กำหนดสำหรับรถบรรทุกได้ จะทำให้เกิดข้อจำกัดในโซลูชัน

    วิธีแก้ปัญหาที่ทำได้คือวิธีที่ทำตามข้อจำกัดทั้งหมดที่กำหนดของปัญหาได้โดยไม่จำเป็นต้องมีประสิทธิภาพสูงสุด

ขั้นตอนแรกในการแก้ปัญหาการเพิ่มประสิทธิภาพคือการระบุวัตถุประสงค์และข้อจำกัด

การแก้ไขปัญหาการเพิ่มประสิทธิภาพใน C++

ต่อไป เราจะยกตัวอย่างปัญหาการเพิ่มประสิทธิภาพ และแสดงวิธีตั้งค่าและแก้ปัญหาใน C++

ตัวอย่างการเพิ่มประสิทธิภาพเชิงเส้น

หนึ่งในด้านที่เก่าแก่ที่สุดและใช้กันอย่างแพร่หลายที่สุดคือการเพิ่มประสิทธิภาพเชิงเส้น (หรือโปรแกรมเชิงเส้น) ซึ่งจะเขียนฟังก์ชันที่เป็นวัตถุประสงค์และข้อจำกัดเป็นนิพจน์เชิงเส้นได้ ต่อไปนี้เป็นตัวอย่างง่ายๆ ของปัญหาประเภทนี้

เพิ่ม 3x + y สูงสุดภายใต้ข้อจำกัดต่อไปนี้

  1. 0 ≤ x ≤ 1
  2. 0 ≤ y ≤ 2
  3. x + y ≤ 2

ฟังก์ชันวัตถุประสงค์ในตัวอย่างนี้คือ 3x + y นิพจน์เชิงเส้นจะเป็นตัวกำหนดทั้งฟังก์ชันวัตถุประสงค์และข้อจำกัดต่างๆ ซึ่งทำให้เป็นปัญหาเชิงเส้น

ขั้นตอนหลักในการแก้ปัญหา

ขั้นตอนพื้นฐานในการตั้งค่าและแก้ปัญหาสำหรับแต่ละภาษาจะเหมือนกัน

  1. นำเข้าไลบรารีที่จำเป็น
  2. ประกาศเครื่องมือแก้โจทย์
  3. สร้างตัวแปร
  4. กำหนดข้อจำกัด
  5. กำหนดฟังก์ชันวัตถุประสงค์
  6. เรียกใช้เครื่องมือแก้โจทย์และ
  7. แสดงผลลัพธ์

โปรแกรม C++

ส่วนนี้จะอธิบายโปรแกรม C++ ที่ตั้งค่าและแก้โจทย์

มีขั้นตอนดังนี้

  • นำเข้าไลบรารีที่จำเป็น
    #include <cstdlib>
    #include <memory>
    
    #include "absl/flags/flag.h"
    #include "absl/log/flags.h"
    #include "ortools/base/init_google.h"
    #include "ortools/base/logging.h"
    #include "ortools/init/init.h"
    #include "ortools/linear_solver/linear_solver.h"
  • ประกาศเครื่องมือแก้โจทย์
    // Create the linear solver with the GLOP backend.
    std::unique_ptr<MPSolver> solver(MPSolver::CreateSolver("GLOP"));
    if (!solver) {
      LOG(WARNING) << "Could not create solver GLOP";
      return;
    }
    MPSolver เป็น Wrapper สำหรับการแก้ปัญหาการเขียนโปรแกรมเชิงเส้นหรือการเขียนโปรแกรมจำนวนเต็มผสม
  • สร้างตัวแปร
    // Create the variables x and y.
    MPVariable* const x = solver->MakeNumVar(0.0, 1, "x");
    MPVariable* const y = solver->MakeNumVar(0.0, 2, "y");
    
    LOG(INFO) << "Number of variables = " << solver->NumVariables();
  • กำหนดข้อจำกัด ข้อจำกัด 2 รายการแรก ได้แก่ 0 &leq; x1 และ 0 &leq; y2 กำหนดตามคำนิยามของตัวแปรแล้ว โค้ดต่อไปนี้เป็นตัวกำหนดข้อจำกัด x + y &leq; 2:
    // Create a linear constraint, x + y <= 2.
    const double infinity = solver->infinity();
    MPConstraint* const ct = solver->MakeRowConstraint(-infinity, 2.0, "ct");
    ct->SetCoefficient(x, 1);
    ct->SetCoefficient(y, 1);
    
    LOG(INFO) << "Number of constraints = " << solver->NumConstraints();
    เมธอด SetCoefficient ตั้งค่าสัมประสิทธิ์ของ x และ y ในนิพจน์สำหรับข้อจำกัด
  • กำหนดฟังก์ชันวัตถุประสงค์
    // Create the objective function, 3 * x + y.
    MPObjective* const objective = solver->MutableObjective();
    objective->SetCoefficient(x, 3);
    objective->SetCoefficient(y, 1);
    objective->SetMaximization();
    เมธอด SetMaximization ประกาศว่านี่คือปัญหาการเพิ่มสูงสุด
  • เรียกใช้เครื่องมือแก้โจทย์คณและแสดงผลลัพธ์
    LOG(INFO) << "Solving with " << solver->SolverVersion();
    const MPSolver::ResultStatus result_status = solver->Solve();
    // Check that the problem has an optimal solution.
    LOG(INFO) << "Status: " << result_status;
    if (result_status != MPSolver::OPTIMAL) {
      LOG(INFO) << "The problem does not have an optimal solution!";
      if (result_status == MPSolver::FEASIBLE) {
        LOG(INFO) << "A potentially suboptimal solution was found";
      } else {
        LOG(WARNING) << "The solver could not solve the problem.";
        return;
      }
    }
    
    LOG(INFO) << "Solution:";
    LOG(INFO) << "Objective value = " << objective->Value();
    LOG(INFO) << "x = " << x->solution_value();
    LOG(INFO) << "y = " << y->solution_value();

ดำเนินการตามโปรแกรมสำเร็จ

โปรดดูโปรแกรมที่สมบูรณ์ด้านล่าง

// Minimal example to call the GLOP solver.
#include <cstdlib>
#include <memory>

#include "absl/flags/flag.h"
#include "absl/log/flags.h"
#include "ortools/base/init_google.h"
#include "ortools/base/logging.h"
#include "ortools/init/init.h"
#include "ortools/linear_solver/linear_solver.h"

namespace operations_research {
void BasicExample() {
  LOG(INFO) << "Google OR-Tools version : " << OrToolsVersion::VersionString();

  // Create the linear solver with the GLOP backend.
  std::unique_ptr<MPSolver> solver(MPSolver::CreateSolver("GLOP"));
  if (!solver) {
    LOG(WARNING) << "Could not create solver GLOP";
    return;
  }

  // Create the variables x and y.
  MPVariable* const x = solver->MakeNumVar(0.0, 1, "x");
  MPVariable* const y = solver->MakeNumVar(0.0, 2, "y");

  LOG(INFO) << "Number of variables = " << solver->NumVariables();

  // Create a linear constraint, x + y <= 2.
  const double infinity = solver->infinity();
  MPConstraint* const ct = solver->MakeRowConstraint(-infinity, 2.0, "ct");
  ct->SetCoefficient(x, 1);
  ct->SetCoefficient(y, 1);

  LOG(INFO) << "Number of constraints = " << solver->NumConstraints();

  // Create the objective function, 3 * x + y.
  MPObjective* const objective = solver->MutableObjective();
  objective->SetCoefficient(x, 3);
  objective->SetCoefficient(y, 1);
  objective->SetMaximization();

  LOG(INFO) << "Solving with " << solver->SolverVersion();
  const MPSolver::ResultStatus result_status = solver->Solve();

  // Check that the problem has an optimal solution.
  LOG(INFO) << "Status: " << result_status;
  if (result_status != MPSolver::OPTIMAL) {
    LOG(INFO) << "The problem does not have an optimal solution!";
    if (result_status == MPSolver::FEASIBLE) {
      LOG(INFO) << "A potentially suboptimal solution was found";
    } else {
      LOG(WARNING) << "The solver could not solve the problem.";
      return;
    }
  }

  LOG(INFO) << "Solution:";
  LOG(INFO) << "Objective value = " << objective->Value();
  LOG(INFO) << "x = " << x->solution_value();
  LOG(INFO) << "y = " << y->solution_value();

  LOG(INFO) << "Advanced usage:";
  LOG(INFO) << "Problem solved in " << solver->wall_time() << " milliseconds";
  LOG(INFO) << "Problem solved in " << solver->iterations() << " iterations";
}
}  // namespace operations_research

int main(int argc, char* argv[]) {
  InitGoogle(argv[0], &argc, &argv, true);
  absl::SetFlag(&FLAGS_stderrthreshold, 0);
  operations_research::BasicExample();
  return EXIT_SUCCESS;
}

การใช้งานโปรแกรม C++

คุณเรียกใช้โปรแกรมข้างต้นได้โดยทำดังนี้

  1. คัดลอกและวางโค้ดข้างต้นลงในไฟล์ใหม่ แล้วบันทึกเป็น program.cc
  2. เปิดหน้าต่างคำสั่งที่ระดับบนสุดของไดเรกทอรีที่คุณติดตั้ง OR-Tools แล้วป้อนข้อมูลต่อไปนี้
    make run SOURCE=relative/path/to/program.cc
    โดยที่ relative/path/to/ คือเส้นทางไปยังไดเรกทอรีที่คุณบันทึกโปรแกรมไว้

โปรแกรมจะแสดงผลค่าของ x และ y ที่เพิ่มฟังก์ชันวัตถุประสงค์สูงสุดดังนี้

Solution:
x =  1.0
y =  1.0

หากต้องการคอมไพล์โปรแกรมโดยไม่ต้องเรียกใช้ ให้ป้อน:

make build SOURCE=relative/path/to/program.cc

การคอมไพล์ในโหมดเลือกใช้

วิธีคอมไพล์ในโหมด O3

make DEBUG='-O3' all

การเรียกใช้ไฟล์ปฏิบัติการ C++

เมื่อคอมไพล์โปรแกรม C++ OR-Tools ด้วย make ระบบจะสร้างไฟล์ปฏิบัติการในไดเรกทอรี bin คุณสามารถเรียกใช้ไฟล์ปฏิบัติการสำหรับโปรแกรมตัวอย่างได้ดังนี้

cd bin && ./program

หากทำการเปลี่ยนแปลงในโปรแกรม คุณจะต้องคอมไพล์อีกครั้งดังที่แสดงด้านบน

ตัวอย่าง C++ เพิ่มเติม

ดูตัวอย่าง C++ เพิ่มเติมที่แสดงให้เห็นวิธีแก้ปัญหาการเพิ่มประสิทธิภาพประเภทต่างๆ ได้ที่ตัวอย่าง

ระบุประเภทของปัญหาที่คุณต้องการแก้ไข

ปัญหาการเพิ่มประสิทธิภาพเกิดขึ้นได้หลายประเภทในโลก โจทย์แต่ละประเภทมีวิธีการและอัลกอริทึมที่แตกต่างกันในการหาวิธีแก้ปัญหาที่เหมาะสมที่สุด

ก่อนที่จะเริ่มเขียนโปรแกรมเพื่อแก้ปัญหาการเพิ่มประสิทธิภาพ คุณจะต้องระบุประเภทของปัญหาที่กำลังจัดการแล้วเลือกเครื่องมือแก้ปัญหาที่เหมาะสม ซึ่งเป็นอัลกอริทึมสำหรับการค้นหาวิธีแก้ปัญหาที่ดีที่สุด

ด้านล่างนี้เป็นภาพรวมคร่าวๆ ของประเภทโจทย์ที่ "หรือ" แก้ไขได้ และลิงก์ไปยังส่วนต่างๆ ในคู่มือนี้ซึ่งอธิบายวิธีแก้โจทย์แต่ละประเภท

การเพิ่มประสิทธิภาพเชิงเส้น

ตามที่คุณได้เรียนรู้ในส่วนก่อนหน้า ปัญหาการเพิ่มประสิทธิภาพเชิงเส้นคือปัญหาที่ฟังก์ชันวัตถุประสงค์และข้อจำกัดเป็นนิพจน์เชิงเส้นในตัวแปร

เครื่องมือแก้โจทย์คณิตหลักใน "หรือ" สำหรับปัญหาประเภทนี้คือเครื่องมือแก้โจทย์คณิตเชิงเส้น ซึ่งที่จริงแล้วเป็น Wrapper สำหรับไลบรารีต่างๆ สำหรับการเพิ่มประสิทธิภาพจำนวนเต็มผสมและเชิงเส้น รวมถึงไลบรารีของบุคคลที่สาม

ดูข้อมูลเพิ่มเติมเกี่ยวกับการเพิ่มประสิทธิภาพเชิงเส้น

การเพิ่มประสิทธิภาพจำนวนเต็มผสม

ปัญหาการเพิ่มประสิทธิภาพจำนวนเต็มผสมคือปัญหาที่ตัวแปรบางตัวหรือทั้งหมดต้องเป็นจำนวนเต็ม ตัวอย่างคือปัญหาการมอบหมายงาน ซึ่งกำหนดให้ผู้ปฏิบัติงานกลุ่มหนึ่งต้องถูกมอบหมายไปยังชุดงานหนึ่ง สำหรับผู้ปฏิบัติงานและงานแต่ละรายการ คุณจะกำหนดตัวแปรที่มีค่าเป็น 1 หากผู้ปฏิบัติงานที่ได้รับมอบหมายนั้นได้รับมอบหมายให้กับผู้ปฏิบัติงาน และมีค่าเป็น 0 ในกรณีนี้ ตัวแปรจะใช้ได้เฉพาะค่า 0 หรือ 1 เท่านั้น

ดูข้อมูลเพิ่มเติมเกี่ยวกับการเพิ่มประสิทธิภาพจำนวนเต็มผสม

การเพิ่มประสิทธิภาพข้อจำกัด

การเพิ่มประสิทธิภาพแบบจํากัด หรือการเขียนโปรแกรมข้อจํากัด (CP) จะระบุวิธีแก้ปัญหาที่เป็นไปได้จากผู้สมัครจำนวนมาก ซึ่งสามารถจำลองปัญหาในรูปแบบของข้อจํากัดที่กําหนดเองได้ CP อิงตามความเป็นไปได้ (ค้นหาโซลูชันที่เป็นไปได้) แทนการเพิ่มประสิทธิภาพ (ค้นหาโซลูชันที่ดีที่สุด) และมุ่งเน้นที่ข้อจำกัดและตัวแปร ไม่ใช่ฟังก์ชันที่เป็นวัตถุประสงค์ อย่างไรก็ตาม คุณใช้ CP เพื่อแก้ปัญหาการเพิ่มประสิทธิภาพได้ เพียงเปรียบเทียบค่าของฟังก์ชันวัตถุประสงค์สําหรับวิธีแก้ปัญหาที่เป็นไปได้ทั้งหมด

ดูข้อมูลเพิ่มเติมเกี่ยวกับการเพิ่มประสิทธิภาพข้อจำกัด

การมอบหมาย

ปัญหาเกี่ยวกับการมอบหมายเกี่ยวข้องกับการมอบหมายกลุ่ม Agent (เช่น ผู้ปฏิบัติงานหรือเครื่อง) ให้กับชุดงาน ซึ่งการมอบหมายตัวแทนแต่ละรายการให้กับงานที่เฉพาะเจาะจงนั้นมีค่าใช้จ่ายคงที่ ปัญหาคือหางานที่มีต้นทุนรวมน้อยที่สุด ปัญหาในการรับงานเป็นกรณีพิเศษของปัญหาการไหลเวียนของเครือข่าย

ดูข้อมูลเพิ่มเติมเกี่ยวกับการมอบหมาย

การบรรจุหีบห่อ

การบรรจุลงในถังคือปัญหาในการบรรจุชุดวัตถุขนาดต่างๆ ลงในภาชนะที่มีความจุแตกต่างกัน เป้าหมายคือบรรจุออบเจ็กต์ให้ได้มากที่สุดเท่าที่จะเป็นไปได้ โดยขึ้นอยู่กับความจุของคอนเทนเนอร์ กรณีพิเศษของกรณีนี้คือปัญหา Knapsack ซึ่งมีเพียงคอนเทนเนอร์เดียว

ดูข้อมูลเพิ่มเติมเกี่ยวกับการบรรจุ Bin

Scheduling

ปัญหาในการกำหนดเวลาเกี่ยวข้องกับการมอบหมายทรัพยากรเพื่อดำเนินการต่างๆ ในชุดงานในเวลาที่เฉพาะเจาะจง ตัวอย่างที่สำคัญคือปัญหาของร้านงานซึ่งมีการประมวลผลงานหลายงานในเครื่องหลายเครื่อง งานแต่ละงานประกอบด้วยลำดับของงานซึ่งต้องดำเนินการตามลำดับที่กำหนด และแต่ละงานต้องได้รับการประมวลผลในเครื่องที่เฉพาะเจาะจง ปัญหาคือการกำหนดเวลาเพื่อให้งานทั้งหมดเสร็จสมบูรณ์ภายในเวลาอันสั้นที่สุดเท่าที่จะเป็นไปได้

ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดเวลา

การกำหนดเส้นทาง

ปัญหาการกำหนดเส้นทางเกี่ยวข้องกับการค้นหาเส้นทางที่เหมาะสมสำหรับกลุ่มยานพาหนะ เพื่อเดินทางข้ามเครือข่าย ซึ่งกำหนดโดยกราฟที่มีทิศทาง ปัญหาการกำหนดแพ็กเกจให้กับรถบรรทุกส่ง ตามที่อธิบายไว้ในปัญหาในการเพิ่มประสิทธิภาพคืออะไรเป็นตัวอย่างหนึ่งของปัญหาในการกำหนดเส้นทาง อีกวิธีคือปัญหาของพนักงานขายที่เดินทาง

ดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดเส้นทาง

ขั้นตอนของเครือข่าย

ปัญหาในการเพิ่มประสิทธิภาพหลายข้ออาจแสดงด้วยกราฟแบบมีทิศทาง ซึ่งประกอบด้วยโหนดและส่วนโค้งที่มีทิศทางตรง ตัวอย่างเช่น ปัญหาการขนส่งซึ่งจัดส่งสินค้าผ่านเครือข่ายทางรถไฟอาจแสดงด้วยกราฟเส้นโค้งเป็นเส้นทางรถไฟและโหนดคือศูนย์กระจายสินค้า

ในปัญหาการไหลสูงสุด เส้นโค้งแต่ละโค้งจะมีความจุสูงสุดที่ส่งผ่านได้ ปัญหาคือการกำหนดจำนวนสินค้าที่จะจัดส่งในแต่ละส่วนเพื่อให้ปริมาณรวมที่ขนส่งมีมากที่สุด

ดูข้อมูลเพิ่มเติมเกี่ยวกับโฟลว์ของเครือข่าย