diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a37e6d4 --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d480051 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Created by https://www.toptal.com/developers/gitignore/api/cmake +# Edit at https://www.toptal.com/developers/gitignore?templates=cmake + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +# End of https://www.toptal.com/developers/gitignore/api/cmake + +build/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3519655 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "cvrp/CVRPController"] + path = cvrp/CVRPController + url = https://github.com/laser-ufpb/CVRPController.git +[submodule "irp/dimacs-irp-verifier"] + path = irp/dimacs-irp-verifier + url = https://github.com/sbeyer/dimacs-irp-verifier.git +[submodule "carp/HGS-CARP"] + path = carp/HGS-CARP + url = https://github.com/vidalt/HGS-CARP.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ab65e25 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.14) +project(AlkaidSD) + +set(CMAKE_CXX_STANDARD 20) +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -march=native") + if (PGO STREQUAL "generate") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fprofile-instr-generate=${CMAKE_SOURCE_DIR}/pgo-clang/dimacs-sd-%p.profraw") + endif () + if (PGO STREQUAL "use") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fprofile-instr-use=${CMAKE_SOURCE_DIR}/pgo-clang/dimacs-sd.profdata") + endif () + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -fuse-ld=lld") +elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -march=native") + if (PGO STREQUAL "generate") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fprofile-generate=${CMAKE_SOURCE_DIR}/pgo-gcc/dimacs-sd-%q{PROFILE_FILE}") + endif () + if (PGO STREQUAL "use") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fprofile-use=${CMAKE_SOURCE_DIR}/pgo-gcc/dimacs-sd") + endif () +endif() + +include(cmake/cli11.cmake) +include(cmake/json.cmake) +include(cmake/mimalloc.cmake) + +include_directories(src) + +add_library( + alkaid-sd + src/algorithm/construction.cc + src/algorithm/operator/cross.cc + src/algorithm/operator/exchange.cc + src/algorithm/operator/or_opt.cc + src/algorithm/operator/relocate.cc + src/algorithm/operator/repair.cc + src/algorithm/operator/sd_swap_one_one.cc + src/algorithm/operator/sd_swap_star.cc + src/algorithm/operator/sd_swap_two_one.cc + src/algorithm/operator/split_reinsertion.cc + src/algorithm/operator/swap_star.cc + src/algorithm/operator/swap.cc + src/algorithm/ruin_method.cc + src/algorithm/solver.cc + src/algorithm/sorter.cc + src/config/config.cc + src/distance_matrix.cc + src/model/route_context.cc + src/problem.cc + src/util/solution_utils.cc + src/util/submit.cc + src/util/timer.cc) + +target_link_libraries(alkaid-sd nlohmann_json::nlohmann_json) + +add_executable(alkaid-sd-main src/main.cc) + +target_link_libraries(alkaid-sd-main PRIVATE alkaid-sd CLI11::CLI11 mimalloc-static) diff --git a/README.md b/README.md new file mode 100644 index 0000000..da48b23 --- /dev/null +++ b/README.md @@ -0,0 +1,163 @@ +# AlkaidSD: An Efficient Iterated Local Search Heuristic for the Split Delivery Vehicle Routing Problem + +DIMACS-12 competition: http://dimacs.rutgers.edu/programs/challenge/vrp/vrpsd/ + +## Build + +### Linux + +```bash +mkdir build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Release +cmake --build . +``` + +Then the executable file is under `./build/` + +### Windows (MSVC) + +```bash +mkdir build +cd build +cmake .. +cmake --build . --config Release +``` + +Then the executable file is under `.\build\Release\` + +## Run + +``` +Usage: ./alkaid-sd-main [OPTIONS] config instance output + +Positionals: + config TEXT REQUIRED Config path + instance TEXT REQUIRED Instance name + output TEXT REQUIRED Output + +Options: + -h,--help Print this help message and exit + -s INT Random Seed +``` + +## Examples + +```bash +# Solve instance `p05_3070` and output solution to `out_p05_3070.txt` +$ ./alkaid-sd-main ./config/AlkaidSD.json p05_3070 out_p05_3070.txt + +# time, value +0.0729653,6207 +0.0941946,6112 +0.112931,6018 +0.190977,5806 +0.242988,5671 +0.258149,5668 +0.328725,5643 +0.376124,5579 +... + +$ cat out_p05_3070.txt + +Route 1: 0 - 97 ( 121 ) - 187 ( 79 ) - 0 +... +Route 102: 0 - 152 ( 0 ) - 173 ( 109 ) - 171 ( 91 ) - 0 +5320 +Intel Core i7-8700 @ 3.20GHz +1347.01 +``` + +## Config +```json5 +{ + "problem_dir": "data/", + "random_seed": 42, + + // Processor type as listed in PassMark. See rule http://dimacs.rutgers.edu/files/3816/3754/6419/SDVRP-Rules.pdf + "processor": "Intel Core i7-8700 @ 3.20GHz", + "time_limit": 1347, + + // Possible time limit types: + // - real + // - cpu + "time_limit_type": "real", + + // Possible acceptance rules: + // + // - type: HC (Hill Climbing) + // + // - type: HCWE (Hill Climbing With Equal) + // + // - type: LAHC (Late Acceptance Hill Climbing) + // length: 83 + // + // - type: SA (Simulated Annealing) + // initial_temperature: 1000 + // decay: 0.99 + // + "acceptance_rule": { + "type": "LAHC", + "length": 83 + }, + + // Possible ruin methods: + // + // - type: SISRs + // average_customers: 36 + // max_length: 8 + // split_rate: 0.740 + // preserved_probability: 0.096 + // + // - type: Random + // num_perturb_customers: [5, 6, 7] + // + "ruin_method": { + "type": "SISRs", + "average_customers": 36, + "max_length": 8, + "split_rate": 0.740, + "preserved_probability": 0.096 + }, + + "sorter": { + "random": 0.078, + "demand": 0.225, + "far": 0.942, + "close": 0.120 + }, + "blink_rate": 0.021, + + // Operators for HRVND (Hierarchical random variable neighborhood descent) + // Possible inter-route operators: + // - Swap<2, 0> + // - Swap<2, 1> + // - Swap<2, 2> + // - Relocate + // - SwapStar + // - Cross + // - SdSwapStar + // - SdSwapOneOne + // - SdSwapTwoOne + "inter_operators": [ + [ + "Relocate", + "Swap<2, 0>", + "Swap<2, 1>", + "Swap<2, 2>", + "Cross", + "SwapStar", + "SdSwapStar" + ] + ], + // Possible intra-route operators: + // - Exchange + // - OrOpt<1> + // - OrOpt<2> + // - OrOpt<3> + "intra_operators": [ + "Exchange", + "OrOpt<1>" + ] +} +``` diff --git a/cmake/cli11.cmake b/cmake/cli11.cmake new file mode 100644 index 0000000..3a567fb --- /dev/null +++ b/cmake/cli11.cmake @@ -0,0 +1,6 @@ +include(FetchContent) +FetchContent_Declare( + cli11 + GIT_REPOSITORY https://github.com/CLIUtils/CLI11 + GIT_TAG v2.3.2) +FetchContent_MakeAvailable(cli11) diff --git a/cmake/json.cmake b/cmake/json.cmake new file mode 100644 index 0000000..06ef45e --- /dev/null +++ b/cmake/json.cmake @@ -0,0 +1,4 @@ +include(FetchContent) +FetchContent_Declare(json + URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz) +FetchContent_MakeAvailable(json) diff --git a/cmake/mimalloc.cmake b/cmake/mimalloc.cmake new file mode 100644 index 0000000..0aec769 --- /dev/null +++ b/cmake/mimalloc.cmake @@ -0,0 +1,6 @@ +include(FetchContent) +FetchContent_Declare( + mimalloc + GIT_REPOSITORY https://github.com/microsoft/mimalloc + GIT_TAG v2.0.9) +FetchContent_MakeAvailable(mimalloc) diff --git a/config/AlkaidSD.json b/config/AlkaidSD.json new file mode 100644 index 0000000..7d5502c --- /dev/null +++ b/config/AlkaidSD.json @@ -0,0 +1,40 @@ +{ + "problem_dir": "data/", + "random_seed": 42, + "processor": "Intel Core i7-8700 @ 3.20GHz", + "time_limit": 1347, + "time_limit_type": "real", + "acceptance_rule": { + "type": "LAHC", + "length": 83 + }, + "ruin_method": { + "type": "SISRs", + "average_customers": 36, + "max_length": 8, + "split_rate": 0.740, + "preserved_probability": 0.096 + }, + "sorter": { + "random": 0.078, + "demand": 0.225, + "far": 0.942, + "close": 0.120 + }, + "blink_rate": 0.021, + "inter_operators": [ + [ + "Relocate", + "Swap<2, 0>", + "Swap<2, 1>", + "Swap<2, 2>", + "Cross", + "SwapStar", + "SdSwapStar" + ] + ], + "intra_operators": [ + "Exchange", + "OrOpt<1>" + ] +} \ No newline at end of file diff --git a/data/SDVRP-instances-description.pdf b/data/SDVRP-instances-description.pdf new file mode 100644 index 0000000..87a3c53 Binary files /dev/null and b/data/SDVRP-instances-description.pdf differ diff --git a/data/SET-1/SD1.txt b/data/SET-1/SD1.txt new file mode 100644 index 0000000..1302c39 --- /dev/null +++ b/data/SET-1/SD1.txt @@ -0,0 +1,12 @@ +8 100 +60 90 60 90 60 90 60 90 +0 0 + 1000 0 + 0 1000 +-1000 0 + -0 -1000 + 2000 0 + 0 2000 +-2000 0 + -0 -2000 + diff --git a/data/SET-1/SD10.txt b/data/SET-1/SD10.txt new file mode 100644 index 0000000..baa2cdc --- /dev/null +++ b/data/SET-1/SD10.txt @@ -0,0 +1,67 @@ +64 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 924 383 + 707 707 + 383 924 + 0 1000 + -383 924 + -707 707 + -924 383 +-1000 0 + -924 -383 + -707 -707 + -383 -924 + -0 -1000 + 383 -924 + 707 -707 + 924 -383 + 2000 0 + 1848 765 + 1414 1414 + 765 1848 + 0 2000 + -765 1848 +-1414 1414 +-1848 766 +-2000 0 +-1848 -765 +-1414 -1414 + -766 -1848 + -0 -2000 + 765 -1848 + 1414 -1414 + 1848 -766 + 3000 0 + 2772 1148 + 2121 2121 + 1148 2772 + 0 3000 +-1148 2772 +-2121 2121 +-2772 1148 +-3000 0 +-2772 -1148 +-2122 -2121 +-1148 -2771 + -0 -3000 + 1148 -2772 + 2121 -2122 + 2771 -1149 + 4000 0 + 3696 1531 + 2828 2828 + 1531 3695 + 0 4000 +-1531 3696 +-2828 2829 +-3695 1531 +-4000 0 +-3696 -1530 +-2829 -2828 +-1531 -3695 + -1 -4000 + 1530 -3696 + 2828 -2829 + 3695 -1531 \ No newline at end of file diff --git a/data/SET-1/SD11.txt b/data/SET-1/SD11.txt new file mode 100644 index 0000000..7da02f7 --- /dev/null +++ b/data/SET-1/SD11.txt @@ -0,0 +1,83 @@ +80 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 0 1000 + -1000 0 + -0 -1000 + 2000 0 + 0 2000 + -2000 0 + -0 -2000 + 3000 0 + 0 3000 + -3000 0 + -0 -3000 + 4000 0 + 0 4000 + -4000 0 + -1 -4000 + 5000 0 + 0 5000 + -5000 0 + -1 -5000 + 6000 0 + 0 6000 + -6000 1 + -1 -6000 + 7000 0 + 0 7000 + -7000 1 + -1 -7000 + 8000 0 + 0 8000 + -8000 1 + -1 -8000 + 9000 0 + 0 9000 + -9000 1 + -1 -9000 + 10000 0 + 0 10000 + -10000 1 + -1 -10000 + 11000 0 + 1 11000 + -11000 1 + -2 -11000 + 12000 0 + 1 12000 + -12000 1 + -2 -12000 + 13000 0 + 1 13000 + -13000 1 + -2 -13000 + 14000 0 + 1 14000 + -14000 1 + -2 -14000 + 15000 0 + 1 15000 + -15000 1 + -2 -15000 + 16000 0 + 1 16000 + -16000 1 + -2 -16000 + 17000 0 + 1 17000 + -17000 2 + -2 -17000 + 18000 0 + 1 18000 + -18000 2 + -3 -18000 + 19000 0 + 1 19000 + -19000 2 + -3 -19000 + 20000 0 + 1 20000 + -20000 2 + -3 -20000 diff --git a/data/SET-1/SD12.txt b/data/SET-1/SD12.txt new file mode 100644 index 0000000..eaea701 --- /dev/null +++ b/data/SET-1/SD12.txt @@ -0,0 +1,83 @@ +80 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 +0 0 + 1000 0 + 707 707 + 0 1000 + -707 707 + -1000 0 + -707 -707 + -0 -1000 + 707 -707 + 2000 0 + 1414 1414 + 0 2000 + -1414 1414 + -2000 0 + -1414 -1414 + -0 -2000 + 1414 -1414 + 3000 0 + 2121 2121 + 0 3000 + -2121 2121 + -3000 0 + -2122 -2121 + -0 -3000 + 2121 -2122 + 4000 0 + 2828 2828 + 0 4000 + -2828 2829 + -4000 0 + -2829 -2828 + -1 -4000 + 2828 -2829 + 5000 0 + 3536 3535 + 0 5000 + -3535 3536 + -5000 0 + -3536 -3535 + -1 -5000 + 3535 -3536 + 6000 0 + 4243 4243 + 0 6000 + -4242 4243 + -6000 1 + -4243 -4242 + -1 -6000 + 4242 -4243 + 7000 0 + 4950 4950 + 0 7000 + -4949 4950 + -7000 1 + -4950 -4949 + -1 -7000 + 4949 -4951 + 8000 0 + 5657 5657 + 0 8000 + -5656 5657 + -8000 1 + -5658 -5656 + -1 -8000 + 5656 -5658 + 9000 0 + 6364 6364 + 0 9000 + -6364 6364 + -9000 1 + -6365 -6363 + -1 -9000 + 6363 -6365 + 10000 0 + 7071 7071 + 0 10000 + -7071 7072 + -10000 1 + -7072 -7070 + -1 -10000 + 7070 -7072 diff --git a/data/SET-1/SD13.txt b/data/SET-1/SD13.txt new file mode 100644 index 0000000..f6d2b0f --- /dev/null +++ b/data/SET-1/SD13.txt @@ -0,0 +1,99 @@ +96 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 707 707 + 0 1000 + -707 707 + -1000 0 + -707 -707 + -0 -1000 + 707 -707 + 2000 0 + 1414 1414 + 0 2000 + -1414 1414 + -2000 0 + -1414 -1414 + -0 -2000 + 1414 -1414 + 3000 0 + 2121 2121 + 0 3000 + -2121 2121 + -3000 0 + -2122 -2121 + -0 -3000 + 2121 -2122 + 4000 0 + 2828 2828 + 0 4000 + -2828 2829 + -4000 0 + -2829 -2828 + -1 -4000 + 2828 -2829 + 5000 0 + 3536 3535 + 0 5000 + -3535 3536 + -5000 0 + -3536 -3535 + -1 -5000 + 3535 -3536 + 6000 0 + 4243 4243 + 0 6000 + -4242 4243 + -6000 1 + -4243 -4242 + -1 -6000 + 4242 -4243 + 7000 0 + 4950 4950 + 0 7000 + -4949 4950 + -7000 1 + -4950 -4949 + -1 -7000 + 4949 -4951 + 8000 0 + 5657 5657 + 0 8000 + -5656 5657 + -8000 1 + -5658 -5656 + -1 -8000 + 5656 -5658 + 9000 0 + 6364 6364 + 0 9000 + -6364 6364 + -9000 1 + -6365 -6363 + -1 -9000 + 6363 -6365 + 10000 0 + 7071 7071 + 0 10000 + -7071 7072 + -10000 1 + -7072 -7070 + -1 -10000 + 7070 -7072 + 11000 0 + 7778 7778 + 1 11000 + -7778 7779 + -11000 1 + -7779 -7777 + -2 -11000 + 7777 -7779 + 12000 0 + 8485 8485 + 1 12000 + -8485 8486 + -12000 1 + -8486 -8484 + -2 -12000 + 8484 -8487 diff --git a/data/SET-1/SD14.txt b/data/SET-1/SD14.txt new file mode 100644 index 0000000..78218da --- /dev/null +++ b/data/SET-1/SD14.txt @@ -0,0 +1,123 @@ +120 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 866 500 + 500 866 + 0 1000 + -500 866 + -866 500 + -1000 0 + -866 -500 + -500 -866 + -0 -1000 + 500 -866 + 866 -500 + 2000 0 + 1732 1000 + 1000 1732 + 0 2000 + -1000 1732 + -1732 1000 + -2000 0 + -1732 -1000 + -1000 -1732 + -0 -2000 + 1000 -1732 + 1732 -1000 + 3000 0 + 2598 1500 + 1500 2598 + 0 3000 + -1500 2598 + -2598 1500 + -3000 0 + -2598 -1500 + -1500 -2598 + -0 -3000 + 1500 -2598 + 2598 -1500 + 4000 0 + 3464 2000 + 2000 3464 + 0 4000 + -2000 3464 + -3464 2000 + -4000 0 + -3464 -2000 + -2000 -3464 + -1 -4000 + 1999 -3464 + 3464 -2001 + 5000 0 + 4330 2500 + 2500 4330 + 0 5000 + -2500 4330 + -4330 2500 + -5000 0 + -4330 -2500 + -2501 -4330 + -1 -5000 + 2499 -4331 + 4330 -2501 + 6000 0 + 5196 3000 + 3000 5196 + 0 6000 + -3000 5196 + -5196 3000 + -6000 1 + -5196 -2999 + -3001 -5196 + -1 -6000 + 2999 -5197 + 5196 -3001 + 7000 0 + 6062 3500 + 3500 6062 + 0 7000 + -3500 6062 + -6062 3500 + -7000 1 + -6063 -3499 + -3501 -6062 + -1 -7000 + 3499 -6063 + 6062 -3501 + 8000 0 + 6928 4000 + 4000 6928 + 0 8000 + -4000 6928 + -6928 4001 + -8000 1 + -6929 -3999 + -4001 -6928 + -1 -8000 + 3999 -6929 + 6928 -4001 + 9000 0 + 7794 4500 + 4500 7794 + 0 9000 + -4500 7795 + -7794 4501 + -9000 1 + -7795 -4499 + -4501 -7794 + -1 -9000 + 4499 -7795 + 7793 -4501 + 10000 0 + 8660 5000 + 5000 8660 + 0 10000 + -4999 8661 + -8660 5001 + -10000 1 + -8661 -4999 + -5001 -8660 + -1 -10000 + 4999 -8661 + 8659 -5001 diff --git a/data/SET-1/SD15.txt b/data/SET-1/SD15.txt new file mode 100644 index 0000000..d135e39 --- /dev/null +++ b/data/SET-1/SD15.txt @@ -0,0 +1,147 @@ +144 100 + 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 +0 0 + 1000 0 + 866 500 + 500 866 + 0 1000 + -500 866 + -866 500 + -1000 0 + -866 -500 + -500 -866 + -0 -1000 + 500 -866 + 866 -500 + 2000 0 + 1732 1000 + 1000 1732 + 0 2000 + -1000 1732 + -1732 1000 + -2000 0 + -1732 -1000 + -1000 -1732 + -0 -2000 + 1000 -1732 + 1732 -1000 + 3000 0 + 2598 1500 + 1500 2598 + 0 3000 + -1500 2598 + -2598 1500 + -3000 0 + -2598 -1500 + -1500 -2598 + -0 -3000 + 1500 -2598 + 2598 -1500 + 4000 0 + 3464 2000 + 2000 3464 + 0 4000 + -2000 3464 + -3464 2000 + -4000 0 + -3464 -2000 + -2000 -3464 + -1 -4000 + 1999 -3464 + 3464 -2001 + 5000 0 + 4330 2500 + 2500 4330 + 0 5000 + -2500 4330 + -4330 2500 + -5000 0 + -4330 -2500 + -2501 -4330 + -1 -5000 + 2499 -4331 + 4330 -2501 + 6000 0 + 5196 3000 + 3000 5196 + 0 6000 + -3000 5196 + -5196 3000 + -6000 1 + -5196 -2999 + -3001 -5196 + -1 -6000 + 2999 -5197 + 5196 -3001 + 7000 0 + 6062 3500 + 3500 6062 + 0 7000 + -3500 6062 + -6062 3500 + -7000 1 + -6063 -3499 + -3501 -6062 + -1 -7000 + 3499 -6063 + 6062 -3501 + 8000 0 + 6928 4000 + 4000 6928 + 0 8000 + -4000 6928 + -6928 4001 + -8000 1 + -6929 -3999 + -4001 -6928 + -1 -8000 + 3999 -6929 + 6928 -4001 + 9000 0 + 7794 4500 + 4500 7794 + 0 9000 + -4500 7795 + -7794 4501 + -9000 1 + -7795 -4499 + -4501 -7794 + -1 -9000 + 4499 -7795 + 7793 -4501 + 10000 0 + 8660 5000 + 5000 8660 + 0 10000 + -4999 8661 + -8660 5001 + -10000 1 + -8661 -4999 + -5001 -8660 + -1 -10000 + 4999 -8661 + 8659 -5001 + 11000 0 + 9526 5500 + 5500 9526 + 1 11000 + -5499 9527 + -9526 5501 + -11000 1 + -9527 -5499 + -5501 -9526 + -2 -11000 + 5499 -9527 + 9525 -5502 + 12000 0 + 10392 6000 + 6000 10392 + 1 12000 + -5999 10393 + -10392 6001 + -12000 1 + -10393 -5999 + -6001 -10392 + -2 -12000 + 5998 -10393 + 10391 -6002 diff --git a/data/SET-1/SD16.txt b/data/SET-1/SD16.txt new file mode 100644 index 0000000..7b1d5bb --- /dev/null +++ b/data/SET-1/SD16.txt @@ -0,0 +1,147 @@ +144 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 +0 0 + 1000 0 + 996 87 + 985 174 + 966 259 + 940 342 + 906 423 + 866 500 + 819 574 + 766 643 + 707 707 + 643 766 + 574 819 + 500 866 + 423 906 + 342 940 + 259 966 + 174 985 + 87 996 + 0 1000 + -87 996 + -174 985 + -259 966 + -342 940 + -423 906 + -500 866 + -574 819 + -643 766 + -707 707 + -766 643 + -819 574 + -866 500 + -906 423 + -940 342 + -966 259 + -985 174 + -996 87 + -1000 0 + -996 -87 + -985 -174 + -966 -259 + -940 -342 + -906 -423 + -866 -500 + -819 -573 + -766 -643 + -707 -707 + -643 -766 + -574 -819 + -500 -866 + -423 -906 + -342 -940 + -259 -966 + -174 -985 + -87 -996 + -0 -1000 + 87 -996 + 174 -985 + 259 -966 + 342 -940 + 422 -906 + 500 -866 + 573 -819 + 643 -766 + 707 -707 + 766 -643 + 819 -574 + 866 -500 + 906 -423 + 940 -342 + 966 -259 + 985 -174 + 996 -87 + 2000 0 + 1992 174 + 1970 347 + 1932 518 + 1879 684 + 1813 845 + 1732 1000 + 1638 1147 + 1532 1286 + 1414 1414 + 1286 1532 + 1147 1638 + 1000 1732 + 845 1813 + 684 1879 + 518 1932 + 347 1970 + 174 1992 + 0 2000 + -174 1992 + -347 1970 + -518 1932 + -684 1879 + -845 1813 + -1000 1732 + -1147 1638 + -1285 1532 + -1414 1414 + -1532 1286 + -1638 1147 + -1732 1000 + -1813 845 + -1879 684 + -1932 518 + -1970 347 + -1992 174 + -2000 0 + -1992 -174 + -1970 -347 + -1932 -517 + -1879 -684 + -1813 -845 + -1732 -1000 + -1638 -1147 + -1532 -1285 + -1414 -1414 + -1286 -1532 + -1147 -1638 + -1000 -1732 + -845 -1813 + -684 -1879 + -518 -1932 + -348 -1970 + -175 -1992 + -0 -2000 + 174 -1992 + 347 -1970 + 517 -1932 + 684 -1879 + 845 -1813 + 1000 -1732 + 1147 -1638 + 1285 -1532 + 1414 -1414 + 1532 -1286 + 1638 -1147 + 1732 -1000 + 1812 -846 + 1879 -684 + 1932 -518 + 1970 -348 + 1992 -175 diff --git a/data/SET-1/SD17.txt b/data/SET-1/SD17.txt new file mode 100644 index 0000000..b45565e --- /dev/null +++ b/data/SET-1/SD17.txt @@ -0,0 +1,163 @@ +160 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 707 707 + 0 1000 + -707 707 + -1000 0 + -707 -707 + -0 -1000 + 707 -707 + 2000 0 + 1414 1414 + 0 2000 + -1414 1414 + -2000 0 + -1414 -1414 + -0 -2000 + 1414 -1414 + 3000 0 + 2121 2121 + 0 3000 + -2121 2121 + -3000 0 + -2122 -2121 + -0 -3000 + 2121 -2122 + 4000 0 + 2828 2828 + 0 4000 + -2828 2829 + -4000 0 + -2829 -2828 + -1 -4000 + 2828 -2829 + 5000 0 + 3536 3535 + 0 5000 + -3535 3536 + -5000 0 + -3536 -3535 + -1 -5000 + 3535 -3536 + 6000 0 + 4243 4243 + 0 6000 + -4242 4243 + -6000 1 + -4243 -4242 + -1 -6000 + 4242 -4243 + 7000 0 + 4950 4950 + 0 7000 + -4949 4950 + -7000 1 + -4950 -4949 + -1 -7000 + 4949 -4951 + 8000 0 + 5657 5657 + 0 8000 + -5656 5657 + -8000 1 + -5658 -5656 + -1 -8000 + 5656 -5658 + 9000 0 + 6364 6364 + 0 9000 + -6364 6364 + -9000 1 + -6365 -6363 + -1 -9000 + 6363 -6365 + 10000 0 + 7071 7071 + 0 10000 + -7071 7072 + -10000 1 + -7072 -7070 + -1 -10000 + 7070 -7072 + 11000 0 + 7778 7778 + 1 11000 + -7778 7779 + -11000 1 + -7779 -7777 + -2 -11000 + 7777 -7779 + 12000 0 + 8485 8485 + 1 12000 + -8485 8486 + -12000 1 + -8486 -8484 + -2 -12000 + 8484 -8487 + 13000 0 + 9193 9192 + 1 13000 + -9192 9193 + -13000 1 + -9193 -9191 + -2 -13000 + 9191 -9194 + 14000 0 + 9900 9899 + 1 14000 + -9899 9900 + -14000 1 + -9901 -9898 + -2 -14000 + 9898 -9901 + 15000 0 + 10607 10606 + 1 15000 + -10606 10607 + -15000 1 + -10608 -10605 + -2 -15000 + 10605 -10608 + 16000 0 + 11314 11313 + 1 16000 + -11313 11314 + -16000 1 + -11315 -11312 + -2 -16000 + 11312 -11316 + 17000 0 + 12021 12021 + 1 17000 + -12020 12022 + -17000 2 + -12022 -12019 + -2 -17000 + 12019 -12023 + 18000 0 + 12728 12728 + 1 18000 + -12727 12729 + -18000 2 + -12729 -12726 + -3 -18000 + 12726 -12730 + 19000 0 + 13435 13435 + 1 19000 + -13434 13436 + -19000 2 + -13437 -13433 + -3 -19000 + 13433 -13437 + 20000 0 + 14142 14142 + 1 20000 + -14141 14143 + -20000 2 + -14144 -14140 + -3 -20000 + 14140 -14144 diff --git a/data/SET-1/SD18.txt b/data/SET-1/SD18.txt new file mode 100644 index 0000000..31dd76c --- /dev/null +++ b/data/SET-1/SD18.txt @@ -0,0 +1,163 @@ +160 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 +0 0 + 1000 0 + 924 383 + 707 707 + 383 924 + 0 1000 + -383 924 + -707 707 + -924 383 + -1000 0 + -924 -383 + -707 -707 + -383 -924 + -0 -1000 + 383 -924 + 707 -707 + 924 -383 + 2000 0 + 1848 765 + 1414 1414 + 765 1848 + 0 2000 + -765 1848 + -1414 1414 + -1848 766 + -2000 0 + -1848 -765 + -1414 -1414 + -766 -1848 + -0 -2000 + 765 -1848 + 1414 -1414 + 1848 -766 + 3000 0 + 2772 1148 + 2121 2121 + 1148 2772 + 0 3000 + -1148 2772 + -2121 2121 + -2772 1148 + -3000 0 + -2772 -1148 + -2122 -2121 + -1148 -2771 + -0 -3000 + 1148 -2772 + 2121 -2122 + 2771 -1149 + 4000 0 + 3696 1531 + 2828 2828 + 1531 3695 + 0 4000 + -1531 3696 + -2828 2829 + -3695 1531 + -4000 0 + -3696 -1530 + -2829 -2828 + -1531 -3695 + -1 -4000 + 1530 -3696 + 2828 -2829 + 3695 -1531 + 5000 0 + 4619 1913 + 3536 3535 + 1914 4619 + 0 5000 + -1913 4620 + -3535 3536 + -4619 1914 + -5000 0 + -4620 -1913 + -3536 -3535 + -1914 -4619 + -1 -5000 + 1913 -4620 + 3535 -3536 + 4619 -1914 + 6000 0 + 5543 2296 + 4243 4243 + 2296 5543 + 0 6000 + -2296 5543 + -4242 4243 + -5543 2297 + -6000 1 + -5544 -2296 + -4243 -4242 + -2297 -5543 + -1 -6000 + 2295 -5544 + 4242 -4243 + 5543 -2297 + 7000 0 + 6467 2679 + 4950 4950 + 2679 6467 + 0 7000 + -2678 6467 + -4949 4950 + -6467 2679 + -7000 1 + -6467 -2678 + -4950 -4949 + -2680 -6467 + -1 -7000 + 2678 -6468 + 4949 -4951 + 6467 -2680 + 8000 0 + 7391 3061 + 5657 5657 + 3062 7391 + 0 8000 + -3061 7391 + -5656 5657 + -7391 3062 + -8000 1 + -7391 -3061 + -5658 -5656 + -3062 -7391 + -1 -8000 + 3060 -7391 + 5656 -5658 + 7391 -3063 + 9000 0 + 8315 3444 + 6364 6364 + 3444 8315 + 0 9000 + -3444 8315 + -6364 6364 + -8315 3445 + -9000 1 + -8315 -3443 + -6365 -6363 + -3445 -8314 + -1 -9000 + 3443 -8315 + 6363 -6365 + 8314 -3446 + 10000 0 + 9239 3827 + 7071 7071 + 3827 9239 + 0 10000 + -3826 9239 + -7071 7072 + -9238 3828 + -10000 1 + -9239 -3826 + -7072 -7070 + -3828 -9238 + -1 -10000 + 3825 -9239 + 7070 -7072 + 9238 -3828 diff --git a/data/SET-1/SD19.txt b/data/SET-1/SD19.txt new file mode 100644 index 0000000..ebd455f --- /dev/null +++ b/data/SET-1/SD19.txt @@ -0,0 +1,195 @@ +192 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 924 383 + 707 707 + 383 924 + 0 1000 + -383 924 + -707 707 + -924 383 + -1000 0 + -924 -383 + -707 -707 + -383 -924 + -0 -1000 + 383 -924 + 707 -707 + 924 -383 + 2000 0 + 1848 765 + 1414 1414 + 765 1848 + 0 2000 + -765 1848 + -1414 1414 + -1848 766 + -2000 0 + -1848 -765 + -1414 -1414 + -766 -1848 + -0 -2000 + 765 -1848 + 1414 -1414 + 1848 -766 + 3000 0 + 2772 1148 + 2121 2121 + 1148 2772 + 0 3000 + -1148 2772 + -2121 2121 + -2772 1148 + -3000 0 + -2772 -1148 + -2122 -2121 + -1148 -2771 + -0 -3000 + 1148 -2772 + 2121 -2122 + 2771 -1149 + 4000 0 + 3696 1531 + 2828 2828 + 1531 3695 + 0 4000 + -1531 3696 + -2828 2829 + -3695 1531 + -4000 0 + -3696 -1530 + -2829 -2828 + -1531 -3695 + -1 -4000 + 1530 -3696 + 2828 -2829 + 3695 -1531 + 5000 0 + 4619 1913 + 3536 3535 + 1914 4619 + 0 5000 + -1913 4620 + -3535 3536 + -4619 1914 + -5000 0 + -4620 -1913 + -3536 -3535 + -1914 -4619 + -1 -5000 + 1913 -4620 + 3535 -3536 + 4619 -1914 + 6000 0 + 5543 2296 + 4243 4243 + 2296 5543 + 0 6000 + -2296 5543 + -4242 4243 + -5543 2297 + -6000 1 + -5544 -2296 + -4243 -4242 + -2297 -5543 + -1 -6000 + 2295 -5544 + 4242 -4243 + 5543 -2297 + 7000 0 + 6467 2679 + 4950 4950 + 2679 6467 + 0 7000 + -2678 6467 + -4949 4950 + -6467 2679 + -7000 1 + -6467 -2678 + -4950 -4949 + -2680 -6467 + -1 -7000 + 2678 -6468 + 4949 -4951 + 6467 -2680 + 8000 0 + 7391 3061 + 5657 5657 + 3062 7391 + 0 8000 + -3061 7391 + -5656 5657 + -7391 3062 + -8000 1 + -7391 -3061 + -5658 -5656 + -3062 -7391 + -1 -8000 + 3060 -7391 + 5656 -5658 + 7391 -3063 + 9000 0 + 8315 3444 + 6364 6364 + 3444 8315 + 0 9000 + -3444 8315 + -6364 6364 + -8315 3445 + -9000 1 + -8315 -3443 + -6365 -6363 + -3445 -8314 + -1 -9000 + 3443 -8315 + 6363 -6365 + 8314 -3446 + 10000 0 + 9239 3827 + 7071 7071 + 3827 9239 + 0 10000 + -3826 9239 + -7071 7072 + -9238 3828 + -10000 1 + -9239 -3826 + -7072 -7070 + -3828 -9238 + -1 -10000 + 3825 -9239 + 7070 -7072 + 9238 -3828 + 11000 0 + 10163 4209 + 7778 7778 + 4210 10163 + 1 11000 + -4209 10163 + -7778 7779 + -10162 4210 + -11000 1 + -10163 -4208 + -7779 -7777 + -4211 -10162 + -2 -11000 + 4208 -10163 + 7777 -7779 + 10162 -4211 + 12000 0 + 11087 4592 + 8485 8485 + 4593 11086 + 1 12000 + -4592 11087 + -8485 8486 + -11086 4593 + -12000 1 + -11087 -4591 + -8486 -8484 + -4594 -11086 + -2 -12000 + 4591 -11087 + 8484 -8487 + 11086 -4594 diff --git a/data/SET-1/SD2.txt b/data/SET-1/SD2.txt new file mode 100644 index 0000000..28d27eb --- /dev/null +++ b/data/SET-1/SD2.txt @@ -0,0 +1,21 @@ +16 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 +0 0 +1000 0 + 0 1000 +-1000 0 + -0 -1000 + 2000 0 + 0 2000 +-2000 0 + -0 -2000 + 3000 0 + 0 3000 + -3000 0 + -0 -3000 + 4000 0 + 0 4000 + -4000 0 + -1 -4000 + + diff --git a/data/SET-1/SD20.txt b/data/SET-1/SD20.txt new file mode 100644 index 0000000..ae7148e --- /dev/null +++ b/data/SET-1/SD20.txt @@ -0,0 +1,243 @@ +240 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 866 500 + 500 866 + 0 1000 + -500 866 + -866 500 + -1000 0 + -866 -500 + -500 -866 + -0 -1000 + 500 -866 + 866 -500 + 2000 0 + 1732 1000 + 1000 1732 + 0 2000 + -1000 1732 + -1732 1000 + -2000 0 + -1732 -1000 + -1000 -1732 + -0 -2000 + 1000 -1732 + 1732 -1000 + 3000 0 + 2598 1500 + 1500 2598 + 0 3000 + -1500 2598 + -2598 1500 + -3000 0 + -2598 -1500 + -1500 -2598 + -0 -3000 + 1500 -2598 + 2598 -1500 + 4000 0 + 3464 2000 + 2000 3464 + 0 4000 + -2000 3464 + -3464 2000 + -4000 0 + -3464 -2000 + -2000 -3464 + -1 -4000 + 1999 -3464 + 3464 -2001 + 5000 0 + 4330 2500 + 2500 4330 + 0 5000 + -2500 4330 + -4330 2500 + -5000 0 + -4330 -2500 + -2501 -4330 + -1 -5000 + 2499 -4331 + 4330 -2501 + 6000 0 + 5196 3000 + 3000 5196 + 0 6000 + -3000 5196 + -5196 3000 + -6000 1 + -5196 -2999 + -3001 -5196 + -1 -6000 + 2999 -5197 + 5196 -3001 + 7000 0 + 6062 3500 + 3500 6062 + 0 7000 + -3500 6062 + -6062 3500 + -7000 1 + -6063 -3499 + -3501 -6062 + -1 -7000 + 3499 -6063 + 6062 -3501 + 8000 0 + 6928 4000 + 4000 6928 + 0 8000 + -4000 6928 + -6928 4001 + -8000 1 + -6929 -3999 + -4001 -6928 + -1 -8000 + 3999 -6929 + 6928 -4001 + 9000 0 + 7794 4500 + 4500 7794 + 0 9000 + -4500 7795 + -7794 4501 + -9000 1 + -7795 -4499 + -4501 -7794 + -1 -9000 + 4499 -7795 + 7793 -4501 + 10000 0 + 8660 5000 + 5000 8660 + 0 10000 + -4999 8661 + -8660 5001 + -10000 1 + -8661 -4999 + -5001 -8660 + -1 -10000 + 4999 -8661 + 8659 -5001 + 11000 0 + 9526 5500 + 5500 9526 + 1 11000 + -5499 9527 + -9526 5501 + -11000 1 + -9527 -5499 + -5501 -9526 + -2 -11000 + 5499 -9527 + 9525 -5502 + 12000 0 + 10392 6000 + 6000 10392 + 1 12000 + -5999 10393 + -10392 6001 + -12000 1 + -10393 -5999 + -6001 -10392 + -2 -12000 + 5998 -10393 + 10391 -6002 + 13000 0 + 11258 6500 + 6500 11258 + 1 13000 + -6499 11259 + -11258 6501 + -13000 1 + -11259 -6499 + -6501 -11258 + -2 -13000 + 6498 -11259 + 11257 -6502 + 14000 0 + 12124 7000 + 7000 12124 + 1 14000 + -6999 12125 + -12124 7001 + -14000 1 + -12125 -6999 + -7001 -12123 + -2 -14000 + 6998 -12125 + 12123 -7002 + 15000 0 + 12990 7500 + 7500 12990 + 1 15000 + -7499 12991 + -12990 7501 + -15000 1 + -12991 -7499 + -7502 -12989 + -2 -15000 + 7498 -12992 + 12989 -7502 + 16000 0 + 13857 8000 + 8000 13856 + 1 16000 + -7999 13857 + -13856 8001 + -16000 1 + -13857 -7999 + -8002 -13855 + -2 -16000 + 7998 -13858 + 13855 -8002 + 17000 0 + 14723 8500 + 8500 14722 + 1 17000 + -8499 14723 + -14722 8501 + -17000 2 + -14723 -8498 + -8502 -14721 + -2 -17000 + 8498 -14724 + 14721 -8503 + 18000 0 + 15589 9000 + 9000 15588 + 1 18000 + -8999 15589 + -15588 9001 + -18000 2 + -15589 -8998 + -9002 -15587 + -3 -18000 + 8998 -15590 + 15587 -9003 + 19000 0 + 16455 9500 + 9501 16454 + 1 19000 + -9499 16455 + -16454 9501 + -19000 2 + -16456 -9498 + -9502 -16453 + -3 -19000 + 9497 -16456 + 16453 -9503 + 20000 0 + 17321 10000 + 10001 17320 + 1 20000 + -9999 17321 + -17320 10001 + -20000 2 + -17322 -9998 + -10002 -17319 + -3 -20000 + 9997 -17322 + 17319 -10003 diff --git a/data/SET-1/SD21.txt b/data/SET-1/SD21.txt new file mode 100644 index 0000000..5118834 --- /dev/null +++ b/data/SET-1/SD21.txt @@ -0,0 +1,291 @@ +288 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 996 87 + 985 174 + 966 259 + 940 342 + 906 423 + 866 500 + 819 574 + 766 643 + 707 707 + 643 766 + 574 819 + 500 866 + 423 906 + 342 940 + 259 966 + 174 985 + 87 996 + 0 1000 + -87 996 + -174 985 + -259 966 + -342 940 + -423 906 + -500 866 + -574 819 + -643 766 + -707 707 + -766 643 + -819 574 + -866 500 + -906 423 + -940 342 + -966 259 + -985 174 + -996 87 + -1000 0 + -996 -87 + -985 -174 + -966 -259 + -940 -342 + -906 -423 + -866 -500 + -819 -573 + -766 -643 + -707 -707 + -643 -766 + -574 -819 + -500 -866 + -423 -906 + -342 -940 + -259 -966 + -174 -985 + -87 -996 + -0 -1000 + 87 -996 + 174 -985 + 259 -966 + 342 -940 + 422 -906 + 500 -866 + 573 -819 + 643 -766 + 707 -707 + 766 -643 + 819 -574 + 866 -500 + 906 -423 + 940 -342 + 966 -259 + 985 -174 + 996 -87 + 2000 0 + 1992 174 + 1970 347 + 1932 518 + 1879 684 + 1813 845 + 1732 1000 + 1638 1147 + 1532 1286 + 1414 1414 + 1286 1532 + 1147 1638 + 1000 1732 + 845 1813 + 684 1879 + 518 1932 + 347 1970 + 174 1992 + 0 2000 + -174 1992 + -347 1970 + -518 1932 + -684 1879 + -845 1813 + -1000 1732 + -1147 1638 + -1285 1532 + -1414 1414 + -1532 1286 + -1638 1147 + -1732 1000 + -1813 845 + -1879 684 + -1932 518 + -1970 347 + -1992 174 + -2000 0 + -1992 -174 + -1970 -347 + -1932 -517 + -1879 -684 + -1813 -845 + -1732 -1000 + -1638 -1147 + -1532 -1285 + -1414 -1414 + -1286 -1532 + -1147 -1638 + -1000 -1732 + -845 -1813 + -684 -1879 + -518 -1932 + -348 -1970 + -175 -1992 + -0 -2000 + 174 -1992 + 347 -1970 + 517 -1932 + 684 -1879 + 845 -1813 + 1000 -1732 + 1147 -1638 + 1285 -1532 + 1414 -1414 + 1532 -1286 + 1638 -1147 + 1732 -1000 + 1812 -846 + 1879 -684 + 1932 -518 + 1970 -348 + 1992 -175 + 3000 0 + 2989 261 + 2954 521 + 2898 776 + 2819 1026 + 2719 1268 + 2598 1500 + 2457 1721 + 2298 1928 + 2121 2121 + 1928 2298 + 1721 2457 + 1500 2598 + 1268 2719 + 1026 2819 + 777 2898 + 521 2954 + 262 2989 + 0 3000 + -261 2989 + -521 2954 + -776 2898 + -1026 2819 + -1268 2719 + -1500 2598 + -1721 2458 + -1928 2298 + -2121 2121 + -2298 1929 + -2457 1721 + -2598 1500 + -2719 1268 + -2819 1026 + -2898 777 + -2954 521 + -2989 262 + -3000 0 + -2989 -261 + -2954 -521 + -2898 -776 + -2819 -1026 + -2719 -1268 + -2598 -1500 + -2458 -1720 + -2298 -1928 + -2122 -2121 + -1929 -2298 + -1721 -2457 + -1500 -2598 + -1268 -2719 + -1026 -2819 + -777 -2898 + -521 -2954 + -262 -2989 + -0 -3000 + 261 -2989 + 521 -2954 + 776 -2898 + 1026 -2819 + 1267 -2719 + 1500 -2598 + 1720 -2458 + 1928 -2298 + 2121 -2122 + 2298 -1929 + 2457 -1721 + 2598 -1500 + 2719 -1268 + 2819 -1027 + 2898 -777 + 2954 -521 + 2989 -262 + 4000 0 + 3985 349 + 3939 695 + 3864 1035 + 3759 1368 + 3625 1690 + 3464 2000 + 3277 2294 + 3064 2571 + 2828 2828 + 2571 3064 + 2294 3277 + 2000 3464 + 1691 3625 + 1368 3759 + 1035 3864 + 695 3939 + 349 3985 + 0 4000 + -348 3985 + -694 3939 + -1035 3864 + -1368 3759 + -1690 3625 + -2000 3464 + -2294 3277 + -2571 3064 + -2828 2829 + -3064 2571 + -3276 2295 + -3464 2000 + -3625 1691 + -3759 1368 + -3864 1036 + -3939 695 + -3985 349 + -4000 0 + -3985 -348 + -3939 -694 + -3864 -1035 + -3759 -1368 + -3625 -1690 + -3464 -2000 + -3277 -2294 + -3064 -2571 + -2829 -2828 + -2572 -3064 + -2295 -3276 + -2000 -3464 + -1691 -3625 + -1369 -3759 + -1036 -3864 + -695 -3939 + -349 -3985 + -1 -4000 + 348 -3985 + 694 -3939 + 1035 -3864 + 1368 -3759 + 1690 -3625 + 1999 -3464 + 2294 -3277 + 2571 -3065 + 2828 -2829 + 3064 -2572 + 3276 -2295 + 3464 -2001 + 3625 -1691 + 3759 -1369 + 3864 -1036 + 3939 -695 + 3985 -349 diff --git a/data/SET-1/SD3.txt b/data/SET-1/SD3.txt new file mode 100644 index 0000000..049626d --- /dev/null +++ b/data/SET-1/SD3.txt @@ -0,0 +1,20 @@ +16 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 707 707 + 0 1000 + -707 707 +-1000 0 + -707 -707 + -0 -1000 + 707 -707 + 2000 0 + 1414 1414 + 0 2000 +-1414 1414 +-2000 0 +-1414 -1414 + -0 -2000 + 1414 -1414 + diff --git a/data/SET-1/SD4.txt b/data/SET-1/SD4.txt new file mode 100644 index 0000000..6247497 --- /dev/null +++ b/data/SET-1/SD4.txt @@ -0,0 +1,29 @@ +24 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 866 500 + 500 866 + 0 1000 + -500 866 + -866 500 +-1000 0 + -866 -500 + -500 -866 + -0 -1000 + 500 -866 + 866 -500 + 2000 0 + 1732 1000 + 1000 1732 + 0 2000 +-1000 1732 +-1732 1000 +-2000 0 +-1732 -1000 +-1000 -1732 + -0 -2000 + 1000 -1732 + 1732 -1000 + + diff --git a/data/SET-1/SD5.txt b/data/SET-1/SD5.txt new file mode 100644 index 0000000..6c07318 --- /dev/null +++ b/data/SET-1/SD5.txt @@ -0,0 +1,35 @@ +32 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 707 707 + 0 1000 + -707 707 +-1000 0 + -707 -707 + -0 -1000 + 707 -707 + 2000 0 + 1414 1414 + 0 2000 +-1414 1414 +-2000 0 +-1414 -1414 + -0 -2000 + 1414 -1414 + 3000 0 + 2121 2121 + 0 3000 +-2121 2121 +-3000 0 +-2122 -2121 + -0 -3000 + 2121 -2122 + 4000 0 + 2828 2828 + 0 4000 +-2828 2829 +-4000 0 +-2829 -2828 + -1 -4000 + 2828 -2829 diff --git a/data/SET-1/SD6.txt b/data/SET-1/SD6.txt new file mode 100644 index 0000000..9b851ef --- /dev/null +++ b/data/SET-1/SD6.txt @@ -0,0 +1,35 @@ +32 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 924 383 + 707 707 + 383 924 + 0 1000 + -383 924 + -707 707 + -924 383 +-1000 0 + -924 -383 + -707 -707 + -383 -924 + -0 -1000 + 383 -924 + 707 -707 + 924 -383 + 2000 0 + 1848 765 + 1414 1414 + 765 1848 + 0 2000 + -765 1848 +-1414 1414 +-1848 766 +-2000 0 +-1848 -765 +-1414 -1414 + -766 -1848 + -0 -2000 + 765 -1848 + 1414 -1414 + 1848 -766 diff --git a/data/SET-1/SD7.txt b/data/SET-1/SD7.txt new file mode 100644 index 0000000..83f17a9 --- /dev/null +++ b/data/SET-1/SD7.txt @@ -0,0 +1,43 @@ +40 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 0 1000 + -1000 0 + -0 -1000 + 2000 0 + 0 2000 + -2000 0 + -0 -2000 + 3000 0 + 0 3000 + -3000 0 + -0 -3000 + 4000 0 + 0 4000 + -4000 0 + -1 -4000 + 5000 0 + 0 5000 + -5000 0 + -1 -5000 + 6000 0 + 0 6000 + -6000 1 + -1 -6000 + 7000 0 + 0 7000 + -7000 1 + -1 -7000 + 8000 0 + 0 8000 + -8000 1 + -1 -8000 + 9000 0 + 0 9000 + -9000 1 + -1 -9000 + 10000 0 + 0 10000 +-10000 1 + -1 -10000 diff --git a/data/SET-1/SD8.txt b/data/SET-1/SD8.txt new file mode 100644 index 0000000..3db6bd3 --- /dev/null +++ b/data/SET-1/SD8.txt @@ -0,0 +1,51 @@ +48 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 0 1000 + -1000 0 + -0 -1000 + 2000 0 + 0 2000 + -2000 0 + -0 -2000 + 3000 0 + 0 3000 + -3000 0 + -0 -3000 + 4000 0 + 0 4000 + -4000 0 + -1 -4000 + 5000 0 + 0 5000 + -5000 0 + -1 -5000 + 6000 0 + 0 6000 + -6000 1 + -1 -6000 + 7000 0 + 0 7000 + -7000 1 + -1 -7000 + 8000 0 + 0 8000 + -8000 1 + -1 -8000 + 9000 0 + 0 9000 + -9000 1 + -1 -9000 + 10000 0 + 0 10000 +-10000 1 + -1 -10000 + 11000 0 + 1 11000 +-11000 1 + -2 -11000 + 12000 0 + 1 12000 +-12000 1 + -2 -12000 \ No newline at end of file diff --git a/data/SET-1/SD9.txt b/data/SET-1/SD9.txt new file mode 100644 index 0000000..86c0369 --- /dev/null +++ b/data/SET-1/SD9.txt @@ -0,0 +1,51 @@ +48 100 +60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 60 90 + 0 0 + 1000 0 + 866 500 + 500 866 + 0 1000 + -500 866 + -866 500 +-1000 0 + -866 -500 + -500 -866 + -0 -1000 + 500 -866 + 866 -500 + 2000 0 + 1732 1000 + 1000 1732 + 0 2000 +-1000 1732 +-1732 1000 +-2000 0 +-1732 -1000 +-1000 -1732 + -0 -2000 + 1000 -1732 + 1732 -1000 + 3000 0 + 2598 1500 + 1500 2598 + 0 3000 +-1500 2598 +-2598 1500 +-3000 0 +-2598 -1500 +-1500 -2598 + -0 -3000 + 1500 -2598 + 2598 -1500 + 4000 0 + 3464 2000 + 2000 3464 + 0 4000 +-2000 3464 +-3464 2000 +-4000 0 +-3464 -2000 +-2000 -3464 + -1 -4000 + 1999 -3464 + 3464 -2001 diff --git a/data/SET-2/S101D1.sd b/data/SET-2/S101D1.sd new file mode 100644 index 0000000..d85c5b7 --- /dev/null +++ b/data/SET-2/S101D1.sd @@ -0,0 +1,103 @@ +100 160 +3 4 5 14 2 5 5 9 2 10 5 4 2 14 4 5 3 15 4 10 3 14 2 13 3 10 3 15 4 4 5 15 4 5 8 15 5 10 10 13 6 14 13 11 8 15 15 10 9 15 15 8 11 14 15 7 13 11 12 5 14 8 7 4 15 6 2 4 15 3 8 3 14 2 13 3 11 3 15 3 7 4 15 2 2 5 12 2 8 5 6 2 13 5 2 2 15 4 8 3 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 diff --git a/data/SET-2/S101D2.sd b/data/SET-2/S101D2.sd new file mode 100644 index 0000000..999a1d3 --- /dev/null +++ b/data/SET-2/S101D2.sd @@ -0,0 +1,103 @@ +100 160 +38 18 31 24 26 18 41 23 18 18 47 21 30 18 46 18 40 19 39 16 45 20 27 21 47 21 18 27 47 23 31 33 44 25 41 39 40 28 46 44 36 31 47 47 32 35 45 47 28 39 40 42 26 43 34 33 23 46 28 20 21 47 22 25 20 46 17 37 19 41 18 45 18 32 21 47 18 20 23 43 18 24 23 32 18 37 23 19 18 45 22 25 18 47 20 36 18 43 17 43 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 diff --git a/data/SET-2/S101D3.sd b/data/SET-2/S101D3.sd new file mode 100644 index 0000000..a7452cf --- /dev/null +++ b/data/SET-2/S101D3.sd @@ -0,0 +1,103 @@ +100 160 +21 33 79 29 37 45 75 33 60 58 68 38 74 69 60 44 79 77 52 52 77 79 44 60 69 74 38 68 58 59 33 75 45 36 29 79 33 22 25 78 22 49 23 71 17 70 21 57 24 79 20 35 29 75 20 22 31 59 20 48 31 34 20 69 30 24 20 79 26 48 21 76 21 66 22 60 18 76 24 35 28 79 27 24 40 77 31 50 52 71 35 68 64 64 41 78 74 55 48 79 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 diff --git a/data/SET-2/S101D5.sd b/data/SET-2/S101D5.sd new file mode 100644 index 0000000..07d9d16 --- /dev/null +++ b/data/SET-2/S101D5.sd @@ -0,0 +1,103 @@ +100 160 +52 59 62 103 52 63 63 83 52 88 63 57 52 106 61 64 52 111 57 86 53 104 50 102 55 84 53 110 57 58 64 111 60 65 75 108 64 89 88 101 69 104 100 93 75 111 108 85 83 110 111 77 91 102 107 71 99 91 94 65 106 79 72 61 111 67 50 58 111 56 77 55 105 48 99 54 91 55 111 53 71 60 109 52 50 63 94 52 77 64 70 52 98 62 52 52 110 59 77 53 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 diff --git a/data/SET-2/S51D1.sd b/data/SET-2/S51D1.sd new file mode 100644 index 0000000..a1cee24 --- /dev/null +++ b/data/SET-2/S51D1.sd @@ -0,0 +1,53 @@ +50 160 +3 4 5 14 2 5 5 9 2 10 5 4 2 14 4 5 3 15 4 10 3 14 2 13 3 10 3 15 4 4 5 15 4 5 8 15 5 10 10 13 6 14 13 11 8 15 15 10 9 15 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 diff --git a/data/SET-2/S51D2.sd b/data/SET-2/S51D2.sd new file mode 100644 index 0000000..1e614d0 --- /dev/null +++ b/data/SET-2/S51D2.sd @@ -0,0 +1,54 @@ +50 160 +33 23 46 28 21 21 47 22 24 20 46 17 37 19 41 18 45 18 32 21 47 18 20 23 43 18 24 23 33 18 37 23 19 18 45 22 25 18 47 20 36 18 43 17 43 19 33 19 47 20 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 + diff --git a/data/SET-2/S51D3.sd b/data/SET-2/S51D3.sd new file mode 100644 index 0000000..122a91d --- /dev/null +++ b/data/SET-2/S51D3.sd @@ -0,0 +1,55 @@ +50 160 +20 27 30 71 20 31 31 51 20 56 31 25 20 74 29 32 20 79 25 54 21 72 18 70 23 52 21 78 25 26 32 79 28 33 43 76 32 57 56 69 37 72 68 61 43 79 76 53 51 78 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 + + diff --git a/data/SET-2/S51D4.sd b/data/SET-2/S51D4.sd new file mode 100644 index 0000000..bae876d --- /dev/null +++ b/data/SET-2/S51D4.sd @@ -0,0 +1,53 @@ +50 160 +43 43 25 143 35 89 27 131 23 122 30 94 25 139 34 42 45 143 40 45 69 137 48 93 94 124 58 127 118 108 70 142 136 92 84 141 143 77 100 127 136 63 117 106 112 52 131 81 70 43 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 diff --git a/data/SET-2/S51D5.sd b/data/SET-2/S51D5.sd new file mode 100644 index 0000000..03d45c1 --- /dev/null +++ b/data/SET-2/S51D5.sd @@ -0,0 +1,53 @@ +50 160 +59 69 56 108 49 94 54 96 53 109 53 78 59 111 52 53 62 99 52 69 64 78 52 93 63 51 52 108 60 70 52 111 56 91 54 100 49 104 55 79 55 111 58 51 66 111 61 71 78 106 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 diff --git a/data/SET-2/S51D6.sd b/data/SET-2/S51D6.sd new file mode 100644 index 0000000..fe33dbf --- /dev/null +++ b/data/SET-2/S51D6.sd @@ -0,0 +1,54 @@ +50 160 +118 118 114 143 116 130 114 140 113 138 115 131 114 142 116 118 119 143 118 119 125 142 120 131 131 139 122 139 137 135 125 143 142 131 129 143 143 127 133 139 142 123 137 134 136 121 140 128 125 118 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 + diff --git a/data/SET-2/S76D1.sd b/data/SET-2/S76D1.sd new file mode 100644 index 0000000..0615e3c --- /dev/null +++ b/data/SET-2/S76D1.sd @@ -0,0 +1,79 @@ +75 160 +9 5 15 7 4 4 15 4 5 3 15 2 11 3 13 3 15 3 9 4 15 3 4 5 13 2 5 5 9 2 11 5 3 2 14 4 6 3 15 3 10 3 14 2 14 3 9 3 15 4 3 5 15 4 6 8 15 5 11 11 13 6 14 13 11 8 15 15 10 9 15 15 8 11 13 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 + diff --git a/data/SET-2/S76D2.sd b/data/SET-2/S76D2.sd new file mode 100644 index 0000000..898a3d0 --- /dev/null +++ b/data/SET-2/S76D2.sd @@ -0,0 +1,79 @@ +75 160 +44 22 22 18 47 20 34 18 44 17 42 19 35 18 46 20 22 23 47 22 23 29 46 24 35 35 43 26 43 41 39 29 47 46 35 33 47 47 31 37 43 46 27 41 38 40 25 44 32 29 23 47 26 16 21 47 20 29 20 45 16 40 19 38 19 47 18 28 22 46 18 16 23 40 18 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 + diff --git a/data/SET-2/S76D3.sd b/data/SET-2/S76D3.sd new file mode 100644 index 0000000..0c74a34 --- /dev/null +++ b/data/SET-2/S76D3.sd @@ -0,0 +1,79 @@ +75 160 +27 37 24 76 17 62 22 64 21 77 21 46 27 79 20 21 30 67 20 37 32 46 20 61 31 19 20 76 28 38 20 79 24 59 22 68 17 72 23 47 23 79 26 19 34 79 29 39 46 74 33 61 59 67 38 75 70 59 45 79 78 51 52 77 79 44 61 68 73 37 69 57 58 32 75 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 + diff --git a/data/SET-2/S76D4.sd b/data/SET-2/S76D4.sd new file mode 100644 index 0000000..cd9bbc9 --- /dev/null +++ b/data/SET-2/S76D4.sd @@ -0,0 +1,79 @@ +75 160 +86 46 139 65 36 39 143 41 51 33 138 22 102 29 118 25 135 26 82 37 143 25 35 44 124 24 50 47 84 24 100 47 31 24 133 42 52 25 143 33 96 27 126 20 126 31 86 28 141 35 32 49 143 41 55 73 135 49 101 98 121 60 131 121 105 72 143 138 89 87 139 143 74 103 124 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 + diff --git a/data/SET-3/p01_00.cri b/data/SET-3/p01_00.cri new file mode 100644 index 0000000..9b80356 --- /dev/null +++ b/data/SET-3/p01_00.cri @@ -0,0 +1,53 @@ +50 160 +7 30 16 9 21 15 19 23 11 5 19 29 23 21 10 15 3 41 9 28 8 8 16 10 28 7 15 14 6 19 11 12 23 26 17 6 9 15 14 7 27 13 11 16 10 5 25 17 18 10 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 \ No newline at end of file diff --git a/data/SET-3/p01_1030.cri b/data/SET-3/p01_1030.cri new file mode 100644 index 0000000..fafcbdb --- /dev/null +++ b/data/SET-3/p01_1030.cri @@ -0,0 +1,53 @@ +50 160 +43 28 23 33 45 43 31 19 22 17 41 28 30 32 38 25 18 32 43 33 19 36 36 34 41 39 17 40 20 19 43 41 42 22 43 31 40 31 31 42 22 18 36 37 44 25 31 29 43 35 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 \ No newline at end of file diff --git a/data/SET-3/p01_1050.cri b/data/SET-3/p01_1050.cri new file mode 100644 index 0000000..9628058 --- /dev/null +++ b/data/SET-3/p01_1050.cri @@ -0,0 +1,53 @@ +50 160 +70 41 30 51 74 71 47 23 29 18 67 41 45 49 60 35 21 48 70 50 22 56 57 53 66 62 18 65 25 22 70 67 69 29 70 46 65 46 47 69 28 20 56 58 72 34 47 43 70 55 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 \ No newline at end of file diff --git a/data/SET-3/p01_1090.cri b/data/SET-3/p01_1090.cri new file mode 100644 index 0000000..4bde75b --- /dev/null +++ b/data/SET-3/p01_1090.cri @@ -0,0 +1,53 @@ +50 160 +125 66 45 87 132 126 79 30 42 20 119 67 75 82 105 54 27 81 125 85 29 96 99 90 117 109 20 115 35 29 124 118 122 43 124 77 115 77 78 122 40 24 97 101 128 52 79 71 124 95 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 \ No newline at end of file diff --git a/data/SET-3/p01_110.cri b/data/SET-3/p01_110.cri new file mode 100644 index 0000000..633aac8 --- /dev/null +++ b/data/SET-3/p01_110.cri @@ -0,0 +1,53 @@ +50 160 +13 6 4 9 14 13 8 2 4 1 13 6 8 8 11 5 2 8 13 9 2 10 10 9 12 11 1 12 3 2 13 13 13 4 13 8 12 8 8 13 3 2 10 11 14 5 8 7 13 10 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 \ No newline at end of file diff --git a/data/SET-3/p01_3070.cri b/data/SET-3/p01_3070.cri new file mode 100644 index 0000000..8cdf495 --- /dev/null +++ b/data/SET-3/p01_3070.cri @@ -0,0 +1,53 @@ +50 160 +102 73 62 83 106 103 79 55 61 50 99 73 77 81 92 67 53 80 102 82 54 88 89 85 98 94 50 97 57 54 102 99 101 61 102 78 97 78 79 101 60 52 88 90 104 66 79 75 102 87 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 \ No newline at end of file diff --git a/data/SET-3/p01_7090.cri b/data/SET-3/p01_7090.cri new file mode 100644 index 0000000..d50f801 --- /dev/null +++ b/data/SET-3/p01_7090.cri @@ -0,0 +1,53 @@ +50 160 +139 124 119 129 141 139 127 115 118 113 137 124 126 128 134 121 114 128 139 129 115 132 132 130 137 135 113 136 116 115 139 137 138 118 139 127 136 127 127 138 118 114 132 133 140 121 127 125 139 131 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 \ No newline at end of file diff --git a/data/SET-3/p02_00.cri b/data/SET-3/p02_00.cri new file mode 100644 index 0000000..ee66ad3 --- /dev/null +++ b/data/SET-3/p02_00.cri @@ -0,0 +1,78 @@ +75 140 +18 26 11 30 21 19 15 16 29 26 37 16 12 31 8 19 20 13 15 22 28 12 6 27 14 18 17 29 13 22 25 28 27 19 10 12 14 24 16 33 15 11 18 17 21 27 19 20 5 22 12 19 22 16 7 26 14 21 24 13 15 18 11 28 9 37 30 10 8 11 3 1 6 10 20 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 \ No newline at end of file diff --git a/data/SET-3/p02_1030.cri b/data/SET-3/p02_1030.cri new file mode 100644 index 0000000..066586f --- /dev/null +++ b/data/SET-3/p02_1030.cri @@ -0,0 +1,78 @@ +75 140 +37 25 20 29 39 38 27 17 19 14 36 25 27 28 33 22 16 28 37 29 16 31 32 30 36 34 14 35 18 16 37 36 37 20 37 27 35 27 27 37 19 15 31 32 38 21 27 26 37 31 32 32 16 39 34 34 22 33 25 23 38 21 22 22 23 27 37 16 41 38 40 39 37 41 15 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 \ No newline at end of file diff --git a/data/SET-3/p02_1050.cri b/data/SET-3/p02_1050.cri new file mode 100644 index 0000000..0582bee --- /dev/null +++ b/data/SET-3/p02_1050.cri @@ -0,0 +1,78 @@ +75 140 +61 36 26 45 64 62 41 20 25 15 59 36 40 43 53 30 18 42 61 44 19 49 50 46 58 55 15 57 22 19 61 58 60 26 61 40 57 41 41 60 24 17 49 51 63 29 41 38 61 48 51 50 18 65 55 54 31 53 36 32 63 28 31 30 33 40 61 19 69 62 67 64 61 69 16 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 \ No newline at end of file diff --git a/data/SET-3/p02_1090.cri b/data/SET-3/p02_1090.cri new file mode 100644 index 0000000..bfaf5ac --- /dev/null +++ b/data/SET-3/p02_1090.cri @@ -0,0 +1,78 @@ +75 140 +109 58 39 76 115 110 69 26 37 17 104 58 66 72 92 47 23 71 109 75 25 84 87 79 102 96 17 100 30 25 109 103 106 38 108 67 101 68 68 107 35 21 84 88 112 45 69 62 108 83 88 87 22 116 97 95 48 92 59 51 113 43 49 47 53 67 109 24 124 110 120 114 108 125 19 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 \ No newline at end of file diff --git a/data/SET-3/p02_110.cri b/data/SET-3/p02_110.cri new file mode 100644 index 0000000..94d7c94 --- /dev/null +++ b/data/SET-3/p02_110.cri @@ -0,0 +1,78 @@ +75 140 +12 6 3 8 12 12 7 2 3 1 11 6 7 7 10 4 2 7 12 8 2 9 9 8 11 10 1 11 2 2 12 11 11 3 12 7 11 7 7 11 3 1 9 9 12 4 7 6 11 9 9 9 1 12 10 10 5 10 6 5 12 4 5 4 5 7 12 2 13 12 13 12 11 13 1 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 \ No newline at end of file diff --git a/data/SET-3/p02_3070.cri b/data/SET-3/p02_3070.cri new file mode 100644 index 0000000..9a99117 --- /dev/null +++ b/data/SET-3/p02_3070.cri @@ -0,0 +1,78 @@ +75 140 +89 64 54 73 92 90 69 48 53 43 87 64 68 71 81 58 46 70 89 72 47 77 78 74 86 83 43 85 50 47 89 86 88 54 89 68 85 69 69 88 52 45 77 79 91 57 69 66 89 76 79 78 46 93 83 82 59 81 64 60 91 56 59 58 61 68 89 47 97 90 95 92 89 97 44 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 \ No newline at end of file diff --git a/data/SET-3/p02_7090.cri b/data/SET-3/p02_7090.cri new file mode 100644 index 0000000..c7cd658 --- /dev/null +++ b/data/SET-3/p02_7090.cri @@ -0,0 +1,78 @@ +75 140 +121 109 104 113 123 122 111 101 103 98 120 109 111 112 117 106 100 112 121 113 100 115 116 114 120 118 98 119 102 100 121 120 121 104 121 111 119 111 111 121 103 99 115 116 122 105 111 110 121 115 116 116 100 123 118 118 106 117 109 107 122 105 106 106 107 111 121 100 125 122 124 123 121 125 99 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 \ No newline at end of file diff --git a/data/SET-3/p03_00.cri b/data/SET-3/p03_00.cri new file mode 100644 index 0000000..548de9f --- /dev/null +++ b/data/SET-3/p03_00.cri @@ -0,0 +1,103 @@ +100 200 +10 7 13 19 26 3 5 9 16 16 12 19 23 20 8 19 2 12 17 9 11 18 29 3 6 17 16 16 9 21 27 23 11 14 8 5 8 16 31 9 5 5 7 18 16 1 27 36 30 13 10 9 14 18 2 6 7 18 28 3 13 19 10 9 20 25 25 36 6 5 15 25 9 8 18 13 14 3 23 6 26 16 11 7 41 35 26 9 15 3 1 2 22 27 20 11 12 10 9 17 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p03_1030.cri b/data/SET-3/p03_1030.cri new file mode 100644 index 0000000..fb7653f --- /dev/null +++ b/data/SET-3/p03_1030.cri @@ -0,0 +1,103 @@ +100 200 +54 35 29 42 56 54 39 24 28 21 52 35 38 40 48 31 23 40 54 41 24 45 46 43 51 49 21 51 26 24 54 52 53 28 53 39 51 39 39 53 27 22 45 46 55 31 39 37 53 44 46 46 23 56 49 49 32 48 36 33 55 30 32 31 34 39 54 23 59 54 57 55 53 59 22 33 43 48 42 52 46 31 47 45 59 51 59 52 54 24 52 43 27 57 47 51 50 37 50 24 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p03_1050.cri b/data/SET-3/p03_1050.cri new file mode 100644 index 0000000..c424a09 --- /dev/null +++ b/data/SET-3/p03_1050.cri @@ -0,0 +1,103 @@ +100 200 +88 51 38 64 92 88 59 28 36 22 84 51 57 61 76 43 26 60 88 63 28 70 72 66 83 78 22 82 32 28 88 84 86 37 87 58 82 58 58 86 35 25 70 73 90 42 59 54 87 69 73 72 26 93 79 78 44 76 52 47 90 41 45 43 48 58 88 27 98 89 95 91 87 99 24 46 66 77 65 84 72 42 74 71 98 82 98 84 88 29 85 66 34 95 74 83 81 54 81 28 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p03_1090.cri b/data/SET-3/p03_1090.cri new file mode 100644 index 0000000..9a9d522 --- /dev/null +++ b/data/SET-3/p03_1090.cri @@ -0,0 +1,103 @@ +100 200 +156 83 56 109 165 157 99 37 53 25 149 83 94 103 132 67 33 101 156 107 36 121 124 113 147 137 25 144 44 36 156 148 152 54 155 96 144 97 97 153 50 30 121 126 160 65 99 89 155 119 126 125 32 167 139 136 69 132 84 74 161 62 70 67 76 96 156 34 177 158 171 163 154 179 28 72 113 135 111 148 125 65 129 122 177 144 177 149 156 39 150 112 49 170 129 146 143 88 142 36 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p03_110.cri b/data/SET-3/p03_110.cri new file mode 100644 index 0000000..f5d5fa7 --- /dev/null +++ b/data/SET-3/p03_110.cri @@ -0,0 +1,103 @@ +100 200 +17 9 6 12 18 17 10 3 5 2 16 9 10 11 14 7 3 11 17 11 3 13 13 12 16 15 2 15 4 3 17 16 16 5 17 10 16 10 10 16 5 3 13 14 17 7 10 9 17 13 13 13 3 18 15 15 7 14 9 8 17 6 7 7 8 10 17 3 19 17 19 18 17 19 2 7 12 15 12 16 13 7 14 13 19 16 19 16 17 4 16 12 5 18 14 16 15 9 15 3 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p03_3070.cri b/data/SET-3/p03_3070.cri new file mode 100644 index 0000000..c0a98a3 --- /dev/null +++ b/data/SET-3/p03_3070.cri @@ -0,0 +1,103 @@ +100 200 +128 91 78 104 132 128 99 68 76 62 124 91 97 101 116 83 66 100 128 103 68 110 112 106 123 118 62 122 72 68 128 124 126 77 127 98 122 98 98 126 75 65 110 113 130 82 99 94 127 109 113 112 66 133 119 118 84 116 92 87 130 81 85 83 88 98 128 67 138 129 135 131 127 139 64 86 106 117 105 124 112 82 114 111 138 122 138 124 128 69 125 106 74 135 114 123 121 94 121 68 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p03_7090.cri b/data/SET-3/p03_7090.cri new file mode 100644 index 0000000..8d4c378 --- /dev/null +++ b/data/SET-3/p03_7090.cri @@ -0,0 +1,103 @@ +100 200 +174 155 149 162 176 174 159 144 148 141 172 155 158 160 168 151 143 160 174 161 144 165 166 163 171 169 141 171 146 144 174 172 173 148 173 159 171 159 159 173 147 142 165 166 175 151 159 157 173 164 166 166 143 176 169 169 152 168 156 153 175 150 152 151 154 159 174 143 179 174 177 175 173 179 142 153 163 168 162 172 166 151 167 165 179 171 179 172 174 144 172 163 147 177 167 171 170 157 170 144 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p04_00.cri b/data/SET-3/p04_00.cri new file mode 100644 index 0000000..16a2cb1 --- /dev/null +++ b/data/SET-3/p04_00.cri @@ -0,0 +1,153 @@ +150 200 +7 30 16 9 21 15 19 23 11 5 19 29 23 21 10 15 3 41 9 28 8 8 16 10 28 7 15 14 6 19 11 12 23 26 17 6 9 15 14 7 27 13 11 16 10 5 25 17 18 10 10 7 13 19 26 3 5 9 16 16 12 19 23 20 8 19 2 12 17 9 11 18 29 3 6 17 16 16 9 21 27 23 11 14 8 5 8 16 31 9 5 5 7 18 16 1 27 36 30 13 10 9 14 18 2 6 7 18 28 3 13 19 10 9 20 25 25 36 6 5 15 25 9 8 18 13 14 3 23 6 26 16 11 7 41 35 26 9 15 3 1 2 22 27 20 11 12 10 9 17 +35 35 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p04_1030.cri b/data/SET-3/p04_1030.cri new file mode 100644 index 0000000..b851d99 --- /dev/null +++ b/data/SET-3/p04_1030.cri @@ -0,0 +1,153 @@ +150 200 +54 35 29 42 56 54 39 24 28 21 52 35 38 40 48 31 23 40 54 41 24 45 46 43 51 49 21 51 26 24 54 52 53 28 53 39 51 39 39 53 27 22 45 46 55 31 39 37 53 44 46 46 23 56 49 49 32 48 36 33 55 30 32 31 34 39 54 23 59 54 57 55 53 59 22 33 43 48 42 52 46 31 47 45 59 51 59 52 54 24 52 43 27 57 47 51 50 37 50 24 46 51 50 58 37 28 23 42 32 56 56 46 20 32 47 35 34 53 50 57 33 39 22 35 46 41 58 39 47 38 45 44 51 49 51 43 42 32 32 57 42 44 43 27 46 47 59 28 42 34 +35 35 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p04_1050.cri b/data/SET-3/p04_1050.cri new file mode 100644 index 0000000..afe83e5 --- /dev/null +++ b/data/SET-3/p04_1050.cri @@ -0,0 +1,153 @@ +150 200 +88 51 38 64 92 88 59 28 36 22 84 51 57 61 76 43 26 60 88 63 28 70 72 66 83 78 22 82 32 28 88 84 86 37 87 58 82 58 58 86 35 25 70 73 90 42 59 54 87 69 73 72 26 93 79 78 44 76 52 47 90 41 45 43 48 58 88 27 98 89 95 91 87 99 24 46 66 77 65 84 72 42 74 71 98 82 98 84 88 29 85 66 34 95 74 83 81 54 81 28 73 83 80 96 55 37 27 64 45 93 93 73 21 44 75 51 49 86 81 94 47 59 24 50 72 62 97 59 74 56 70 68 82 78 83 66 64 45 44 95 64 69 66 34 72 74 99 37 64 48 +35 35 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p04_1090.cri b/data/SET-3/p04_1090.cri new file mode 100644 index 0000000..e20740d --- /dev/null +++ b/data/SET-3/p04_1090.cri @@ -0,0 +1,153 @@ +150 200 +156 83 56 109 165 157 99 37 53 25 149 83 94 103 132 67 33 101 156 107 36 121 124 113 147 137 25 144 44 36 156 148 152 54 155 96 144 97 97 153 50 30 121 126 160 65 99 89 155 119 126 125 32 167 139 136 69 132 84 74 161 62 70 67 76 96 156 34 177 158 171 163 154 179 28 72 113 135 111 148 125 65 129 122 177 144 177 149 156 39 150 112 49 170 129 146 143 88 142 36 127 147 140 172 90 55 34 109 70 167 167 126 22 69 130 82 79 153 142 168 74 98 28 81 124 105 175 99 129 93 121 117 144 136 146 112 109 71 68 171 109 119 113 49 125 128 179 55 109 77 +35 35 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p04_110.cri b/data/SET-3/p04_110.cri new file mode 100644 index 0000000..e2e6289 --- /dev/null +++ b/data/SET-3/p04_110.cri @@ -0,0 +1,153 @@ +150 200 +17 9 6 12 18 17 10 3 5 2 16 9 10 11 14 7 3 11 17 11 3 13 13 12 16 15 2 15 4 3 17 16 16 5 17 10 16 10 10 16 5 3 13 14 17 7 10 9 17 13 13 13 3 18 15 15 7 14 9 8 17 6 7 7 8 10 17 3 19 17 19 18 17 19 2 7 12 15 12 16 13 7 14 13 19 16 19 16 17 4 16 12 5 18 14 16 15 9 15 3 14 16 15 19 9 6 3 12 7 18 18 13 2 7 14 9 8 16 15 18 8 10 3 8 13 11 19 10 14 10 13 13 15 15 16 12 12 7 7 19 12 13 12 5 13 14 19 5 12 8 +35 35 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p04_3070.cri b/data/SET-3/p04_3070.cri new file mode 100644 index 0000000..d5949ca --- /dev/null +++ b/data/SET-3/p04_3070.cri @@ -0,0 +1,153 @@ +150 200 +128 91 78 104 132 128 99 68 76 62 124 91 97 101 116 83 66 100 128 103 68 110 112 106 123 118 62 122 72 68 128 124 126 77 127 98 122 98 98 126 75 65 110 113 130 82 99 94 127 109 113 112 66 133 119 118 84 116 92 87 130 81 85 83 88 98 128 67 138 129 135 131 127 139 64 86 106 117 105 124 112 82 114 111 138 122 138 124 128 69 125 106 74 135 114 123 121 94 121 68 113 123 120 136 95 77 67 104 85 133 133 113 61 84 115 91 89 126 121 134 87 99 64 90 112 102 137 99 114 96 110 108 122 118 123 106 104 85 84 135 104 109 106 74 112 114 139 77 104 88 +35 35 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p04_7090.cri b/data/SET-3/p04_7090.cri new file mode 100644 index 0000000..5c12975 --- /dev/null +++ b/data/SET-3/p04_7090.cri @@ -0,0 +1,153 @@ +150 200 +174 155 149 162 176 174 159 144 148 141 172 155 158 160 168 151 143 160 174 161 144 165 166 163 171 169 141 171 146 144 174 172 173 148 173 159 171 159 159 173 147 142 165 166 175 151 159 157 173 164 166 166 143 176 169 169 152 168 156 153 175 150 152 151 154 159 174 143 179 174 177 175 173 179 142 153 163 168 162 172 166 151 167 165 179 171 179 172 174 144 172 163 147 177 167 171 170 157 170 144 166 171 170 178 157 148 143 162 152 176 176 166 140 152 167 155 154 173 170 177 153 159 142 155 166 161 178 159 167 158 165 164 171 169 171 163 162 152 152 177 162 164 163 147 166 167 179 148 162 154 +35 35 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p05_00.cri b/data/SET-3/p05_00.cri new file mode 100644 index 0000000..16432cb --- /dev/null +++ b/data/SET-3/p05_00.cri @@ -0,0 +1,202 @@ +199 200 +18 26 11 30 21 19 15 16 29 26 37 16 12 31 8 19 20 13 15 22 28 12 6 27 14 18 17 29 13 22 25 28 27 19 10 12 14 24 16 33 15 11 18 17 21 27 19 20 5 7 30 16 9 21 15 19 23 11 5 19 29 23 21 10 15 3 41 9 28 8 8 16 10 28 7 15 14 6 19 11 12 23 26 17 6 9 15 14 7 27 13 11 16 10 5 25 17 18 10 10 7 13 19 26 3 5 9 16 16 12 19 23 20 8 19 2 12 17 9 11 18 29 3 6 17 16 16 9 21 27 23 11 14 8 5 8 16 31 9 5 5 7 18 16 1 27 36 30 13 10 9 14 18 2 6 7 18 28 3 13 19 10 9 20 25 25 36 6 5 15 25 9 8 18 13 14 3 23 6 26 16 11 7 41 35 26 9 15 3 1 2 22 27 20 11 12 10 9 17 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p05_1030.cri b/data/SET-3/p05_1030.cri new file mode 100644 index 0000000..3bc9c77 --- /dev/null +++ b/data/SET-3/p05_1030.cri @@ -0,0 +1,202 @@ +199 200 +54 35 29 42 56 54 39 24 28 21 52 35 38 40 48 31 23 40 54 41 24 45 46 43 51 49 21 51 26 24 54 52 53 28 53 39 51 39 39 53 27 22 45 46 55 31 39 37 53 44 46 46 23 56 49 49 32 48 36 33 55 30 32 31 34 39 54 23 59 54 57 55 53 59 22 33 43 48 42 52 46 31 47 45 59 51 59 52 54 24 52 43 27 57 47 51 50 37 50 24 46 51 50 58 37 28 23 42 32 56 56 46 20 32 47 35 34 53 50 57 33 39 22 35 46 41 58 39 47 38 45 44 51 49 51 43 42 32 32 57 42 44 43 27 46 47 59 28 42 34 39 24 59 37 37 21 47 24 28 41 20 25 21 25 20 50 59 29 21 20 50 38 44 28 44 37 45 41 38 53 46 32 43 30 30 50 53 28 38 36 39 56 56 26 22 43 54 47 32 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p05_1050.cri b/data/SET-3/p05_1050.cri new file mode 100644 index 0000000..6001cb5 --- /dev/null +++ b/data/SET-3/p05_1050.cri @@ -0,0 +1,202 @@ +199 200 +88 51 38 64 92 88 59 28 36 22 84 51 57 61 76 43 26 60 88 63 28 70 72 66 83 78 22 82 32 28 88 84 86 37 87 58 82 58 58 86 35 25 70 73 90 42 59 54 87 69 73 72 26 93 79 78 44 76 52 47 90 41 45 43 48 58 88 27 98 89 95 91 87 99 24 46 66 77 65 84 72 42 74 71 98 82 98 84 88 29 85 66 34 95 74 83 81 54 81 28 73 83 80 96 55 37 27 64 45 93 93 73 21 44 75 51 49 86 81 94 47 59 24 50 72 62 97 59 74 56 70 68 82 78 83 66 64 45 44 95 64 69 66 34 72 74 99 37 64 48 58 28 98 54 55 22 75 29 36 62 20 31 22 30 20 81 99 38 22 20 81 57 69 37 69 54 70 62 56 87 72 44 67 41 40 81 87 37 57 53 59 93 92 33 25 66 89 75 45 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p05_1090.cri b/data/SET-3/p05_1090.cri new file mode 100644 index 0000000..8a1fc9e --- /dev/null +++ b/data/SET-3/p05_1090.cri @@ -0,0 +1,202 @@ +199 200 +156 83 56 109 165 157 99 37 53 25 149 83 94 103 132 67 33 101 156 107 36 121 124 113 147 137 25 144 44 36 156 148 152 54 155 96 144 97 97 153 50 30 121 126 160 65 99 89 155 119 126 125 32 167 139 136 69 132 84 74 161 62 70 67 76 96 156 34 177 158 171 163 154 179 28 72 113 135 111 148 125 65 129 122 177 144 177 149 156 39 150 112 49 170 129 146 143 88 142 36 127 147 140 172 90 55 34 109 70 167 167 126 22 69 130 82 79 153 142 168 74 98 28 81 124 105 175 99 129 93 121 117 144 136 146 112 109 71 68 171 109 119 113 49 125 128 179 55 109 77 97 37 177 89 91 25 130 39 53 105 20 42 25 41 21 143 178 56 24 20 143 95 118 54 118 88 121 104 92 155 124 68 115 62 60 142 154 55 94 87 98 166 164 47 31 113 159 130 70 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p05_110.cri b/data/SET-3/p05_110.cri new file mode 100644 index 0000000..4502037 --- /dev/null +++ b/data/SET-3/p05_110.cri @@ -0,0 +1,202 @@ +199 200 +17 9 6 12 18 17 10 3 5 2 16 9 10 11 14 7 3 11 17 11 3 13 13 12 16 15 2 15 4 3 17 16 16 5 17 10 16 10 10 16 5 3 13 14 17 7 10 9 17 13 13 13 3 18 15 15 7 14 9 8 17 6 7 7 8 10 17 3 19 17 19 18 17 19 2 7 12 15 12 16 13 7 14 13 19 16 19 16 17 4 16 12 5 18 14 16 15 9 15 3 14 16 15 19 9 6 3 12 7 18 18 13 2 7 14 9 8 16 15 18 8 10 3 8 13 11 19 10 14 10 13 13 15 15 16 12 12 7 7 19 12 13 12 5 13 14 19 5 12 8 10 3 19 9 10 2 14 4 5 11 2 4 2 4 2 15 19 6 2 2 15 10 13 5 13 9 13 11 10 17 13 7 12 6 6 15 17 5 10 9 10 18 18 5 3 12 17 14 7 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p05_3070.cri b/data/SET-3/p05_3070.cri new file mode 100644 index 0000000..aefbfa7 --- /dev/null +++ b/data/SET-3/p05_3070.cri @@ -0,0 +1,202 @@ +199 200 +128 91 78 104 132 128 99 68 76 62 124 91 97 101 116 83 66 100 128 103 68 110 112 106 123 118 62 122 72 68 128 124 126 77 127 98 122 98 98 126 75 65 110 113 130 82 99 94 127 109 113 112 66 133 119 118 84 116 92 87 130 81 85 83 88 98 128 67 138 129 135 131 127 139 64 86 106 117 105 124 112 82 114 111 138 122 138 124 128 69 125 106 74 135 114 123 121 94 121 68 113 123 120 136 95 77 67 104 85 133 133 113 61 84 115 91 89 126 121 134 87 99 64 90 112 102 137 99 114 96 110 108 122 118 123 106 104 85 84 135 104 109 106 74 112 114 139 77 104 88 98 68 138 94 95 62 115 69 76 102 60 71 62 70 60 121 139 78 62 60 121 97 109 77 109 94 110 102 96 127 112 84 107 81 80 121 127 77 97 93 99 133 132 73 65 106 129 115 85 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p05_7090.cri b/data/SET-3/p05_7090.cri new file mode 100644 index 0000000..193fb7f --- /dev/null +++ b/data/SET-3/p05_7090.cri @@ -0,0 +1,202 @@ +199 200 +174 155 149 162 176 174 159 144 148 141 172 155 158 160 168 151 143 160 174 161 144 165 166 163 171 169 141 171 146 144 174 172 173 148 173 159 171 159 159 173 147 142 165 166 175 151 159 157 173 164 166 166 143 176 169 169 152 168 156 153 175 150 152 151 154 159 174 143 179 174 177 175 173 179 142 153 163 168 162 172 166 151 167 165 179 171 179 172 174 144 172 163 147 177 167 171 170 157 170 144 166 171 170 178 157 148 143 162 152 176 176 166 140 152 167 155 154 173 170 177 153 159 142 155 166 161 178 159 167 158 165 164 171 169 171 163 162 152 152 177 162 164 163 147 166 167 179 148 162 154 159 144 179 157 157 141 167 144 148 161 140 145 141 145 140 170 179 149 141 140 170 158 164 148 164 157 165 161 158 173 166 152 163 150 150 170 173 148 158 156 159 176 176 146 142 163 174 167 152 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p10_00.cri b/data/SET-3/p10_00.cri new file mode 100644 index 0000000..16432cb --- /dev/null +++ b/data/SET-3/p10_00.cri @@ -0,0 +1,202 @@ +199 200 +18 26 11 30 21 19 15 16 29 26 37 16 12 31 8 19 20 13 15 22 28 12 6 27 14 18 17 29 13 22 25 28 27 19 10 12 14 24 16 33 15 11 18 17 21 27 19 20 5 7 30 16 9 21 15 19 23 11 5 19 29 23 21 10 15 3 41 9 28 8 8 16 10 28 7 15 14 6 19 11 12 23 26 17 6 9 15 14 7 27 13 11 16 10 5 25 17 18 10 10 7 13 19 26 3 5 9 16 16 12 19 23 20 8 19 2 12 17 9 11 18 29 3 6 17 16 16 9 21 27 23 11 14 8 5 8 16 31 9 5 5 7 18 16 1 27 36 30 13 10 9 14 18 2 6 7 18 28 3 13 19 10 9 20 25 25 36 6 5 15 25 9 8 18 13 14 3 23 6 26 16 11 7 41 35 26 9 15 3 1 2 22 27 20 11 12 10 9 17 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p10_1030.cri b/data/SET-3/p10_1030.cri new file mode 100644 index 0000000..3bc9c77 --- /dev/null +++ b/data/SET-3/p10_1030.cri @@ -0,0 +1,202 @@ +199 200 +54 35 29 42 56 54 39 24 28 21 52 35 38 40 48 31 23 40 54 41 24 45 46 43 51 49 21 51 26 24 54 52 53 28 53 39 51 39 39 53 27 22 45 46 55 31 39 37 53 44 46 46 23 56 49 49 32 48 36 33 55 30 32 31 34 39 54 23 59 54 57 55 53 59 22 33 43 48 42 52 46 31 47 45 59 51 59 52 54 24 52 43 27 57 47 51 50 37 50 24 46 51 50 58 37 28 23 42 32 56 56 46 20 32 47 35 34 53 50 57 33 39 22 35 46 41 58 39 47 38 45 44 51 49 51 43 42 32 32 57 42 44 43 27 46 47 59 28 42 34 39 24 59 37 37 21 47 24 28 41 20 25 21 25 20 50 59 29 21 20 50 38 44 28 44 37 45 41 38 53 46 32 43 30 30 50 53 28 38 36 39 56 56 26 22 43 54 47 32 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p10_1050.cri b/data/SET-3/p10_1050.cri new file mode 100644 index 0000000..6001cb5 --- /dev/null +++ b/data/SET-3/p10_1050.cri @@ -0,0 +1,202 @@ +199 200 +88 51 38 64 92 88 59 28 36 22 84 51 57 61 76 43 26 60 88 63 28 70 72 66 83 78 22 82 32 28 88 84 86 37 87 58 82 58 58 86 35 25 70 73 90 42 59 54 87 69 73 72 26 93 79 78 44 76 52 47 90 41 45 43 48 58 88 27 98 89 95 91 87 99 24 46 66 77 65 84 72 42 74 71 98 82 98 84 88 29 85 66 34 95 74 83 81 54 81 28 73 83 80 96 55 37 27 64 45 93 93 73 21 44 75 51 49 86 81 94 47 59 24 50 72 62 97 59 74 56 70 68 82 78 83 66 64 45 44 95 64 69 66 34 72 74 99 37 64 48 58 28 98 54 55 22 75 29 36 62 20 31 22 30 20 81 99 38 22 20 81 57 69 37 69 54 70 62 56 87 72 44 67 41 40 81 87 37 57 53 59 93 92 33 25 66 89 75 45 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p10_1090.cri b/data/SET-3/p10_1090.cri new file mode 100644 index 0000000..8a1fc9e --- /dev/null +++ b/data/SET-3/p10_1090.cri @@ -0,0 +1,202 @@ +199 200 +156 83 56 109 165 157 99 37 53 25 149 83 94 103 132 67 33 101 156 107 36 121 124 113 147 137 25 144 44 36 156 148 152 54 155 96 144 97 97 153 50 30 121 126 160 65 99 89 155 119 126 125 32 167 139 136 69 132 84 74 161 62 70 67 76 96 156 34 177 158 171 163 154 179 28 72 113 135 111 148 125 65 129 122 177 144 177 149 156 39 150 112 49 170 129 146 143 88 142 36 127 147 140 172 90 55 34 109 70 167 167 126 22 69 130 82 79 153 142 168 74 98 28 81 124 105 175 99 129 93 121 117 144 136 146 112 109 71 68 171 109 119 113 49 125 128 179 55 109 77 97 37 177 89 91 25 130 39 53 105 20 42 25 41 21 143 178 56 24 20 143 95 118 54 118 88 121 104 92 155 124 68 115 62 60 142 154 55 94 87 98 166 164 47 31 113 159 130 70 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p10_110.cri b/data/SET-3/p10_110.cri new file mode 100644 index 0000000..4502037 --- /dev/null +++ b/data/SET-3/p10_110.cri @@ -0,0 +1,202 @@ +199 200 +17 9 6 12 18 17 10 3 5 2 16 9 10 11 14 7 3 11 17 11 3 13 13 12 16 15 2 15 4 3 17 16 16 5 17 10 16 10 10 16 5 3 13 14 17 7 10 9 17 13 13 13 3 18 15 15 7 14 9 8 17 6 7 7 8 10 17 3 19 17 19 18 17 19 2 7 12 15 12 16 13 7 14 13 19 16 19 16 17 4 16 12 5 18 14 16 15 9 15 3 14 16 15 19 9 6 3 12 7 18 18 13 2 7 14 9 8 16 15 18 8 10 3 8 13 11 19 10 14 10 13 13 15 15 16 12 12 7 7 19 12 13 12 5 13 14 19 5 12 8 10 3 19 9 10 2 14 4 5 11 2 4 2 4 2 15 19 6 2 2 15 10 13 5 13 9 13 11 10 17 13 7 12 6 6 15 17 5 10 9 10 18 18 5 3 12 17 14 7 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p10_3070.cri b/data/SET-3/p10_3070.cri new file mode 100644 index 0000000..aefbfa7 --- /dev/null +++ b/data/SET-3/p10_3070.cri @@ -0,0 +1,202 @@ +199 200 +128 91 78 104 132 128 99 68 76 62 124 91 97 101 116 83 66 100 128 103 68 110 112 106 123 118 62 122 72 68 128 124 126 77 127 98 122 98 98 126 75 65 110 113 130 82 99 94 127 109 113 112 66 133 119 118 84 116 92 87 130 81 85 83 88 98 128 67 138 129 135 131 127 139 64 86 106 117 105 124 112 82 114 111 138 122 138 124 128 69 125 106 74 135 114 123 121 94 121 68 113 123 120 136 95 77 67 104 85 133 133 113 61 84 115 91 89 126 121 134 87 99 64 90 112 102 137 99 114 96 110 108 122 118 123 106 104 85 84 135 104 109 106 74 112 114 139 77 104 88 98 68 138 94 95 62 115 69 76 102 60 71 62 70 60 121 139 78 62 60 121 97 109 77 109 94 110 102 96 127 112 84 107 81 80 121 127 77 97 93 99 133 132 73 65 106 129 115 85 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p10_7090.cri b/data/SET-3/p10_7090.cri new file mode 100644 index 0000000..193fb7f --- /dev/null +++ b/data/SET-3/p10_7090.cri @@ -0,0 +1,202 @@ +199 200 +174 155 149 162 176 174 159 144 148 141 172 155 158 160 168 151 143 160 174 161 144 165 166 163 171 169 141 171 146 144 174 172 173 148 173 159 171 159 159 173 147 142 165 166 175 151 159 157 173 164 166 166 143 176 169 169 152 168 156 153 175 150 152 151 154 159 174 143 179 174 177 175 173 179 142 153 163 168 162 172 166 151 167 165 179 171 179 172 174 144 172 163 147 177 167 171 170 157 170 144 166 171 170 178 157 148 143 162 152 176 176 166 140 152 167 155 154 173 170 177 153 159 142 155 166 161 178 159 167 158 165 164 171 169 171 163 162 152 152 177 162 164 163 147 166 167 179 148 162 154 159 144 179 157 157 141 167 144 148 161 140 145 141 145 140 170 179 149 141 140 170 158 164 148 164 157 165 161 158 173 166 152 163 150 150 170 173 148 158 156 159 176 176 146 142 163 174 167 152 +35 35 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 \ No newline at end of file diff --git a/data/SET-3/p11_00.cri b/data/SET-3/p11_00.cri new file mode 100644 index 0000000..21628c2 --- /dev/null +++ b/data/SET-3/p11_00.cri @@ -0,0 +1,123 @@ +120 200 +25 7 13 6 14 5 11 19 5 15 15 17 13 12 18 13 18 12 17 4 7 12 13 8 16 15 6 5 9 11 10 3 7 2 4 4 18 14 12 17 20 14 16 10 9 11 7 13 5 4 21 13 11 12 14 10 8 16 19 5 17 7 16 14 17 13 17 13 14 16 7 13 9 11 35 5 28 7 3 10 7 12 11 10 8 11 21 4 15 16 4 16 7 10 9 11 17 12 11 7 9 11 12 7 8 6 5 12 13 7 7 8 11 13 11 10 7 4 20 13 +10 45 +25 1 +25 3 +31 5 +32 5 +31 7 +32 9 +34 9 +46 9 +35 7 +34 6 +35 5 +47 6 +40 5 +39 3 +36 3 +73 6 +73 8 +24 36 +76 6 +76 10 +76 13 +78 3 +78 9 +79 3 +79 5 +79 11 +82 3 +82 7 +90 15 +84 3 +84 5 +84 9 +85 1 +87 5 +85 8 +87 7 +86 41 +86 44 +86 46 +85 55 +89 43 +89 46 +89 52 +92 42 +92 52 +94 42 +94 44 +94 48 +96 42 +99 46 +99 50 +83 80 +83 83 +85 81 +85 85 +85 89 +87 80 +87 86 +90 77 +90 88 +93 82 +93 84 +93 89 +94 86 +95 80 +99 89 +37 83 +50 80 +35 85 +35 87 +44 86 +46 89 +46 83 +46 87 +46 89 +48 83 +50 85 +50 88 +54 86 +54 90 +10 35 +10 40 +18 30 +17 35 +16 38 +14 40 +15 42 +11 42 +18 40 +21 39 +20 40 +18 41 +20 44 +22 44 +16 45 +20 45 +25 45 +30 55 +20 50 +22 51 +18 49 +16 48 +20 55 +18 53 +14 50 +15 51 +16 54 +28 33 +33 38 +30 50 +13 40 +15 36 +18 31 +25 37 +30 46 +25 52 +16 33 +25 35 +5 40 +5 50 \ No newline at end of file diff --git a/data/SET-3/p11_1030.cri b/data/SET-3/p11_1030.cri new file mode 100644 index 0000000..939d60c --- /dev/null +++ b/data/SET-3/p11_1030.cri @@ -0,0 +1,123 @@ +120 200 +54 35 29 42 56 54 39 24 28 21 52 35 38 40 48 31 23 40 54 41 24 45 46 43 51 49 21 51 26 24 54 52 53 28 53 39 51 39 39 53 27 22 45 46 55 31 39 37 53 44 46 46 23 56 49 49 32 48 36 33 55 30 32 31 34 39 54 23 59 54 57 55 53 59 22 33 43 48 42 52 46 31 47 45 59 51 59 52 54 24 52 43 27 57 47 51 50 37 50 24 46 51 50 58 37 28 23 42 32 56 56 46 20 32 47 35 34 53 50 57 +10 45 +25 1 +25 3 +31 5 +32 5 +31 7 +32 9 +34 9 +46 9 +35 7 +34 6 +35 5 +47 6 +40 5 +39 3 +36 3 +73 6 +73 8 +24 36 +76 6 +76 10 +76 13 +78 3 +78 9 +79 3 +79 5 +79 11 +82 3 +82 7 +90 15 +84 3 +84 5 +84 9 +85 1 +87 5 +85 8 +87 7 +86 41 +86 44 +86 46 +85 55 +89 43 +89 46 +89 52 +92 42 +92 52 +94 42 +94 44 +94 48 +96 42 +99 46 +99 50 +83 80 +83 83 +85 81 +85 85 +85 89 +87 80 +87 86 +90 77 +90 88 +93 82 +93 84 +93 89 +94 86 +95 80 +99 89 +37 83 +50 80 +35 85 +35 87 +44 86 +46 89 +46 83 +46 87 +46 89 +48 83 +50 85 +50 88 +54 86 +54 90 +10 35 +10 40 +18 30 +17 35 +16 38 +14 40 +15 42 +11 42 +18 40 +21 39 +20 40 +18 41 +20 44 +22 44 +16 45 +20 45 +25 45 +30 55 +20 50 +22 51 +18 49 +16 48 +20 55 +18 53 +14 50 +15 51 +16 54 +28 33 +33 38 +30 50 +13 40 +15 36 +18 31 +25 37 +30 46 +25 52 +16 33 +25 35 +5 40 +5 50 \ No newline at end of file diff --git a/data/SET-3/p11_1050.cri b/data/SET-3/p11_1050.cri new file mode 100644 index 0000000..40524a6 --- /dev/null +++ b/data/SET-3/p11_1050.cri @@ -0,0 +1,123 @@ +120 200 +88 51 38 64 92 88 59 28 36 22 84 51 57 61 76 43 26 60 88 63 28 70 72 66 83 78 22 82 32 28 88 84 86 37 87 58 82 58 58 86 35 25 70 73 90 42 59 54 87 69 73 72 26 93 79 78 44 76 52 47 90 41 45 43 48 58 88 27 98 89 95 91 87 99 24 46 66 77 65 84 72 42 74 71 98 82 98 84 88 29 85 66 34 95 74 83 81 54 81 28 73 83 80 96 55 37 27 64 45 93 93 73 21 44 75 51 49 86 81 94 +10 45 +25 1 +25 3 +31 5 +32 5 +31 7 +32 9 +34 9 +46 9 +35 7 +34 6 +35 5 +47 6 +40 5 +39 3 +36 3 +73 6 +73 8 +24 36 +76 6 +76 10 +76 13 +78 3 +78 9 +79 3 +79 5 +79 11 +82 3 +82 7 +90 15 +84 3 +84 5 +84 9 +85 1 +87 5 +85 8 +87 7 +86 41 +86 44 +86 46 +85 55 +89 43 +89 46 +89 52 +92 42 +92 52 +94 42 +94 44 +94 48 +96 42 +99 46 +99 50 +83 80 +83 83 +85 81 +85 85 +85 89 +87 80 +87 86 +90 77 +90 88 +93 82 +93 84 +93 89 +94 86 +95 80 +99 89 +37 83 +50 80 +35 85 +35 87 +44 86 +46 89 +46 83 +46 87 +46 89 +48 83 +50 85 +50 88 +54 86 +54 90 +10 35 +10 40 +18 30 +17 35 +16 38 +14 40 +15 42 +11 42 +18 40 +21 39 +20 40 +18 41 +20 44 +22 44 +16 45 +20 45 +25 45 +30 55 +20 50 +22 51 +18 49 +16 48 +20 55 +18 53 +14 50 +15 51 +16 54 +28 33 +33 38 +30 50 +13 40 +15 36 +18 31 +25 37 +30 46 +25 52 +16 33 +25 35 +5 40 +5 50 \ No newline at end of file diff --git a/data/SET-3/p11_1090.cri b/data/SET-3/p11_1090.cri new file mode 100644 index 0000000..b9591f2 --- /dev/null +++ b/data/SET-3/p11_1090.cri @@ -0,0 +1,123 @@ +120 200 +156 83 56 109 165 157 99 37 53 25 149 83 94 103 132 67 33 101 156 107 36 121 124 113 147 137 25 144 44 36 156 148 152 54 155 96 144 97 97 153 50 30 121 126 160 65 99 89 155 119 126 125 32 167 139 136 69 132 84 74 161 62 70 67 76 96 156 34 177 158 171 163 154 179 28 72 113 135 111 148 125 65 129 122 177 144 177 149 156 39 150 112 49 170 129 146 143 88 142 36 127 147 140 172 90 55 34 109 70 167 167 126 22 69 130 82 79 153 142 168 +10 45 +25 1 +25 3 +31 5 +32 5 +31 7 +32 9 +34 9 +46 9 +35 7 +34 6 +35 5 +47 6 +40 5 +39 3 +36 3 +73 6 +73 8 +24 36 +76 6 +76 10 +76 13 +78 3 +78 9 +79 3 +79 5 +79 11 +82 3 +82 7 +90 15 +84 3 +84 5 +84 9 +85 1 +87 5 +85 8 +87 7 +86 41 +86 44 +86 46 +85 55 +89 43 +89 46 +89 52 +92 42 +92 52 +94 42 +94 44 +94 48 +96 42 +99 46 +99 50 +83 80 +83 83 +85 81 +85 85 +85 89 +87 80 +87 86 +90 77 +90 88 +93 82 +93 84 +93 89 +94 86 +95 80 +99 89 +37 83 +50 80 +35 85 +35 87 +44 86 +46 89 +46 83 +46 87 +46 89 +48 83 +50 85 +50 88 +54 86 +54 90 +10 35 +10 40 +18 30 +17 35 +16 38 +14 40 +15 42 +11 42 +18 40 +21 39 +20 40 +18 41 +20 44 +22 44 +16 45 +20 45 +25 45 +30 55 +20 50 +22 51 +18 49 +16 48 +20 55 +18 53 +14 50 +15 51 +16 54 +28 33 +33 38 +30 50 +13 40 +15 36 +18 31 +25 37 +30 46 +25 52 +16 33 +25 35 +5 40 +5 50 \ No newline at end of file diff --git a/data/SET-3/p11_110.cri b/data/SET-3/p11_110.cri new file mode 100644 index 0000000..2bd9e9f --- /dev/null +++ b/data/SET-3/p11_110.cri @@ -0,0 +1,123 @@ +120 200 +17 9 6 12 18 17 10 3 5 2 16 9 10 11 14 7 3 11 17 11 3 13 13 12 16 15 2 15 4 3 17 16 16 5 17 10 16 10 10 16 5 3 13 14 17 7 10 9 17 13 13 13 3 18 15 15 7 14 9 8 17 6 7 7 8 10 17 3 19 17 19 18 17 19 2 7 12 15 12 16 13 7 14 13 19 16 19 16 17 4 16 12 5 18 14 16 15 9 15 3 14 16 15 19 9 6 3 12 7 18 18 13 2 7 14 9 8 16 15 18 +10 45 +25 1 +25 3 +31 5 +32 5 +31 7 +32 9 +34 9 +46 9 +35 7 +34 6 +35 5 +47 6 +40 5 +39 3 +36 3 +73 6 +73 8 +24 36 +76 6 +76 10 +76 13 +78 3 +78 9 +79 3 +79 5 +79 11 +82 3 +82 7 +90 15 +84 3 +84 5 +84 9 +85 1 +87 5 +85 8 +87 7 +86 41 +86 44 +86 46 +85 55 +89 43 +89 46 +89 52 +92 42 +92 52 +94 42 +94 44 +94 48 +96 42 +99 46 +99 50 +83 80 +83 83 +85 81 +85 85 +85 89 +87 80 +87 86 +90 77 +90 88 +93 82 +93 84 +93 89 +94 86 +95 80 +99 89 +37 83 +50 80 +35 85 +35 87 +44 86 +46 89 +46 83 +46 87 +46 89 +48 83 +50 85 +50 88 +54 86 +54 90 +10 35 +10 40 +18 30 +17 35 +16 38 +14 40 +15 42 +11 42 +18 40 +21 39 +20 40 +18 41 +20 44 +22 44 +16 45 +20 45 +25 45 +30 55 +20 50 +22 51 +18 49 +16 48 +20 55 +18 53 +14 50 +15 51 +16 54 +28 33 +33 38 +30 50 +13 40 +15 36 +18 31 +25 37 +30 46 +25 52 +16 33 +25 35 +5 40 +5 50 \ No newline at end of file diff --git a/data/SET-3/p11_3070.cri b/data/SET-3/p11_3070.cri new file mode 100644 index 0000000..947e344 --- /dev/null +++ b/data/SET-3/p11_3070.cri @@ -0,0 +1,123 @@ +120 200 +128 91 78 104 132 128 99 68 76 62 124 91 97 101 116 83 66 100 128 103 68 110 112 106 123 118 62 122 72 68 128 124 126 77 127 98 122 98 98 126 75 65 110 113 130 82 99 94 127 109 113 112 66 133 119 118 84 116 92 87 130 81 85 83 88 98 128 67 138 129 135 131 127 139 64 86 106 117 105 124 112 82 114 111 138 122 138 124 128 69 125 106 74 135 114 123 121 94 121 68 113 123 120 136 95 77 67 104 85 133 133 113 61 84 115 91 89 126 121 134 +10 45 +25 1 +25 3 +31 5 +32 5 +31 7 +32 9 +34 9 +46 9 +35 7 +34 6 +35 5 +47 6 +40 5 +39 3 +36 3 +73 6 +73 8 +24 36 +76 6 +76 10 +76 13 +78 3 +78 9 +79 3 +79 5 +79 11 +82 3 +82 7 +90 15 +84 3 +84 5 +84 9 +85 1 +87 5 +85 8 +87 7 +86 41 +86 44 +86 46 +85 55 +89 43 +89 46 +89 52 +92 42 +92 52 +94 42 +94 44 +94 48 +96 42 +99 46 +99 50 +83 80 +83 83 +85 81 +85 85 +85 89 +87 80 +87 86 +90 77 +90 88 +93 82 +93 84 +93 89 +94 86 +95 80 +99 89 +37 83 +50 80 +35 85 +35 87 +44 86 +46 89 +46 83 +46 87 +46 89 +48 83 +50 85 +50 88 +54 86 +54 90 +10 35 +10 40 +18 30 +17 35 +16 38 +14 40 +15 42 +11 42 +18 40 +21 39 +20 40 +18 41 +20 44 +22 44 +16 45 +20 45 +25 45 +30 55 +20 50 +22 51 +18 49 +16 48 +20 55 +18 53 +14 50 +15 51 +16 54 +28 33 +33 38 +30 50 +13 40 +15 36 +18 31 +25 37 +30 46 +25 52 +16 33 +25 35 +5 40 +5 50 \ No newline at end of file diff --git a/data/SET-3/p11_7090.cri b/data/SET-3/p11_7090.cri new file mode 100644 index 0000000..0354705 --- /dev/null +++ b/data/SET-3/p11_7090.cri @@ -0,0 +1,123 @@ +120 200 +174 155 149 162 176 174 159 144 148 141 172 155 158 160 168 151 143 160 174 161 144 165 166 163 171 169 141 171 146 144 174 172 173 148 173 159 171 159 159 173 147 142 165 166 175 151 159 157 173 164 166 166 143 176 169 169 152 168 156 153 175 150 152 151 154 159 174 143 179 174 177 175 173 179 142 153 163 168 162 172 166 151 167 165 179 171 179 172 174 144 172 163 147 177 167 171 170 157 170 144 166 171 170 178 157 148 143 162 152 176 176 166 140 152 167 155 154 173 170 177 +10 45 +25 1 +25 3 +31 5 +32 5 +31 7 +32 9 +34 9 +46 9 +35 7 +34 6 +35 5 +47 6 +40 5 +39 3 +36 3 +73 6 +73 8 +24 36 +76 6 +76 10 +76 13 +78 3 +78 9 +79 3 +79 5 +79 11 +82 3 +82 7 +90 15 +84 3 +84 5 +84 9 +85 1 +87 5 +85 8 +87 7 +86 41 +86 44 +86 46 +85 55 +89 43 +89 46 +89 52 +92 42 +92 52 +94 42 +94 44 +94 48 +96 42 +99 46 +99 50 +83 80 +83 83 +85 81 +85 85 +85 89 +87 80 +87 86 +90 77 +90 88 +93 82 +93 84 +93 89 +94 86 +95 80 +99 89 +37 83 +50 80 +35 85 +35 87 +44 86 +46 89 +46 83 +46 87 +46 89 +48 83 +50 85 +50 88 +54 86 +54 90 +10 35 +10 40 +18 30 +17 35 +16 38 +14 40 +15 42 +11 42 +18 40 +21 39 +20 40 +18 41 +20 44 +22 44 +16 45 +20 45 +25 45 +30 55 +20 50 +22 51 +18 49 +16 48 +20 55 +18 53 +14 50 +15 51 +16 54 +28 33 +33 38 +30 50 +13 40 +15 36 +18 31 +25 37 +30 46 +25 52 +16 33 +25 35 +5 40 +5 50 \ No newline at end of file diff --git a/data/SET-4/eil22.sd b/data/SET-4/eil22.sd new file mode 100644 index 0000000..509aa39 --- /dev/null +++ b/data/SET-4/eil22.sd @@ -0,0 +1,24 @@ +21 6000 +1100 700 800 1400 2100 400 800 100 500 600 1200 1300 1300 300 900 2100 1000 900 2500 1800 700 +145 215 +151 264 +159 261 +130 254 +128 252 +163 247 +146 246 +161 242 +142 239 +163 236 +148 232 +128 231 +156 217 +129 214 +146 208 +164 208 +141 206 +147 193 +164 193 +129 189 +155 185 +139 182 diff --git a/data/SET-4/eil23.sd b/data/SET-4/eil23.sd new file mode 100644 index 0000000..051aa86 --- /dev/null +++ b/data/SET-4/eil23.sd @@ -0,0 +1,25 @@ +22 4500 +125 84 60 500 300 175 350 150 1100 4100 225 300 250 500 150 100 250 120 600 500 175 75 +266 235 +295 272 +301 258 +309 260 +217 274 +218 278 +282 267 +242 249 +230 262 +249 268 +256 267 +265 257 +267 242 +259 265 +315 233 +329 252 +318 252 +329 224 +267 213 +275 192 +303 201 +208 217 +326 181 diff --git a/data/SET-4/eil30.sd b/data/SET-4/eil30.sd new file mode 100644 index 0000000..1e36235 --- /dev/null +++ b/data/SET-4/eil30.sd @@ -0,0 +1,32 @@ +29 4500 +300 3100 125 100 200 150 150 450 300 100 950 125 150 150 550 150 100 150 400 300 1500 100 300 500 800 300 100 150 1000 +162 354 +218 382 +218 358 +201 370 +214 371 +224 370 +210 382 +104 354 +126 338 +119 340 +129 349 +126 347 +125 346 +116 355 +126 335 +125 355 +119 357 +115 341 +153 351 +175 363 +180 360 +159 331 +188 357 +152 349 +215 389 +212 394 +188 393 +207 406 +184 410 +207 392 diff --git a/data/SET-4/eil33.sd b/data/SET-4/eil33.sd new file mode 100644 index 0000000..466cf83 --- /dev/null +++ b/data/SET-4/eil33.sd @@ -0,0 +1,35 @@ +32 8000 +700 400 400 1200 40 80 2000 900 600 750 1500 150 250 1600 450 700 550 650 200 400 300 1300 700 750 1400 4000 600 1000 500 2500 1700 1100 +292 495 +298 427 +309 445 +307 464 +336 475 +320 439 +321 437 +322 437 +323 433 +324 433 +323 429 +314 435 +311 442 +304 427 +293 421 +296 418 +261 384 +297 410 +315 407 +314 406 +321 391 +321 398 +314 394 +313 378 +304 382 +295 402 +283 406 +279 399 +271 401 +264 414 +277 439 +290 434 +319 433 diff --git a/data/SET-4/eil51.sd b/data/SET-4/eil51.sd new file mode 100644 index 0000000..e7f4c9e --- /dev/null +++ b/data/SET-4/eil51.sd @@ -0,0 +1,53 @@ +50 160 +7 30 16 9 21 15 19 23 11 5 19 29 23 21 10 15 3 41 9 28 8 8 16 10 28 7 15 14 6 19 11 12 23 26 17 6 9 15 14 7 27 13 11 16 10 5 25 17 18 10 +30 40 +37 52 +49 49 +52 64 +20 26 +40 30 +21 47 +17 63 +31 62 +52 33 +51 21 +42 41 +31 32 +5 25 +12 42 +36 16 +52 41 +27 23 +17 33 +13 13 +57 58 +62 42 +42 57 +16 57 +8 52 +7 38 +27 68 +30 48 +43 67 +58 48 +58 27 +37 69 +38 46 +46 10 +61 33 +62 63 +63 69 +32 22 +45 35 +59 15 +5 6 +10 17 +21 10 +5 64 +30 15 +39 10 +32 39 +25 32 +25 55 +48 28 +56 37 diff --git a/data/SET-4/eilA101.sd b/data/SET-4/eilA101.sd new file mode 100644 index 0000000..f0c3e6b --- /dev/null +++ b/data/SET-4/eilA101.sd @@ -0,0 +1,103 @@ +100 200 +10 7 13 19 26 3 5 9 16 16 12 19 23 20 8 19 2 12 17 9 11 18 29 3 6 17 16 16 9 21 27 23 11 14 8 5 8 16 31 9 5 5 7 18 16 1 27 36 30 13 10 9 14 18 2 6 7 18 28 3 13 19 10 9 20 25 25 36 6 5 15 25 9 8 18 13 14 3 23 6 26 16 11 7 41 35 26 9 15 3 1 2 22 27 20 11 12 10 9 17 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 diff --git a/data/SET-4/eilA76.sd b/data/SET-4/eilA76.sd new file mode 100644 index 0000000..4468e05 --- /dev/null +++ b/data/SET-4/eilA76.sd @@ -0,0 +1,78 @@ +75 140 +18 26 11 30 21 19 15 16 29 26 37 16 12 31 8 19 20 13 15 22 28 12 6 27 14 18 17 29 13 22 25 28 27 19 10 12 14 24 16 33 15 11 18 17 21 27 19 20 5 22 12 19 22 16 7 26 14 21 24 13 15 18 11 28 9 37 30 10 8 11 3 1 6 10 20 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 diff --git a/data/SET-4/eilB101.sd b/data/SET-4/eilB101.sd new file mode 100644 index 0000000..65df02b --- /dev/null +++ b/data/SET-4/eilB101.sd @@ -0,0 +1,103 @@ +100 112 +10 7 13 19 26 3 5 9 16 16 12 19 23 20 8 19 2 12 17 9 11 18 29 3 6 17 16 16 9 21 27 23 11 14 8 5 8 16 31 9 5 5 7 18 16 1 27 36 30 13 10 9 14 18 2 6 7 18 28 3 13 19 10 9 20 25 25 36 6 5 15 25 9 8 18 13 14 3 23 6 26 16 11 7 41 35 26 9 15 3 1 2 22 27 20 11 12 10 9 17 +35 35 +41 49 +35 17 +55 45 +55 20 +15 30 +25 30 +20 50 +10 43 +55 60 +30 60 +20 65 +50 35 +30 25 +15 10 +30 5 +10 20 +5 30 +20 40 +15 60 +45 65 +45 20 +45 10 +55 5 +65 35 +65 20 +45 30 +35 40 +41 37 +64 42 +40 60 +31 52 +35 69 +53 52 +65 55 +63 65 +2 60 +20 20 +5 5 +60 12 +40 25 +42 7 +24 12 +23 3 +11 14 +6 38 +2 48 +8 56 +13 52 +6 68 +47 47 +49 58 +27 43 +37 31 +57 29 +63 23 +53 12 +32 12 +36 26 +21 24 +17 34 +12 24 +24 58 +27 69 +15 77 +62 77 +49 73 +67 5 +56 39 +37 47 +37 56 +57 68 +47 16 +44 17 +46 13 +49 11 +49 42 +53 43 +61 52 +57 48 +56 37 +55 54 +15 47 +14 37 +11 31 +16 22 +4 18 +28 18 +26 52 +26 35 +31 67 +15 19 +22 22 +18 24 +26 27 +25 24 +22 27 +25 21 +19 21 +20 26 +18 18 diff --git a/data/SET-4/eilB76.sd b/data/SET-4/eilB76.sd new file mode 100644 index 0000000..69390de --- /dev/null +++ b/data/SET-4/eilB76.sd @@ -0,0 +1,78 @@ +75 100 +18 26 11 30 21 19 15 16 29 26 37 16 12 31 8 19 20 13 15 22 28 12 6 27 14 18 17 29 13 22 25 28 27 19 10 12 14 24 16 33 15 11 18 17 21 27 19 20 5 22 12 19 22 16 7 26 14 21 24 13 15 18 11 28 9 37 30 10 8 11 3 1 6 10 20 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 diff --git a/data/SET-4/eilC76.sd b/data/SET-4/eilC76.sd new file mode 100644 index 0000000..6055da4 --- /dev/null +++ b/data/SET-4/eilC76.sd @@ -0,0 +1,78 @@ +75 180 +18 26 11 30 21 19 15 16 29 26 37 16 12 31 8 19 20 13 15 22 28 12 6 27 14 18 17 29 13 22 25 28 27 19 10 12 14 24 16 33 15 11 18 17 21 27 19 20 5 22 12 19 22 16 7 26 14 21 24 13 15 18 11 28 9 37 30 10 8 11 3 1 6 10 20 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 diff --git a/data/SET-4/eilD76.sd b/data/SET-4/eilD76.sd new file mode 100644 index 0000000..a6ff269 --- /dev/null +++ b/data/SET-4/eilD76.sd @@ -0,0 +1,78 @@ +75 220 +18 26 11 30 21 19 15 16 29 26 37 16 12 31 8 19 20 13 15 22 28 12 6 27 14 18 17 29 13 22 25 28 27 19 10 12 14 24 16 33 15 11 18 17 21 27 19 20 5 22 12 19 22 16 7 26 14 21 24 13 15 18 11 28 9 37 30 10 8 11 3 1 6 10 20 +40 40 +22 22 +36 26 +21 45 +45 35 +55 20 +33 34 +50 50 +55 45 +26 59 +40 66 +55 65 +35 51 +62 35 +62 57 +62 24 +21 36 +33 44 +9 56 +62 48 +66 14 +44 13 +26 13 +11 28 +7 43 +17 64 +41 46 +55 34 +35 16 +52 26 +43 26 +31 76 +22 53 +26 29 +50 40 +55 50 +54 10 +60 15 +47 66 +30 60 +30 50 +12 17 +15 14 +16 19 +21 48 +50 30 +51 42 +50 15 +48 21 +12 38 +15 56 +29 39 +54 38 +55 57 +67 41 +10 70 +6 25 +65 27 +40 60 +70 64 +64 4 +36 6 +30 20 +20 30 +15 5 +50 70 +57 72 +45 42 +38 33 +50 4 +66 8 +59 5 +35 60 +27 24 +40 20 +40 37 diff --git a/src/algorithm/base_star.h b/src/algorithm/base_star.h new file mode 100644 index 0000000..3dc1c36 --- /dev/null +++ b/src/algorithm/base_star.h @@ -0,0 +1,229 @@ +#ifndef ALKAID_SD_SRC_ALGORITHM_BASE_STAR_H_ +#define ALKAID_SD_SRC_ALGORITHM_BASE_STAR_H_ + +#include "algorithm/operators.h" + +namespace alkaid_sd { + +struct Insertion { + Delta delta; + Node predecessor{}, successor{}; +}; + +template struct BestInsertion { + Insertion insertions[num]; + + inline void Reset() { + for (auto &insertion : insertions) { + insertion.delta = Delta(std::numeric_limits::max(), -1); + } + } + + inline void Add(int delta, Node predecessor, Node successor, Random &random) { + for (int i = 0; i < num; ++i) { + if (insertions[i].delta.value == std::numeric_limits::max()) { + insertions[i] = {{delta, 1}, predecessor, successor}; + return; + } else if (delta < insertions[i].delta.value) { + for (int j = num - 1; j > i; --j) { + insertions[j] = insertions[j - 1]; + } + insertions[i] = {{delta, 1}, predecessor, successor}; + return; + } else if (delta == insertions[i].delta.value && + insertions[i].delta.counter != -1) { + if (random.NextInt(1, insertions[i].delta.counter + 1) == 1) { + for (int j = num - 1; j > i; --j) { + insertions[j] = insertions[j - 1]; + } + ++insertions[i].delta.counter; + insertions[i].predecessor = predecessor; + insertions[i].successor = successor; + break; + } else { + ++insertions[i].delta.counter; + } + } + } + } + + inline void Add(const Insertion &insertion, Random &random) { + for (int i = 0; i < num; ++i) { + if (insertion.delta.value < insertions[i].delta.value) { + for (int j = num - 1; j > i; --j) { + insertions[j] = insertions[j - 1]; + } + insertions[i] = insertion; + return; + } else if (insertion.delta.value == insertions[i].delta.value && + insertions[i].delta.counter != -1) { + if (random.NextInt(1, insertions[i].delta.counter + + insertion.delta.counter) == 1) { + for (int j = num - 1; j > i; --j) { + insertions[j] = insertions[j - 1]; + } + insertions[i].delta.counter += insertion.delta.counter; + insertions[i].predecessor = insertion.predecessor; + insertions[i].successor = insertion.successor; + break; + } else { + insertions[i].delta.counter += insertion.delta.counter; + } + } + } + } + + [[nodiscard]] inline const Insertion *FindBest() const { return insertions; } + + [[nodiscard]] inline const Insertion * + FindBestWithoutNode(Node node_index) const { + for (auto &insertion : insertions) { + if (insertion.delta.counter > 0 && insertion.predecessor != node_index && + insertion.successor != node_index) { + return &insertion; + } + } + return nullptr; + } + + [[nodiscard]] inline const Insertion * + FindBestWithoutRoute(Node route_index) const { + for (auto &insertion : insertions) { + if (insertion.delta.counter > 0 && insertion.route_index != route_index) { + return &insertion; + } + } + return nullptr; + } + + template + [[nodiscard]] inline const Insertion *FindBest(Predicate &&predicate) const { + for (auto &insertion : insertions) { + if (insertion.delta.counter > 0 && predicate(insertion)) { + return &insertion; + } + } + return nullptr; + } +}; + +inline int CalcDelta(const DistanceMatrix &distance_matrix, + const Solution &solution, Node node_index, + Node predecessor, Node successor) { + return distance_matrix[solution.customer(node_index)] + [solution.customer(predecessor)] + + distance_matrix[solution.customer(node_index)] + [solution.customer(successor)] - + distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)]; +} + +template +void PreprocessInsertions(const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, + std::vector> &insertions, + Random &random) { + Node node_x = context.heads[route_x]; + while (node_x) { + auto &insertion = insertions[node_x]; + insertion.Reset(); + Node predecessor = 0; + Node successor = context.heads[route_y]; + while (true) { + int delta = + CalcDelta(distance_matrix, solution, node_x, predecessor, successor); + insertion.Add(delta, predecessor, successor, random); + if (!successor) { + break; + } + predecessor = successor; + successor = solution.successor(successor); + } + node_x = solution.successor(node_x); + } +} + +inline std::vector>> star_caches(kMaxNumRoutes); + +inline void PreprocessStarInsertions(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, + const RouteContext &context, Node route, + Random &random) { + auto &&insertions = star_caches[route]; + if (!insertions.empty()) { + return; + } + insertions.resize(problem.num_customers); + for (Node customer = 1; customer < problem.num_customers; ++customer) { + insertions[customer].Reset(); + } + Node predecessor = 0; + Node successor = context.heads[route]; + while (true) { + Node predecessor_customer = solution.customer(predecessor); + Node successor_customer = solution.customer(successor); + auto &&predecessor_distances = distance_matrix[predecessor_customer]; + auto &&successor_distances = distance_matrix[successor_customer]; + auto distance = distance_matrix[predecessor_customer][successor_customer]; + for (Node customer = 1; customer < problem.num_customers; ++customer) { + int delta = predecessor_distances[customer] + + successor_distances[customer] - distance; + insertions[customer].Add(delta, predecessor, successor, random); + } + if (!successor) { + break; + } + predecessor = successor; + successor = solution.successor(successor); + } +} + +template +void PreprocessRouteInsertions(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, + const RouteContext &context, + std::vector> &insertions, + Random &random) { + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + Node node_x = context.heads[route_x]; + while (node_x) { + auto &insertion = insertions[node_x]; + insertion.Reset(); + Load load = solution.load(node_x); + for (Node route_y = 0; route_y < context.num_routes; ++route_y) { + if (route_x == route_y || + context.route_loads[route_y] + load > problem.capacity) { + continue; + } + Node predecessor = 0; + Node successor = context.heads[route_y]; + Insertion best_insertion; + best_insertion.delta = Delta(std::numeric_limits::max(), -1); + while (true) { + int delta = CalcDelta(distance_matrix, solution, node_x, predecessor, + successor); + if (best_insertion.delta.Update(delta, random)) { + best_insertion.predecessor = predecessor; + best_insertion.successor = successor; + } + if (!successor) { + break; + } + predecessor = successor; + successor = solution.successor(successor); + } + if (best_insertion.delta.counter > 0) { + insertion.Add(best_insertion, random); + } + } + node_x = solution.successor(node_x); + } + } +} + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_ALGORITHM_BASE_STAR_H_ diff --git a/src/algorithm/construction.cc b/src/algorithm/construction.cc new file mode 100644 index 0000000..a4f1661 --- /dev/null +++ b/src/algorithm/construction.cc @@ -0,0 +1,203 @@ +#include "algorithm/construction.h" + +#include +#include +#include + +#include "model/route_context.h" +#include "util/utils.h" + +namespace alkaid_sd { + +enum InsertionCriterion { kMcfic, kNfic }; + +enum InsertionStrategies { kSis, kPis }; + +using CandidateList = std::vector; + +int AddRoute(const Problem &problem, CandidateList &candidate_list, + Random &random, Solution &solution, RouteContext &context) { + int position = random.NextInt(0, static_cast(candidate_list.size()) - 1); + Node customer = candidate_list[position]; + Node node_index = + solution.Insert(customer, problem.customers[customer].demand, 0, 0); + candidate_list[position] = candidate_list.back(); + candidate_list.pop_back(); + context.heads[context.num_routes] = node_index; + context.route_loads[context.num_routes] = problem.customers[customer].demand; + ++context.num_routes; + return position; +} + +template +void SequentialInsertion(const Problem &problem, const Func &func, + CandidateList &candidate_list, Random &random, + Solution &solution, RouteContext &context) { + InsertionWithCost best_insertion{}; + std::vector is_full(context.num_routes, false); + while (!candidate_list.empty()) { + bool inserted = false; + for (Node route_index = 0; route_index < context.num_routes; + ++route_index) { + if (is_full[route_index]) { + continue; + } + int candidate_position = -1; + best_insertion.cost = Delta(std::numeric_limits::max(), -1); + for (int i = 0; i < static_cast(candidate_list.size()); ++i) { + Node customer = candidate_list[i]; + int demand = problem.customers[customer].demand; + if (context.route_loads[route_index] + demand > problem.capacity) { + continue; + } + auto insertion = CalcBestInsertion(solution, func, context, route_index, + customer, random); + if (best_insertion.Update(insertion, random)) { + candidate_position = i; + } + } + if (candidate_position == -1) { + is_full[route_index] = true; + } else { + Node customer = candidate_list[candidate_position]; + candidate_list[candidate_position] = candidate_list.back(); + candidate_list.pop_back(); + int demand = problem.customers[customer].demand; + Node node_index = + solution.Insert(customer, demand, best_insertion.predecessor, + best_insertion.successor); + if (best_insertion.predecessor == 0) { + context.heads[route_index] = node_index; + } + context.route_loads[route_index] += demand; + inserted = true; + } + } + if (!inserted) { + AddRoute(problem, candidate_list, random, solution, context); + is_full.push_back(false); + } + } +} + +template +void ParallelInsertion(const Problem &problem, const Func &func, + CandidateList &candidate_list, Random &random, + Solution &solution, RouteContext &context) { + std::vector>> best_insertions( + candidate_list.size()); + for (int i = 0; i < static_cast(candidate_list.size()); ++i) { + for (Node j = 0; j < context.num_routes; ++j) { + best_insertions[i].push_back(CalcBestInsertion( + solution, func, context, j, candidate_list[i], random)); + } + } + std::vector updated(context.num_routes, false); + InsertionWithCost best_insertion{}; + while (!candidate_list.empty()) { + int candidate_position = -1; + best_insertion.cost = Delta(std::numeric_limits::max(), -1); + for (int i = 0; i < static_cast(best_insertions.size()); ++i) { + for (int j = 0; j < static_cast(best_insertions[i].size()); ++j) { + Node route_index = best_insertions[i][j].route_index; + int demand = problem.customers[candidate_list[i]].demand; + if (context.route_loads[route_index] + demand > problem.capacity) { + best_insertions[i][j] = best_insertions[i].back(); + best_insertions[i].pop_back(); + --j; + } else { + if (updated[route_index]) { + best_insertions[i][j] = + CalcBestInsertion(solution, func, context, route_index, + candidate_list[i], random); + } + if (best_insertion.Update(best_insertions[i][j], random)) { + candidate_position = i; + } + } + } + } + if (candidate_position == -1) { + int position = + AddRoute(problem, candidate_list, random, solution, context); + best_insertions[position] = std::move(best_insertions.back()); + best_insertions.pop_back(); + for (int i = 0; i < static_cast(candidate_list.size()); ++i) { + Node customer = candidate_list[i]; + best_insertions[i].push_back(CalcBestInsertion( + solution, func, context, context.num_routes - 1, customer, random)); + } + updated.push_back(false); + } else { + Node customer = candidate_list[candidate_position]; + candidate_list[candidate_position] = candidate_list.back(); + candidate_list.pop_back(); + best_insertions[candidate_position] = std::move(best_insertions.back()); + best_insertions.pop_back(); + int demand = problem.customers[customer].demand; + Node node_index = + solution.Insert(customer, demand, best_insertion.predecessor, + best_insertion.successor); + Node route_index = best_insertion.route_index; + if (best_insertion.predecessor == 0) { + context.heads[route_index] = node_index; + } + context.route_loads[route_index] += demand; + updated[route_index] = true; + } + } +} + +template +void InsertCandidates(const Problem &problem, const Func &func, + CandidateList &candidate_list, Random &random, + Solution &solution, RouteContext &context) { + int strategy = random.NextInt(0, 1); + if (strategy == kSis) { + SequentialInsertion(problem, func, candidate_list, random, solution, + context); + } else { + ParallelInsertion(problem, func, candidate_list, random, solution, context); + } +} + +std::unique_ptr Construct(const Problem &problem, + const DistanceMatrix &distance_matrix, + Random &random) { + std::vector candidate_list(problem.num_customers - 1); + std::iota(candidate_list.begin(), candidate_list.end(), 1); + Node num_fleets = CalcFleetLowerBound(problem); + auto solution = std::make_unique(); + auto context = std::make_unique(); + for (Node i = 0; i < num_fleets; ++i) { + AddRoute(problem, candidate_list, random, *solution, *context); + } + int criterion = random.NextInt(0, 1); + if (criterion == kMcfic) { + float gamma = static_cast(random.NextInt(0, 34)) * 0.05f; + auto func = [&](Node predecessor, Node successor, Node customer) { + Node pre_customer = solution->customer(predecessor); + Node suc_customer = solution->customer(successor); + return static_cast(distance_matrix[pre_customer][customer] + + distance_matrix[customer][suc_customer] - + distance_matrix[pre_customer][suc_customer]) - + 2 * gamma * distance_matrix[0][customer]; + }; + InsertCandidates(problem, func, candidate_list, random, *solution, + *context); + } else { + auto func = [&](Node predecessor, Node successor, Node customer) { + Node pre_customer = solution->customer(predecessor); + if (pre_customer == 0) { + return std::numeric_limits::max(); + } else { + return static_cast(distance_matrix[pre_customer][customer]); + } + }; + InsertCandidates(problem, func, candidate_list, random, *solution, + *context); + } + return solution; +} + +} // namespace alkaid_sd diff --git a/src/algorithm/construction.h b/src/algorithm/construction.h new file mode 100644 index 0000000..8cd55a7 --- /dev/null +++ b/src/algorithm/construction.h @@ -0,0 +1,20 @@ +#ifndef ALKAID_SD_SRC_ALGORITHM_CONSTRUCTION_H_ +#define ALKAID_SD_SRC_ALGORITHM_CONSTRUCTION_H_ + +#include + +#include "distance_matrix.h" +#include "model/solution.h" +#include "problem.h" +#include "util/random.h" + + +namespace alkaid_sd { + +std::unique_ptr Construct(const Problem &problem, + const DistanceMatrix &distance_matrix, + Random &random); + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_ALGORITHM_CONSTRUCTION_H_ diff --git a/src/algorithm/operator/cross.cc b/src/algorithm/operator/cross.cc new file mode 100644 index 0000000..08461e5 --- /dev/null +++ b/src/algorithm/operator/cross.cc @@ -0,0 +1,120 @@ +#include "algorithm/operators.h" + +#include "util/solution_utils.h" + +namespace alkaid_sd { + +struct CrossMove { + bool reversed; + Node route_x, route_y; + Node left_x, left_y; +}; + +void DoCross(const CrossMove &move, Solution &solution, RouteContext &context) { + Node right_x = move.left_x ? solution.successor(move.left_x) + : context.heads[move.route_x]; + Node right_y = move.left_y ? solution.successor(move.left_y) + : context.heads[move.route_y]; + if (!move.reversed) { + solution.Link(move.left_x, right_y); + solution.Link(move.left_y, right_x); + if (move.left_x == 0) { + context.heads[move.route_x] = right_y; + } + if (move.left_y == 0) { + context.heads[move.route_y] = right_x; + } + } else { + Node head_y = context.heads[move.route_y]; + if (right_x) { + Node tail_x = context.tails[move.route_x]; + ReversedLink(solution, right_x, tail_x, 0, right_y); + context.heads[move.route_y] = tail_x; + } else { + solution.Link(0, right_y); + context.heads[move.route_y] = right_y; + } + solution.set_successor(0, context.heads[move.route_x]); + if (move.left_y) { + ReversedLink(solution, head_y, move.left_y, move.left_x, 0); + } else { + solution.Link(move.left_x, 0); + } + context.heads[move.route_x] = solution.successor(0); + } +} + +void Cross(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, Node route_x, + Node route_y, CrossMove &best_move, Delta &best_delta, + Random &random) { + Node left_x = 0; + do { + Node successor_x = + left_x ? solution.successor(left_x) : context.heads[route_x]; + Node left_y = 0; + do { + Node predecessor_y = left_y; + Node successor_y = + left_y ? solution.successor(left_y) : context.heads[route_y]; + int predecessor_load_x = context.loads[left_x]; + int successor_load_x = context.route_loads[route_x] - predecessor_load_x; + int predecessor_load_y = context.loads[left_y]; + int successor_load_y = context.route_loads[route_y] - predecessor_load_y; + int base = -distance_matrix[solution.customer(left_x)] + [solution.customer(successor_x)] - + distance_matrix[solution.customer(left_y)] + [solution.customer(successor_y)]; + for (bool reversed : {false, true}) { + if (predecessor_load_x + successor_load_y <= problem.capacity && + successor_load_x + predecessor_load_y <= problem.capacity) { + int delta = base + + distance_matrix[solution.customer(left_x)] + [solution.customer(successor_y)] + + distance_matrix[solution.customer(successor_x)] + [solution.customer(predecessor_y)]; + if (best_delta.Update(delta, random)) { + best_move = {reversed, route_x, route_y, left_x, left_y}; + } + } + std::swap(predecessor_y, successor_y); + std::swap(predecessor_load_y, successor_load_y); + } + left_y = successor_y; + } while (left_y); + left_x = successor_x; + } while (left_x); +} + +bool Cross(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + CrossMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = route_x + 1; route_y < context.num_routes; ++route_y) { + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + Cross(problem, distance_matrix, solution, context, route_x, route_y, + move, cache.delta, random); + } else { + move.route_x = route_x; + move.route_y = route_y; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoCross(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/exchange.cc b/src/algorithm/operator/exchange.cc new file mode 100644 index 0000000..bd3244b --- /dev/null +++ b/src/algorithm/operator/exchange.cc @@ -0,0 +1,79 @@ +#include "algorithm/operators.h" + +namespace alkaid_sd { + +struct ExchangeMove { + Node node_a; + Node node_b; +}; + +void DoExchange(const ExchangeMove &move, Node route_index, Solution &solution, + RouteContext &context) { + Node predecessor_a = solution.predecessor(move.node_a); + Node successor_a = solution.successor(move.node_a); + Node predecessor_b = solution.predecessor(move.node_b); + Node successor_b = solution.successor(move.node_b); + solution.Link(predecessor_a, move.node_b); + solution.Link(move.node_b, successor_a); + solution.Link(predecessor_b, move.node_a); + solution.Link(move.node_a, successor_b); + if (!predecessor_a) { + context.heads[route_index] = move.node_b; + } + UpdateRouteContext(solution, route_index, predecessor_a, context); +} + +void Exchange(const DistanceMatrix &distance_matrix, const Solution &solution, + Node node_a, Node node_b, ExchangeMove &best_move, + Delta &best_delta, Random &random) { + Node predecessor_a = solution.predecessor(node_a); + Node successor_a = solution.successor(node_a); + Node predecessor_b = solution.predecessor(node_b); + Node successor_b = solution.successor(node_b); + int delta = distance_matrix[solution.customer(predecessor_a)] + [solution.customer(node_b)] + + distance_matrix[solution.customer(node_b)] + [solution.customer(successor_a)] + + distance_matrix[solution.customer(predecessor_b)] + [solution.customer(node_a)] + + distance_matrix[solution.customer(node_a)] + [solution.customer(successor_b)] - + distance_matrix[solution.customer(predecessor_a)] + [solution.customer(node_a)] - + distance_matrix[solution.customer(node_a)] + [solution.customer(successor_a)] - + distance_matrix[solution.customer(predecessor_b)] + [solution.customer(node_b)] - + distance_matrix[solution.customer(node_b)] + [solution.customer(successor_b)]; + if (best_delta.Update(delta, random)) { + best_move = {node_a, node_b}; + } +} + +bool Exchange(const Problem &problem, const DistanceMatrix &distance_matrix, + Node route_index, Solution &solution, RouteContext &context, + Random &random) { + ExchangeMove best_move{}; + Delta best_delta; + Node node_a = context.heads[route_index]; + while (node_a) { + Node node_b = solution.successor(node_a); + if (node_b) { + node_b = solution.successor(node_b); + while (node_b) { + Exchange(distance_matrix, solution, node_a, node_b, best_move, + best_delta, random); + node_b = solution.successor(node_b); + } + } + node_a = solution.successor(node_a); + } + if (best_delta.value < 0) { + DoExchange(best_move, route_index, solution, context); + return true; + } + return false; +} + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/or_opt.cc b/src/algorithm/operator/or_opt.cc new file mode 100644 index 0000000..8cf8b92 --- /dev/null +++ b/src/algorithm/operator/or_opt.cc @@ -0,0 +1,118 @@ +#include "algorithm/operators.h" + +#include "util/solution_utils.h" + +namespace alkaid_sd { + +struct OrOptMove { + bool reversed; + Node head; + Node tail; + Node predecessor; + Node successor; +}; + +void DoOrOpt(const OrOptMove &move, Node route_index, Solution &solution, + RouteContext &context) { + Node predecessor_head = solution.predecessor(move.head); + Node successor_tail = solution.successor(move.tail); + solution.set_successor(0, context.heads[route_index]); + solution.Link(predecessor_head, successor_tail); + if (!move.reversed) { + solution.Link(move.predecessor, move.head); + solution.Link(move.tail, move.successor); + } else { + ReversedLink(solution, move.head, move.tail, move.predecessor, + move.successor); + } + context.heads[route_index] = solution.successor(0); +} + +template +void OrOpt(const DistanceMatrix &distance_matrix, const Solution &solution, + Node head, Node tail, Node predecessor, Node successor, + OrOptMove &best_move, Delta &best_delta, Random &random) { + Node predecessor_head = solution.predecessor(head); + Node successor_tail = solution.successor(tail); + int delta = distance_matrix[solution.customer(predecessor_head)] + [solution.customer(successor_tail)] - + distance_matrix[solution.customer(predecessor_head)] + [solution.customer(head)] - + distance_matrix[solution.customer(tail)] + [solution.customer(successor_tail)] - + distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)]; + bool reversed = false; + int insertion_delta = + distance_matrix[solution.customer(predecessor)][solution.customer(head)] + + distance_matrix[solution.customer(successor)][solution.customer(tail)]; + if (num > 1) { + int reversed_delta = + distance_matrix[solution.customer(predecessor)] + [solution.customer(tail)] + + distance_matrix[solution.customer(successor)][solution.customer(head)]; + if (reversed_delta < insertion_delta) { + insertion_delta = reversed_delta; + reversed = true; + } + } + delta += insertion_delta; + if (best_delta.Update(delta, random)) { + best_move = {reversed, head, tail, predecessor, successor}; + } +} + +template +bool OrOpt(const Problem &problem, const DistanceMatrix &distance_matrix, + Node route_index, Solution &solution, RouteContext &context, + Random &random) { + OrOptMove best_move{}; + Delta best_delta; + Node head = context.heads[route_index]; + Node tail = head; + for (Node i = 0; tail && i < num - 1; ++i) { + tail = solution.successor(tail); + } + while (tail) { + Node predecessor, successor; + predecessor = solution.successor(tail); + while (predecessor) { + successor = solution.successor(predecessor); + OrOpt(distance_matrix, solution, head, tail, predecessor, successor, + best_move, best_delta, random); + predecessor = successor; + } + successor = solution.predecessor(head); + while (successor) { + predecessor = solution.predecessor(successor); + OrOpt(distance_matrix, solution, head, tail, predecessor, successor, + best_move, best_delta, random); + successor = predecessor; + } + head = solution.successor(head); + tail = solution.successor(tail); + } + if (best_delta.value < 0) { + DoOrOpt(best_move, route_index, solution, context); + UpdateRouteContext(solution, route_index, 0, context); + return true; + } + return false; +} + +template bool OrOpt<1>(const Problem &problem, + const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context, + Random &random); + +template bool OrOpt<2>(const Problem &problem, + const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context, + Random &random); + +template bool OrOpt<3>(const Problem &problem, + const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context, + Random &random); + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/relocate.cc b/src/algorithm/operator/relocate.cc new file mode 100644 index 0000000..f64001b --- /dev/null +++ b/src/algorithm/operator/relocate.cc @@ -0,0 +1,88 @@ +#include "algorithm/operators.h" + +#include "algorithm/base_star.h" +#include "model/route_head_guard.h" + +namespace alkaid_sd { + +struct RelocateMove { + Node route_x, route_y; + Node node_x, predecessor_x, successor_x; +}; + +inline void DoRelocate(RelocateMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_x = solution.predecessor(move.node_x); + Node successor_x = solution.successor(move.node_x); + { + RouteHeadGuard guard(solution, context, move.route_x); + solution.Link(predecessor_x, successor_x); + } + { + RouteHeadGuard guard(solution, context, move.route_y); + solution.Link(move.predecessor_x, move.node_x); + solution.Link(move.node_x, move.successor_x); + } +} + +void Relocate(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, RelocateMove &best_move, + Delta &best_delta, Random &random) { + PreprocessStarInsertions(problem, distance_matrix, solution, context, route_y, + random); + Node node_x = context.heads[route_x]; + while (node_x) { + if (context.route_loads[route_y] + solution.load(node_x) <= + problem.capacity) { + auto insertion = + star_caches[route_y][solution.customer(node_x)].FindBest(); + Node predecessor_x = solution.predecessor(node_x); + Node successor_x = solution.successor(node_x); + int delta = + insertion->delta.value - CalcDelta(distance_matrix, solution, node_x, + predecessor_x, successor_x); + if (best_delta.Update(delta, random)) { + best_move = {route_x, route_y, node_x, insertion->predecessor, + insertion->successor}; + } + } + node_x = solution.successor(node_x); + } +} + +bool Relocate(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + RelocateMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = 0; route_y < context.num_routes; ++route_y) { + if (route_x == route_y) { + continue; + } + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + Relocate(problem, distance_matrix, solution, context, route_x, route_y, + move, cache.delta, random); + } else { + move.route_x = route_x; + move.route_y = route_y; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoRelocate(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/repair.cc b/src/algorithm/operator/repair.cc new file mode 100644 index 0000000..f8712b0 --- /dev/null +++ b/src/algorithm/operator/repair.cc @@ -0,0 +1,69 @@ +#include "algorithm/operators.h" + +#include + +namespace alkaid_sd { + +void MergeAdjacentSameCustomers(const DistanceMatrix &distance_matrix, + Node route_index, Solution &solution, + RouteContext &context) { + Node node_index = context.heads[route_index]; + while (true) { + Node successor = solution.successor(node_index); + if (!successor) { + break; + } + if (solution.customer(node_index) == solution.customer(successor)) { + solution.set_load(node_index, + solution.load(node_index) + solution.load(successor)); + solution.Remove(successor); + } else { + node_index = successor; + } + } +} + +int CalcRemovalDelta(const DistanceMatrix &distance_matrix, + const Solution &solution, Node node_index) { + Node predecessor = solution.predecessor(node_index); + Node successor = solution.successor(node_index); + return distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)] - + distance_matrix[solution.customer(predecessor)] + [solution.customer(node_index)] - + distance_matrix[solution.customer(node_index)] + [solution.customer(successor)]; +} + +void Repair(const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context) { + if (!context.heads[route_index]) { + return; + } + MergeAdjacentSameCustomers(distance_matrix, route_index, solution, context); + std::map customer_node_map; + Node node_index = context.heads[route_index]; + solution.set_successor(0, node_index); + while (node_index) { + Node successor = solution.successor(node_index); + Node customer = solution.customer(node_index); + auto it = customer_node_map.find(customer); + if (it == customer_node_map.end()) { + customer_node_map[customer] = node_index; + } else { + Node &last_node_index = it->second; + if (CalcRemovalDelta(distance_matrix, solution, last_node_index) < + CalcRemovalDelta(distance_matrix, solution, node_index)) { + std::swap(last_node_index, node_index); + } + solution.set_load(last_node_index, solution.load(last_node_index) + + solution.load(node_index)); + solution.Remove(node_index); + } + node_index = successor; + } + context.heads[route_index] = solution.successor(0); + UpdateRouteContext(solution, route_index, 0, context); +} + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/sd_swap_one_one.cc b/src/algorithm/operator/sd_swap_one_one.cc new file mode 100644 index 0000000..c636673 --- /dev/null +++ b/src/algorithm/operator/sd_swap_one_one.cc @@ -0,0 +1,131 @@ +#include "algorithm/operators.h" + +#include "algorithm/base_star.h" +#include "model/route_head_guard.h" +#include "util/solution_utils.h" + +namespace alkaid_sd { + +struct SdSwapOneOneMove { + bool swapped; + Node route_x, route_y; + Node node_x, predecessor_x, successor_x; + Node node_y, predecessor_y, successor_y; + int split_load; +}; + +void DoSdSwapOneOne(const SdSwapOneOneMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_y = solution.predecessor(move.node_y); + Node successor_y = solution.successor(move.node_y); + { + RouteHeadGuard guard(solution, context, move.route_x); + solution.set_load(move.node_x, move.split_load); + solution.Link(move.predecessor_y, move.node_y); + solution.Link(move.node_y, move.successor_y); + } + { + RouteHeadGuard guard(solution, context, move.route_y); + solution.Link(predecessor_y, successor_y); + solution.Insert(solution.customer(move.node_x), solution.load(move.node_y), + move.predecessor_x, move.successor_x); + } +} + +inline void SdSwapOneOne(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + bool swapped, Node route_x, Node route_y, Node node_x, + Node node_y, int split_load, + SdSwapOneOneMove &best_move, Delta &best_delta, + Random &random) { + Node predecessor_x = solution.predecessor(node_x); + Node successor_x = solution.successor(node_x); + Node predecessor_y = solution.predecessor(node_y); + Node successor_y = solution.successor(node_y); + int delta = + -CalcDelta(distance_matrix, solution, node_y, predecessor_y, successor_y); + int delta_x = + CalcDelta(distance_matrix, solution, node_x, predecessor_y, successor_y); + int before = + CalcDelta(distance_matrix, solution, node_y, predecessor_x, node_x); + int after = CalcDelta(distance_matrix, solution, node_y, node_x, successor_x); + int delta_y; + Node predecessor; + Node successor; + if (before <= after) { + predecessor = predecessor_x; + successor = node_x; + delta_y = before; + } else { + predecessor = node_x; + successor = successor_x; + delta_y = after; + } + delta += delta_x + delta_y; + if (best_delta.Update(delta, random)) { + best_move = {swapped, route_x, route_y, node_x, predecessor_y, + successor_y, node_y, predecessor, successor, split_load}; + } +} + +void SdSwapOneOne(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, SdSwapOneOneMove &best_move, + Delta &best_delta, Random &random) { + for (Node node_x = context.heads[route_x]; node_x; + node_x = solution.successor(node_x)) { + Load load_x = solution.load(node_x); + for (Node node_y = context.heads[route_y]; node_y; + node_y = solution.successor(node_y)) { + Load load_y = solution.load(node_y); + if (load_x > load_y) { + SdSwapOneOne(problem, distance_matrix, solution, context, false, + route_x, route_y, node_x, node_y, load_x - load_y, + best_move, best_delta, random); + } else if (load_y > load_x) { + SdSwapOneOne(problem, distance_matrix, solution, context, true, route_y, + route_x, node_y, node_x, load_y - load_x, best_move, + best_delta, random); + } + } + } +} + +bool SdSwapOneOne(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SdSwapOneOneMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = route_x + 1; route_y < context.num_routes; ++route_y) { + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + SdSwapOneOne(problem, distance_matrix, solution, context, route_x, + route_y, move, cache.delta, random); + } else { + if (!move.swapped) { + move.route_x = route_x; + move.route_y = route_y; + } else { + move.route_x = route_y; + move.route_y = route_x; + } + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSdSwapOneOne(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/sd_swap_star.cc b/src/algorithm/operator/sd_swap_star.cc new file mode 100644 index 0000000..cfcbd33 --- /dev/null +++ b/src/algorithm/operator/sd_swap_star.cc @@ -0,0 +1,143 @@ +#include "algorithm/operators.h" + +#include + +#include "algorithm/base_star.h" +#include "model/route_head_guard.h" + +namespace alkaid_sd { + +struct SdSwapStarMove { + bool swapped; + Node route_x, route_y; + Node node_x, predecessor_x, successor_x; + Node node_y, predecessor_y, successor_y; + int split_load; +}; + +inline void DoSdSwapStar(SdSwapStarMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_y = solution.predecessor(move.node_y); + Node successor_y = solution.successor(move.node_y); + { + RouteHeadGuard guard(solution, context, move.route_x); + solution.set_load(move.node_x, move.split_load); + solution.Link(move.predecessor_y, move.node_y); + solution.Link(move.node_y, move.successor_y); + } + { + RouteHeadGuard guard(solution, context, move.route_y); + solution.Link(predecessor_y, successor_y); + solution.Insert(solution.customer(move.node_x), solution.load(move.node_y), + move.predecessor_x, move.successor_x); + } +} + +inline void +SdSwapStar(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, bool swapped, + Node route_x, Node route_y, Node node_x, Node node_y, int split_load, + const std::vector> &insertions, + SdSwapStarMove &best_move, Delta &best_delta, Random &random) { + auto &&insertion_x = insertions[node_x]; + auto &&insertion_y = insertions[node_y]; + Node predecessor_y = solution.predecessor(node_y); + Node successor_y = solution.successor(node_y); + int delta = + -CalcDelta(distance_matrix, solution, node_y, predecessor_y, successor_y); + int delta_x = + CalcDelta(distance_matrix, solution, node_x, predecessor_y, successor_y); + auto best_insertion_y = insertion_y.FindBest(); + auto best_insertion_x = insertion_x.FindBestWithoutNode(node_y); + if (best_insertion_x && best_insertion_x->delta.value < delta_x) { + delta_x = best_insertion_x->delta.value; + predecessor_y = best_insertion_x->predecessor; + successor_y = best_insertion_x->successor; + } + delta += delta_x + best_insertion_y->delta.value; + if (best_delta.Update(delta, random)) { + best_move = {swapped, + route_x, + route_y, + node_x, + predecessor_y, + successor_y, + node_y, + best_insertion_y->predecessor, + best_insertion_y->successor, + split_load}; + } +} + +void SdSwapStar(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, SdSwapStarMove &best_move, + Delta &best_delta, + std::vector> &insertions, Random &random) { + PreprocessInsertions(distance_matrix, solution, context, route_x, route_y, + insertions, random); + PreprocessInsertions(distance_matrix, solution, context, route_y, route_x, + insertions, random); + Node node_x = context.heads[route_x]; + while (node_x) { + Load load_x = solution.load(node_x); + Node node_y = context.heads[route_y]; + while (node_y) { + Load load_y = solution.load(node_y); + if (load_x > load_y) { + SdSwapStar(problem, distance_matrix, solution, context, false, route_x, + route_y, node_x, node_y, load_x - load_y, insertions, + best_move, best_delta, random); + } else if (load_y > load_x) { + SdSwapStar(problem, distance_matrix, solution, context, true, route_y, + route_x, node_y, node_x, load_y - load_x, insertions, + best_move, best_delta, random); + } + node_y = solution.successor(node_y); + } + node_x = solution.successor(node_x); + } +} + +bool SdSwapStar(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + static std::vector> insertions; + SdSwapStarMove best_move{}; + Delta best_delta; + Node max_index = *std::max_element( + solution.node_pool(), solution.node_pool() + solution.num_nodes()); + insertions.clear(); + insertions.resize(max_index + 1); + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = route_x + 1; route_y < context.num_routes; ++route_y) { + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + SdSwapStar(problem, distance_matrix, solution, context, route_x, + route_y, move, cache.delta, insertions, random); + } else { + if (!move.swapped) { + move.route_x = route_x; + move.route_y = route_y; + } else { + move.route_x = route_y; + move.route_y = route_x; + } + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSdSwapStar(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/sd_swap_two_one.cc b/src/algorithm/operator/sd_swap_two_one.cc new file mode 100644 index 0000000..4a8c472 --- /dev/null +++ b/src/algorithm/operator/sd_swap_two_one.cc @@ -0,0 +1,272 @@ +#include "algorithm/operators.h" + +#include "model/route_head_guard.h" +#include "util/solution_utils.h" + +namespace alkaid_sd { + +struct SdSwapTwoOneMove { + bool type; + Node route_ij, route_k; + Node predecessor_ij, successor_ij; + Node node_i, node_j, node_k; + int split_load; + bool direction_ij, direction_ijk; +}; + +void DoSdSwapTwoOne(const SdSwapTwoOneMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_k = solution.predecessor(move.node_k); + Node successor_k = solution.successor(move.node_k); + { + RouteHeadGuard guard(solution, context, move.route_ij); + solution.Link(move.predecessor_ij, move.successor_ij); + } + { + RouteHeadGuard guard(solution, context, move.route_k); + solution.Link(predecessor_k, successor_k); + } + if (move.type == 0) { + int new_node_j = + solution.NewNode(solution.customer(move.node_j), move.split_load); + solution.set_load(move.node_j, + solution.load(move.node_j) - move.split_load); + { + RouteHeadGuard guard(solution, context, move.route_ij); + if (move.direction_ijk) { + solution.Link(move.predecessor_ij, new_node_j); + solution.Link(new_node_j, move.node_k); + solution.Link(move.node_k, move.successor_ij); + } else { + solution.Link(move.predecessor_ij, move.node_k); + solution.Link(move.node_k, new_node_j); + solution.Link(new_node_j, move.successor_ij); + } + } + { + RouteHeadGuard guard(solution, context, move.route_k); + if (move.direction_ij) { + solution.Link(predecessor_k, move.node_i); + solution.Link(move.node_i, move.node_j); + solution.Link(move.node_j, successor_k); + } else { + solution.Link(predecessor_k, move.node_j); + solution.Link(move.node_j, move.node_i); + solution.Link(move.node_i, successor_k); + } + } + } else { + int new_node_k = + solution.NewNode(solution.customer(move.node_k), move.split_load); + solution.set_load(move.node_k, + solution.load(move.node_k) - move.split_load); + { + RouteHeadGuard guard(solution, context, move.route_ij); + solution.Link(move.predecessor_ij, move.node_k); + solution.Link(move.node_k, move.successor_ij); + } + { + RouteHeadGuard guard(solution, context, move.route_k); + int before_ij; + int after_ij; + if (move.direction_ij) { + solution.Link(predecessor_k, move.node_i); + solution.Link(move.node_i, move.node_j); + solution.Link(move.node_j, successor_k); + before_ij = move.node_i; + after_ij = move.node_j; + } else { + solution.Link(predecessor_k, move.node_j); + solution.Link(move.node_j, move.node_i); + solution.Link(move.node_i, successor_k); + before_ij = move.node_j; + after_ij = move.node_i; + } + if (move.direction_ijk) { + solution.Link(after_ij, new_node_k); + solution.Link(new_node_k, successor_k); + } else { + solution.Link(predecessor_k, new_node_k); + solution.Link(new_node_k, before_ij); + } + } + } +} + +inline void SdSwapTwoOne0(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_ij, Node route_k, Node node_i, Node node_j, + Node node_k, Node predecessor_ij, Node successor_ij, + int split_load, int base_delta, + SdSwapTwoOneMove &best_move, Delta &best_delta, + Random &random) { + Node predecessor_k = solution.predecessor(node_k); + Node successor_k = solution.successor(node_k); + int delta_ij = distance_matrix[solution.customer(predecessor_k)] + [solution.customer(node_i)] + + distance_matrix[solution.customer(node_j)] + [solution.customer(successor_k)]; + int delta_ji = distance_matrix[solution.customer(predecessor_k)] + [solution.customer(node_j)] + + distance_matrix[solution.customer(node_i)] + [solution.customer(successor_k)]; + int delta_jk = distance_matrix[solution.customer(predecessor_ij)] + [solution.customer(node_j)] + + distance_matrix[solution.customer(node_k)] + [solution.customer(successor_ij)]; + int delta_kj = distance_matrix[solution.customer(predecessor_ij)] + [solution.customer(node_k)] + + distance_matrix[solution.customer(node_j)] + [solution.customer(successor_ij)]; + bool direction_ij = true; + if (delta_ij > delta_ji) { + delta_ij = delta_ji; + direction_ij = false; + } + bool direction_jk = true; + if (delta_jk > delta_kj) { + delta_jk = delta_kj; + direction_jk = false; + } + int delta = + base_delta + + distance_matrix[solution.customer(node_j)][solution.customer(node_k)] + + delta_ij + delta_jk; + if (best_delta.Update(delta, random)) { + best_move = {0, route_ij, route_k, predecessor_ij, successor_ij, + node_i, node_j, node_k, split_load, direction_ij, + direction_jk}; + } +} + +inline void SdSwapTwoOne1(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_ij, Node route_k, Node node_i, Node node_j, + Node node_k, Node predecessor_ij, Node successor_ij, + int split_load, int base_delta, + SdSwapTwoOneMove &best_move, Delta &best_delta, + Random &random) { + Node predecessor_k = solution.predecessor(node_k); + Node successor_k = solution.successor(node_k); + base_delta += distance_matrix[solution.customer(predecessor_ij)] + [solution.customer(node_k)] + + distance_matrix[solution.customer(node_k)] + [solution.customer(successor_ij)]; + for (bool direction_ij : {true, false}) { + int before_ij = node_i; + int after_ij = node_j; + if (!direction_ij) { + std::swap(before_ij, after_ij); + } + for (bool direction_ijk : {true, false}) { + int delta_ijk; + if (direction_ijk) { + delta_ijk = distance_matrix[solution.customer(predecessor_k)] + [solution.customer(before_ij)] + + distance_matrix[solution.customer(after_ij)] + [solution.customer(node_k)] + + distance_matrix[solution.customer(node_k)] + [solution.customer(successor_k)]; + } else { + delta_ijk = distance_matrix[solution.customer(predecessor_k)] + [solution.customer(node_k)] + + distance_matrix[solution.customer(node_k)] + [solution.customer(before_ij)] + + distance_matrix[solution.customer(after_ij)] + [solution.customer(successor_k)]; + } + int delta = base_delta + delta_ijk; + if (best_delta.Update(delta, random)) { + best_move = { + 1, route_ij, route_k, predecessor_ij, successor_ij, node_i, + node_j, node_k, split_load, direction_ij, direction_ijk}; + } + } + } +} + +void SdSwapTwoOne(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_ij, Node route_k, SdSwapTwoOneMove &best_move, + Delta &best_delta, Random &random) { + Node node_i = context.heads[route_ij]; + Node node_j = solution.successor(node_i); + while (node_j) { + Load load_i = solution.load(node_i); + Load load_j = solution.load(node_j); + for (Node node_k = context.heads[route_k]; node_k; + node_k = solution.successor(node_k)) { + Load load_k = solution.load(node_k); + int predecessor_ij = solution.predecessor(node_i); + int successor_ij = solution.successor(node_j); + int base_delta = + -distance_matrix[solution.customer(predecessor_ij)] + [solution.customer(node_i)] - + distance_matrix[solution.customer(node_j)] + [solution.customer(successor_ij)] - + distance_matrix[solution.customer(solution.predecessor(node_k))] + [solution.customer(node_k)] - + distance_matrix[solution.customer(node_k)] + [solution.customer(solution.successor(node_k))]; + if (load_i + load_j > load_k) { + if (load_i < load_k) { + SdSwapTwoOne0(problem, distance_matrix, solution, context, route_ij, + route_k, node_i, node_j, node_k, predecessor_ij, + successor_ij, load_i + load_j - load_k, base_delta, + best_move, best_delta, random); + } + if (load_j < load_k) { + SdSwapTwoOne0(problem, distance_matrix, solution, context, route_ij, + route_k, node_j, node_i, node_k, predecessor_ij, + successor_ij, load_i + load_j - load_k, base_delta, + best_move, best_delta, random); + } + } else if (load_k > load_i + load_j) { + SdSwapTwoOne1(problem, distance_matrix, solution, context, route_ij, + route_k, node_i, node_j, node_k, predecessor_ij, + successor_ij, load_k - load_i - load_j, base_delta, + best_move, best_delta, random); + } + } + node_i = node_j; + node_j = solution.successor(node_j); + } +} + +bool SdSwapTwoOne(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SdSwapTwoOneMove best_move{}; + Delta best_delta; + for (Node route_ij = 0; route_ij < context.num_routes; ++route_ij) { + for (Node route_k = 0; route_k < context.num_routes; ++route_k) { + if (route_ij == route_k) { + continue; + } + auto &cache = operator_caches.Get(route_ij, route_k); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + SdSwapTwoOne(problem, distance_matrix, solution, context, route_ij, + route_k, move, cache.delta, random); + } else { + move.route_ij = route_ij; + move.route_k = route_k; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSdSwapTwoOne(best_move, solution, context); + associated_routes.insert(best_move.route_ij); + associated_routes.insert(best_move.route_k); + return true; + } + return false; +} + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/split_reinsertion.cc b/src/algorithm/operator/split_reinsertion.cc new file mode 100644 index 0000000..1ebd685 --- /dev/null +++ b/src/algorithm/operator/split_reinsertion.cc @@ -0,0 +1,74 @@ +#include "algorithm/operators.h" + +#include + +#include "util/utils.h" + +namespace alkaid_sd { + +struct SplitReinsertionMove { + InsertionWithCost insertion; + int residual; + SplitReinsertionMove(const InsertionWithCost &insertion, int residual) + : insertion(insertion), residual(residual) {} +}; + +int SplitReinsertion(const Problem &problem, + const DistanceMatrix &distance_matrix, Node customer, + int demand, double blink_rate, Solution &solution, + RouteContext &context, std::set &associated_routes, + Random &random) { + auto func = [&](Node predecessor, Node successor, Node customer) { + Node pre_customer = solution.customer(predecessor); + Node suc_customer = solution.customer(successor); + return distance_matrix[customer][pre_customer] + + distance_matrix[customer][suc_customer] - + distance_matrix[pre_customer][suc_customer]; + }; + std::vector moves; + moves.reserve(context.num_routes); + int sum_residual = 0; + for (Node route_index = 0; route_index < context.num_routes; ++route_index) { + int residual = + std::min(demand, problem.capacity - context.route_loads[route_index]); + if (residual > 0) { + auto insertion = CalcBestInsertion(solution, func, context, route_index, + customer, random); + moves.emplace_back(insertion, residual); + sum_residual += residual; + } + } + if (sum_residual < demand) { + return -1; + } + std::stable_sort( + moves.begin(), moves.end(), + [](const SplitReinsertionMove &lhs, const SplitReinsertionMove &rhs) { + return lhs.insertion.cost.value * rhs.residual < + rhs.insertion.cost.value * lhs.residual; + }); + int delta = 0; + for (const auto &move : moves) { + sum_residual -= move.residual; + if (sum_residual >= demand && random.NextFloat() < blink_rate) { + continue; + } + Load load = std::min(demand, move.residual); + delta += move.insertion.cost.value; + Node node_index = solution.Insert( + customer, load, move.insertion.predecessor, move.insertion.successor); + if (move.insertion.predecessor == 0) { + context.heads[move.insertion.route_index] = node_index; + } + UpdateRouteContext(solution, move.insertion.route_index, + move.insertion.predecessor, context); + associated_routes.insert(move.insertion.route_index); + demand -= load; + if (demand == 0) { + break; + } + } + return delta; +} + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/swap.cc b/src/algorithm/operator/swap.cc new file mode 100644 index 0000000..1a4ccdd --- /dev/null +++ b/src/algorithm/operator/swap.cc @@ -0,0 +1,239 @@ +#include "algorithm/operators.h" + +#include "util/solution_utils.h" + +namespace alkaid_sd { + +struct SwapMove { + Node route_x, route_y; + int direction_x, direction_y; + Node left_x, left_y; + Node right_x, right_y; +}; + +inline void SegmentInsertion(Solution &solution, RouteContext &context, + Node left, Node right, Node predecessor, + Node successor, Node route_index, int direction) { + if (direction) { + ReversedLink(solution, left, right, predecessor, successor); + } else { + solution.Link(predecessor, left); + solution.Link(right, successor); + } + if (predecessor == 0) { + context.heads[route_index] = direction ? right : left; + } +} + +inline void DoSwap(SwapMove &move, Solution &solution, RouteContext &context) { + if (move.direction_y == -1) { + Node predecessor = solution.predecessor(move.left_x); + Node successor = solution.successor(move.right_x); + solution.Link(predecessor, successor); + if (predecessor == 0) { + context.heads[move.route_x] = successor; + } + SegmentInsertion(solution, context, move.left_x, move.right_x, move.left_y, + move.right_y, move.route_y, move.direction_x); + } else { + Node predecessor_x = solution.predecessor(move.left_x); + Node successor_x = solution.successor(move.right_x); + Node predecessor_y = solution.predecessor(move.left_y); + Node successor_y = solution.successor(move.right_y); + SegmentInsertion(solution, context, move.left_x, move.right_x, + predecessor_y, successor_y, move.route_y, + move.direction_x); + SegmentInsertion(solution, context, move.left_y, move.right_y, + predecessor_x, successor_x, move.route_x, + move.direction_y); + } +} + +inline void UpdateShift(const DistanceMatrix &distance_matrix, + const Solution &solution, Node route_x, Node route_y, + Node left, Node right, Node predecessor, Node successor, + Node base_x, SwapMove &best_move, + Delta &best_delta, Random &random) { + Node customer_left = solution.customer(left); + Node customer_predecessor = solution.customer(predecessor); + Node customer_right = solution.customer(right); + Node customer_successor = solution.customer(successor); + int d1 = distance_matrix[customer_left][customer_predecessor] + + distance_matrix[customer_right][customer_successor]; + int d2 = distance_matrix[customer_left][customer_successor] + + distance_matrix[customer_right][customer_predecessor]; + int direction = d1 >= d2; + int delta = base_x + (direction ? d2 : d1) - + distance_matrix[customer_predecessor][customer_successor]; + if (best_delta.Update(delta, random)) { + best_move = {route_x, route_y, direction, -1, + left, predecessor, right, successor}; + } +} + +inline void UpdateSwap(const DistanceMatrix &distance_matrix, + const Solution &solution, Node route_x, Node route_y, + Node left_x, Node right_x, Node left_y, Node right_y, + int base_x, SwapMove &best_move, Delta &best_delta, + Random &random) { + Node customer_left_x = solution.customer(left_x); + Node customer_right_x = solution.customer(right_x); + Node customer_left_y = solution.customer(left_y); + Node customer_right_y = solution.customer(right_y); + Node predecessor_x = solution.customer(solution.predecessor(left_x)); + Node successor_x = solution.customer(solution.successor(right_x)); + Node predecessor_y = solution.customer(solution.predecessor(left_y)); + Node successor_y = solution.customer(solution.successor(right_y)); + int d1 = distance_matrix[customer_left_x][predecessor_y] + + distance_matrix[customer_right_x][successor_y]; + int d2 = distance_matrix[customer_left_x][successor_y] + + distance_matrix[customer_right_x][predecessor_y]; + int d3 = distance_matrix[customer_left_y][predecessor_x] + + distance_matrix[customer_right_y][successor_x]; + int d4 = distance_matrix[customer_left_y][successor_x] + + distance_matrix[customer_right_y][predecessor_x]; + int direction_x = d1 >= d2; + int direction_y = d3 >= d4; + int delta = base_x + (direction_x ? d2 : d1) + (direction_y ? d4 : d3) - + distance_matrix[customer_left_y][predecessor_y] - + distance_matrix[customer_right_y][successor_y]; + if (best_delta.Update(delta, random)) { + best_move = {route_x, route_y, direction_x, direction_y, + left_x, left_y, right_x, right_y}; + } +} + +template +void Swap(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, Node route_x, Node route_y, + SwapMove &best_move, Delta &best_delta, Random &random) { + Node left_x = context.heads[route_x]; + Load load_x = solution.load(left_x); + Node right_x = left_x; + for (int i = 1; right_x && i < num_x; ++i) { + right_x = solution.successor(right_x); + load_x += solution.load(right_x); + } + while (right_x) { + int base_x = + -distance_matrix[solution.customer(left_x)] + [solution.customer(solution.predecessor(left_x))] - + distance_matrix[solution.customer(right_x)] + [solution.customer(solution.successor(right_x))]; + if (num_y == 0) { + base_x += distance_matrix[solution.customer(solution.predecessor(left_x))] + [solution.customer(solution.successor(right_x))]; + } + Load load_y_lower = + -problem.capacity + context.route_loads[route_y] + load_x; + if (num_y == 0) { + if (load_y_lower <= 0) { + Node predecessor = 0; + Node successor = context.heads[route_y]; + while (true) { + UpdateShift(distance_matrix, solution, route_x, route_y, left_x, + right_x, predecessor, successor, base_x, best_move, + best_delta, random); + if (!successor) { + break; + } + predecessor = successor; + successor = solution.successor(successor); + } + } + } else { + Load load_y_upper = + problem.capacity - context.route_loads[route_x] + load_x; + Node left_y = context.heads[route_y]; + Load load_y = solution.load(left_y); + Node right_y = left_y; + for (int i = 1; right_y && i < num_y; ++i) { + right_y = solution.successor(right_y); + load_y += solution.load(right_y); + } + while (right_y) { + if (load_y >= load_y_lower && load_y <= load_y_upper) { + UpdateSwap(distance_matrix, solution, route_x, route_y, left_x, + right_x, left_y, right_y, base_x, best_move, best_delta, + random); + } + load_y -= solution.load(left_y); + left_y = solution.successor(left_y); + right_y = solution.successor(right_y); + load_y += solution.load(right_y); + } + } + load_x -= solution.load(left_x); + left_x = solution.successor(left_x); + right_x = solution.successor(right_x); + load_x += solution.load(right_x); + } +} + +template +bool Swap(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SwapMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = num_x != num_y ? 0 : route_x + 1; + route_y < context.num_routes; ++route_y) { + if (num_x != num_y && route_x == route_y) { + continue; + } + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + Swap(problem, distance_matrix, solution, context, route_x, + route_y, move, cache.delta, random); + } else { + move.route_x = route_x; + move.route_y = route_y; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSwap(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +template bool Swap<1, 0>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +template bool Swap<2, 0>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +template bool Swap<1, 1>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +template bool Swap<2, 1>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +template bool Swap<2, 2>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +} // namespace alkaid_sd diff --git a/src/algorithm/operator/swap_star.cc b/src/algorithm/operator/swap_star.cc new file mode 100644 index 0000000..1c7896e --- /dev/null +++ b/src/algorithm/operator/swap_star.cc @@ -0,0 +1,130 @@ +#include "algorithm/operators.h" + +#include + +#include "algorithm/base_star.h" +#include "model/route_head_guard.h" + +namespace alkaid_sd { + +struct SwapStarMove { + Node route_x, route_y; + Node node_x, predecessor_x, successor_x; + Node node_y, predecessor_y, successor_y; +}; + +inline void DoSwapStar(SwapStarMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_x = solution.predecessor(move.node_x); + Node successor_x = solution.successor(move.node_x); + Node predecessor_y = solution.predecessor(move.node_y); + Node successor_y = solution.successor(move.node_y); + { + RouteHeadGuard guard(solution, context, move.route_x); + solution.Link(predecessor_x, successor_x); + solution.Link(move.predecessor_y, move.node_y); + solution.Link(move.node_y, move.successor_y); + } + { + RouteHeadGuard guard(solution, context, move.route_y); + solution.Link(predecessor_y, successor_y); + solution.Link(move.predecessor_x, move.node_x); + solution.Link(move.node_x, move.successor_x); + } +} + +void SwapStar(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, SwapStarMove &best_move, + Delta &best_delta, std::vector> &insertions, + Random &random) { + PreprocessInsertions(distance_matrix, solution, context, route_x, route_y, + insertions, random); + PreprocessInsertions(distance_matrix, solution, context, route_y, route_x, + insertions, random); + Node node_x = context.heads[route_x]; + while (node_x) { + auto &&insertion_x = insertions[node_x]; + Load load_x = solution.load(node_x); + Load load_y_lower = + -problem.capacity + context.route_loads[route_y] + load_x; + Load load_y_upper = + problem.capacity - context.route_loads[route_x] + load_x; + Node node_y = context.heads[route_y]; + while (node_y) { + Load load_y = solution.load(node_y); + if (load_y >= load_y_lower && load_y <= load_y_upper) { + auto &&insertion_y = insertions[node_y]; + Node predecessor_x = solution.predecessor(node_x); + Node successor_x = solution.successor(node_x); + Node predecessor_y = solution.predecessor(node_y); + Node successor_y = solution.successor(node_y); + int delta = -CalcDelta(distance_matrix, solution, node_x, predecessor_x, + successor_x) - + CalcDelta(distance_matrix, solution, node_y, predecessor_y, + successor_y); + int delta_x = CalcDelta(distance_matrix, solution, node_x, + predecessor_y, successor_y); + int delta_y = CalcDelta(distance_matrix, solution, node_y, + predecessor_x, successor_x); + auto best_insertion_x = insertion_x.FindBestWithoutNode(node_y); + if (best_insertion_x && best_insertion_x->delta.value < delta_x) { + delta_x = best_insertion_x->delta.value; + predecessor_y = best_insertion_x->predecessor; + successor_y = best_insertion_x->successor; + } + auto best_insertion_y = insertion_y.FindBestWithoutNode(node_x); + if (best_insertion_y && best_insertion_y->delta.value < delta_y) { + delta_y = best_insertion_y->delta.value; + predecessor_x = best_insertion_y->predecessor; + successor_x = best_insertion_y->successor; + } + delta += delta_x + delta_y; + if (best_delta.Update(delta, random)) { + best_move = {route_x, route_y, node_x, predecessor_y, + successor_y, node_y, predecessor_x, successor_x}; + } + } + node_y = solution.successor(node_y); + } + node_x = solution.successor(node_x); + } +} + +bool SwapStar(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + static std::vector> insertions; + SwapStarMove best_move{}; + Delta best_delta; + Node max_index = *std::max_element( + solution.node_pool(), solution.node_pool() + solution.num_nodes()); + insertions.clear(); + insertions.resize(max_index + 1); + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = route_x + 1; route_y < context.num_routes; ++route_y) { + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + SwapStar(problem, distance_matrix, solution, context, route_x, route_y, + move, cache.delta, insertions, random); + } else { + move.route_x = route_x; + move.route_y = route_y; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSwapStar(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace alkaid_sd diff --git a/src/algorithm/operators.h b/src/algorithm/operators.h new file mode 100644 index 0000000..1a55e4f --- /dev/null +++ b/src/algorithm/operators.h @@ -0,0 +1,90 @@ +#ifndef ALKAID_SD_SRC_ALGORITHM_OPERATORS_H_ +#define ALKAID_SD_SRC_ALGORITHM_OPERATORS_H_ + +#include +#include +#include + +#include + +#include "distance_matrix.h" +#include "model/operator_cache.h" +#include "model/route_context.h" +#include "model/solution.h" +#include "util/random.h" + +namespace alkaid_sd { + +using InterOperatorFunction = bool(const Problem &, const DistanceMatrix &, + Solution &, RouteContext &, std::set &, + Random &, OperatorCaches &); + +template InterOperatorFunction Swap; +InterOperatorFunction Relocate; +InterOperatorFunction SwapStar; +InterOperatorFunction Cross; +InterOperatorFunction SdSwapStar; +InterOperatorFunction SdSwapOneOne; +InterOperatorFunction SdSwapTwoOne; + +static const std::map + kInterOperatorFunctionMap = { + {"Swap<2, 0>", Swap<2, 0>}, {"Swap<2, 1>", Swap<2, 1>}, + {"Swap<2, 2>", Swap<2, 2>}, {"Relocate", Relocate}, + {"SwapStar", SwapStar}, {"Cross", Cross}, + {"SdSwapStar", SdSwapStar}, {"SdSwapOneOne", SdSwapOneOne}, + {"SdSwapTwoOne", SdSwapTwoOne}}; + +static const std::map kInterOperatorCacheMap = { + {Swap<2, 0>, true}, {Swap<2, 1>, true}, {Swap<2, 2>, true}, + {Relocate, true}, {SwapStar, true}, {Cross, true}, + {SdSwapStar, true}, {SdSwapOneOne, true}, {SdSwapTwoOne, true}, +}; + +using IntraOperatorFunction = bool(const Problem &, const DistanceMatrix &, + Node, Solution &, RouteContext &, Random &); + +IntraOperatorFunction Exchange; +template IntraOperatorFunction OrOpt; + +static const std::map + kIntraOperatorFunctionMap = {{"Exchange", Exchange}, + {"OrOpt<1>", OrOpt<1>}, + {"OrOpt<2>", OrOpt<2>}, + {"OrOpt<3>", OrOpt<3>}}; + +int SplitReinsertion(const Problem &problem, + const DistanceMatrix &distance_matrix, Node customer, + int demand, double blink_rate, Solution &solution, + RouteContext &context, std::set &associated_routes, + Random &random); + +void Repair(const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context); + +} // namespace alkaid_sd + +namespace nlohmann { +using alkaid_sd::InterOperatorFunction; +template <> struct adl_serializer> { + static void from_json(const json &j, + std::vector &functions) { + for (auto &&name : j) { + functions.emplace_back(alkaid_sd::kInterOperatorFunctionMap.at(name)); + } + } + static void to_json(json &, const std::vector &) {} +}; +using alkaid_sd::IntraOperatorFunction; +template <> struct adl_serializer> { + static void from_json(const json &j, + std::vector &functions) { + for (auto &&name : j) { + functions.emplace_back(alkaid_sd::kIntraOperatorFunctionMap.at(name)); + } + } + static void to_json(json &, const std::vector &) {} +}; +}; // namespace nlohmann + +#endif // ALKAID_SD_SRC_ALGORITHM_OPERATORS_H_ diff --git a/src/algorithm/ruin_method.cc b/src/algorithm/ruin_method.cc new file mode 100644 index 0000000..229aa95 --- /dev/null +++ b/src/algorithm/ruin_method.cc @@ -0,0 +1,125 @@ +#include "algorithm/ruin_method.h" + +#include +#include +#include +#include + +#include "util/utils.h" + +namespace alkaid_sd { + +RandomRuin::RandomRuin(std::vector num_perturb_customers) + : num_perturb_customers_(std::move(num_perturb_customers)) {} + +std::vector RandomRuin::Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) { + int num_perturb = num_perturb_customers_[random.NextInt( + 0, static_cast(num_perturb_customers_.size()) - 1)]; + std::vector customers(problem.num_customers - 1); + std::iota(customers.begin(), customers.end(), 1); + random.Shuffle(customers.begin(), customers.end()); + customers.erase(customers.begin() + num_perturb, customers.end()); + return customers; +} + +SisrsRuin::SisrsRuin(int average_customers, int max_length, double split_rate, + double preserved_probability) + : average_customers_(average_customers), max_length_(max_length), + split_rate_(split_rate), preserved_probability_(preserved_probability) {} + +std::vector SisrsRuin::Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) { + double average_length = + static_cast(problem.num_customers - 1) / context.num_routes; + double max_length = + std::min(static_cast(max_length_), average_length); + double max_strings = 4.0 * average_customers_ / (1 + max_length_) - 1; + int num_strings = static_cast(random.NextFloat() * max_strings) + 1; + int customer_seed = random.NextInt(1, problem.num_customers - 1); + std::vector node_indices(solution.node_pool(), + solution.node_pool() + solution.num_nodes()); + auto &&seed_distances = distance_matrix[customer_seed]; + std::stable_sort(node_indices.begin(), node_indices.end(), + [&](Node lhs, Node rhs) { + return seed_distances[solution.customer(lhs)] < + seed_distances[solution.customer(rhs)]; + }); + std::set visited_heads; + std::vector route; + std::vector customer_indices; + for (Node node_index : node_indices) { + if (visited_heads.size() >= num_strings) { + break; + } + int position; + int head = GetRouteHead(solution, node_index, position); + if (!visited_heads.insert(head).second) { + continue; + } + GetRoute(solution, head, route); + int route_length = static_cast(route.size()); + double max_ruin_length = + std::min(static_cast(route_length), max_length); + int ruin_length = + static_cast(random.NextFloat() * max_ruin_length) + 1; + int num_preserved = 0; + int preserved_start_position = -1; + if (ruin_length >= 2 && ruin_length < route_length && + random.NextFloat() < split_rate_) { + while (ruin_length < route_length) { + if (random.NextFloat() < preserved_probability_) { + break; + } + ++num_preserved; + ++ruin_length; + } + preserved_start_position = + random.NextInt(1, ruin_length - num_preserved - 1); + } + int min_start_position = std::max(0, position - ruin_length + 1); + int max_start_position = std::min(route_length - ruin_length, position); + int start_position = random.NextInt(min_start_position, max_start_position); + for (int j = 0; j < ruin_length; ++j) { + if (j < preserved_start_position || + j >= preserved_start_position + num_preserved) { + customer_indices.emplace_back( + solution.customer(route[start_position + j])); + } + } + } + std::sort(customer_indices.begin(), customer_indices.end()); + customer_indices.erase( + std::unique(customer_indices.begin(), customer_indices.end()), + customer_indices.end()); + random.Shuffle(customer_indices.begin(), customer_indices.end()); + return customer_indices; +} + +Node SisrsRuin::GetRouteHead(Solution &solution, Node node_index, + int &position) { + position = 0; + while (true) { + Node predecessor = solution.predecessor(node_index); + if (!predecessor) { + return node_index; + } + node_index = predecessor; + ++position; + } +} + +void SisrsRuin::GetRoute(const Solution &solution, Node head, + std::vector &route) { + route.clear(); + while (head) { + route.emplace_back(head); + head = solution.successor(head); + } +} + +} // namespace alkaid_sd diff --git a/src/algorithm/ruin_method.h b/src/algorithm/ruin_method.h new file mode 100644 index 0000000..e443cfa --- /dev/null +++ b/src/algorithm/ruin_method.h @@ -0,0 +1,85 @@ +#ifndef ALKAID_SD_SRC_ALGORITHM_RUIN_METHOD_H_ +#define ALKAID_SD_SRC_ALGORITHM_RUIN_METHOD_H_ + +#include +#include +#include + +#include + +#include "distance_matrix.h" +#include "model/route_context.h" +#include "model/solution.h" +#include "problem.h" +#include "util/random.h" + + +namespace alkaid_sd { + +class RuinMethod { +public: + virtual ~RuinMethod() = default; + virtual std::vector Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) = 0; +}; + +class RandomRuin : public RuinMethod { +public: + explicit RandomRuin(std::vector num_perturb_customers); + std::vector Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) override; + +private: + std::vector num_perturb_customers_; +}; + +class SisrsRuin : public RuinMethod { +public: + SisrsRuin(int average_customers, int max_length, double split_rate, + double preserved_probability); + std::vector Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) override; + +private: + static Node GetRouteHead(Solution &solution, Node node_index, int &position); + static void GetRoute(const Solution &solution, Node head, + std::vector &route); + int average_customers_; + int max_length_; + double split_rate_; + double preserved_probability_; +}; + +} // namespace alkaid_sd + +namespace nlohmann { +template <> struct adl_serializer> { + static void from_json(const json &j, + std::unique_ptr &ruin_method) { + if (j["type"].get() == "SISRs") { + auto average_customers = j["average_customers"].get(); + auto max_length = j["max_length"].get(); + auto split_rate = j["split_rate"].get(); + auto preserved_probability = j["preserved_probability"].get(); + ruin_method = std::make_unique( + average_customers, max_length, split_rate, preserved_probability); + } else { + auto num_perturb_customers = + j["num_perturb_customers"].get>(); + ruin_method = + std::make_unique(num_perturb_customers); + } + } + + static void to_json(json &, const std::unique_ptr &) {} +}; + +} // namespace nlohmann + +#endif // ALKAID_SD_SRC_ALGORITHM_RUIN_METHOD_H_ diff --git a/src/algorithm/solver.cc b/src/algorithm/solver.cc new file mode 100644 index 0000000..4246382 --- /dev/null +++ b/src/algorithm/solver.cc @@ -0,0 +1,268 @@ +#include "algorithm/solver.h" + +#include +#include +#include +#include +#include + +#include "algorithm/base_star.h" +#include "algorithm/construction.h" +#include "algorithm/operators.h" +#include "util/mcmf.h" +#include "util/solution_utils.h" +#include "util/submit.h" +#include "util/utils.h" + +namespace alkaid_sd { + +void IntraRouteSearch(const Problem &problem, const Config &config, + const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context, + Random &random) { + Repair(distance_matrix, route_index, solution, context); + std::vector intra_neighborhoods(config.intra_operators.size()); + std::iota(intra_neighborhoods.begin(), intra_neighborhoods.end(), 0); + while (true) { + random.Shuffle(intra_neighborhoods.begin(), intra_neighborhoods.end()); + bool improved = false; + for (Node neighborhood : intra_neighborhoods) { + improved = config.intra_operators[neighborhood]( + problem, distance_matrix, route_index, solution, context, random); + if (improved) { + break; + } + } + if (!improved) { + break; + } + } +} + +void RandomizedVariableNeighborhoodDescent( + const Problem &problem, const Config &config, + const DistanceMatrix &distance_matrix, Solution &solution, + RouteContext &context, Random &random, + std::vector> &operators_caches) { + static std::vector> routes; + std::set associated_routes; + for (auto tier = 0; tier < config.inter_operators.size(); ++tier) { + for (auto i = 0; i < config.inter_operators[tier].size(); ++i) { + operators_caches[tier][i].Init( + context, kInterOperatorCacheMap.at(config.inter_operators[tier][i])); + } + } + for (Node route_index = 0; route_index < routes.size(); ++route_index) { + bool same_route = false; + if (route_index < context.num_routes) { + same_route = true; + Node head = context.heads[route_index]; + for (Node node : routes[route_index]) { + if (head != node) { + same_route = false; + break; + } + head = solution.successor(head); + } + if (head != 0) { + same_route = false; + } + } + if (!same_route) { + for (Node i = 0; i < problem.num_customers; ++i) { + star_caches[route_index].clear(); + } + } + } + for (auto tier = 0; tier < config.inter_operators.size();) { + std::vector inter_neighborhoods(config.inter_operators[tier].size()); + std::iota(inter_neighborhoods.begin(), inter_neighborhoods.end(), 0); + random.Shuffle(inter_neighborhoods.begin(), inter_neighborhoods.end()); + bool improved = false; + for (int neighborhood : inter_neighborhoods) { + Node original_num_routes = context.num_routes; + improved = config.inter_operators[tier][neighborhood]( + problem, distance_matrix, solution, context, associated_routes, + random, operators_caches[tier][neighborhood]); + if (improved) { + std::vector heads; + for (Node route_index : associated_routes) { + Node head = context.heads[route_index]; + if (head) { + heads.emplace_back(head); + } + if (route_index < original_num_routes) { + for (auto &&tier_operator_caches : operators_caches) { + for (auto &&operator_cache : tier_operator_caches) { + operator_cache.RemoveRoute(route_index); + } + } + for (Node i = 0; i < problem.num_customers; ++i) { + star_caches[route_index].clear(); + } + } + } + Node num_routes = 0; + for (Node route_index = 0; route_index < context.num_routes; + ++route_index) { + if (!associated_routes.count(route_index)) { + MoveRouteContext(num_routes, route_index, context); + for (auto &&tier_operator_caches : operators_caches) { + for (auto &&operator_cache : tier_operator_caches) { + operator_cache.MoveRoute(num_routes, route_index); + } + } + star_caches[num_routes].swap(star_caches[route_index]); + ++num_routes; + } + } + for (Node head : heads) { + context.heads[num_routes] = head; + UpdateRouteContext(solution, num_routes, 0, context); + IntraRouteSearch(problem, config, distance_matrix, num_routes, + solution, context, random); + for (auto &&tier_operator_caches : operators_caches) { + for (auto &&operator_cache : tier_operator_caches) { + operator_cache.AddRoute(num_routes); + } + } + ++num_routes; + } + context.num_routes = num_routes; + associated_routes.clear(); + break; + } + } + if (!improved) { + tier += 1; + } else if (tier > 0) { + tier = 0; + } + } + routes.resize(context.num_routes); + for (Node route_index = 0; route_index < routes.size(); ++route_index) { + auto &route = routes[route_index]; + route.clear(); + for (Node node = context.heads[route_index]; node; + node = solution.successor(node)) { + route.push_back(node); + } + } +} + +void Perturb(const Problem &problem, const DistanceMatrix &distance_matrix, + const Config &config, Solution &solution, RouteContext &context, + Random &random) { + CalcRouteContext(solution, context); + std::vector customers = config.ruin_method->Ruin( + problem, distance_matrix, solution, context, random); + config.sorter.Sort(problem, distance_matrix, customers, random); + for (Node customer : customers) { + for (Node route_index = 0; route_index < context.num_routes; + ++route_index) { + Node node_index = context.heads[route_index]; + while (node_index) { + Node successor = solution.successor(node_index); + if (solution.customer(node_index) == customer) { + RemoveNode(distance_matrix, node_index, route_index, solution, + context); + } + node_index = successor; + } + } + } + std::set associated_routes; + for (Node customer : customers) { + SplitReinsertion(problem, distance_matrix, customer, + problem.customers[customer].demand, config.blink_rate, + solution, context, associated_routes, random); + associated_routes.clear(); + } +} + +std::unique_ptr Solve(const Config &config, const Problem &problem, + const DistanceMatrix &distance_matrix) { + Random random(config.random_seed); + auto context = std::make_unique(); + std::vector> operators_caches; + for (auto &&operators : config.inter_operators) { + operators_caches.emplace_back(operators.size()); + } + auto best_solution = std::make_unique(); + int best_objective = std::numeric_limits::max(); + auto timer = CreateTimer(config.time_limit_type); + timer->Set(config.time_limit); + const int kMaxStagnation = + std::min(5000, static_cast(problem.num_customers) * + static_cast(CalcFleetLowerBound(problem))); + int counter = 0; + while (!timer->IsTimeOut()) { + auto solution = Construct(problem, distance_matrix, random); + int objective = CalcObjective(*solution, distance_matrix); + int iter_best_objective = objective; + auto new_solution = std::make_unique(*solution); + auto acceptance_rule = config.acceptance_rule(); + int num_stagnation = 0; + while (num_stagnation < kMaxStagnation && !timer->IsTimeOut()) { + ++counter; + ++num_stagnation; + CalcRouteContext(*new_solution, *context); + for (Node i = 0; i < context->num_routes; ++i) { + IntraRouteSearch(problem, config, distance_matrix, i, *new_solution, + *context, random); + } + RandomizedVariableNeighborhoodDescent(problem, config, distance_matrix, + *new_solution, *context, random, + operators_caches); + int new_objective = CalcObjective(*new_solution, distance_matrix); + if (new_objective < iter_best_objective) { + num_stagnation = 0; + iter_best_objective = new_objective; + } + if (new_objective < best_objective) { + Submit(*timer, new_objective); + best_objective = new_objective; + *best_solution = *new_solution; + } + if (acceptance_rule->Accept(objective, new_objective, random)) { + objective = new_objective; + *solution = *new_solution; + } else { + *new_solution = *solution; + } + Perturb(problem, distance_matrix, config, *new_solution, *context, + random); + } + } + std::cout << counter << std::endl; + return best_solution; +} + +void Recover(const DistanceMatrix &distance_matrix, Solution &solution, Node i, + Node j) { + Node customer = + distance_matrix + .previous_node_indices[solution.customer(i)][solution.customer(j)]; + if (customer != -1) { + Node k = solution.Insert(customer, 0, i, j); + Recover(distance_matrix, solution, i, k); + Recover(distance_matrix, solution, k, j); + } +} + +void RestoreByFloyd(const DistanceMatrix &distance_matrix, Solution &solution) { + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + Node predecessor = 0; + while (node_index) { + Recover(distance_matrix, solution, predecessor, node_index); + predecessor = node_index; + node_index = solution.successor(node_index); + } + Recover(distance_matrix, solution, predecessor, 0); + } + } +} + +} // namespace alkaid_sd diff --git a/src/algorithm/solver.h b/src/algorithm/solver.h new file mode 100644 index 0000000..ccd1e56 --- /dev/null +++ b/src/algorithm/solver.h @@ -0,0 +1,20 @@ +#ifndef ALKAID_SD_SRC_ALGORITHM_SOLVER_H_ +#define ALKAID_SD_SRC_ALGORITHM_SOLVER_H_ + +#include +#include + +#include "config/config.h" +#include "model/solution.h" +#include "problem.h" + +namespace alkaid_sd { + +std::unique_ptr Solve(const Config &config, const Problem &problem, + const DistanceMatrix &distance_matrix); + +void RestoreByFloyd(const DistanceMatrix &distance_matrix, Solution &solution); + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_ALGORITHM_SOLVER_H_ diff --git a/src/algorithm/sorter.cc b/src/algorithm/sorter.cc new file mode 100644 index 0000000..0900b7e --- /dev/null +++ b/src/algorithm/sorter.cc @@ -0,0 +1,67 @@ +#include "algorithm/sorter.h" + +#include +#include +#include + +#include "util/utils.h" + +namespace alkaid_sd { + +void Sorter::AddSortFunction(SortFunction *sort_function, double weight) { + sum_weights_ += weight; + sort_functions_.emplace_back(sort_function, weight); +} + +void Sorter::Sort(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) const { + double r = random.NextFloat() * sum_weights_; + for (auto &&[sort_function, weight] : sort_functions_) { + r -= weight; + if (r < 0) { + sort_function(problem, distance_matrix, customers, random); + return; + } + } +} + +void SortByRandom(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) { + random.Shuffle(customers.begin(), customers.end()); +} + +void SortByDemand(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) { + std::stable_sort(customers.begin(), customers.end(), [&](Node lhs, Node rhs) { + return problem.customers[lhs].demand > problem.customers[rhs].demand; + }); +} + +void SortByFar(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) { + std::stable_sort(customers.begin(), customers.end(), [&](Node lhs, Node rhs) { + return distance_matrix[0][lhs] > distance_matrix[0][rhs]; + }); +} + +void SortByClose(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) { + std::stable_sort(customers.begin(), customers.end(), [&](Node lhs, Node rhs) { + return distance_matrix[0][lhs] < distance_matrix[0][rhs]; + }); +} + +static const std::map kSortFunctionMap = { + {"random", SortByRandom}, + {"demand", SortByDemand}, + {"far", SortByFar}, + {"close", SortByClose}}; + +} // namespace alkaid_sd + +void nlohmann::adl_serializer::from_json( + const json &j, alkaid_sd::Sorter &sorter) { + for (auto &[key, value] : j.items()) { + sorter.AddSortFunction(alkaid_sd::kSortFunctionMap.at(key), value); + } +} diff --git a/src/algorithm/sorter.h b/src/algorithm/sorter.h new file mode 100644 index 0000000..74ae4a1 --- /dev/null +++ b/src/algorithm/sorter.h @@ -0,0 +1,41 @@ +#ifndef ALKAID_SD_SRC_ALGORITHM_SORTER_H_ +#define ALKAID_SD_SRC_ALGORITHM_SORTER_H_ + +#include +#include +#include + +#include + +#include "distance_matrix.h" +#include "problem.h" +#include "util/random.h" + +namespace alkaid_sd { + +using SortFunction = void(const Problem &, const DistanceMatrix &, + std::vector &, Random &); + +class Sorter { +public: + void AddSortFunction(SortFunction *sort_function, double weight); + void Sort(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) const; + +private: + double sum_weights_ = 0; + std::vector> sort_functions_; +}; + +} // namespace alkaid_sd + +namespace nlohmann { + +template <> struct adl_serializer { + static void from_json(const json &j, alkaid_sd::Sorter &sorter); + static void to_json(json &, const alkaid_sd::Sorter &) {} +}; + +} // namespace nlohmann + +#endif // ALKAID_SD_SRC_ALGORITHM_SORTER_H_ diff --git a/src/config/config.cc b/src/config/config.cc new file mode 100644 index 0000000..14fdf9e --- /dev/null +++ b/src/config/config.cc @@ -0,0 +1,37 @@ +#include "config/config.h" + +#include +#include + +namespace alkaid_sd { + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Config, problem_dir, random_seed, processor, + time_limit, time_limit_type, acceptance_rule, + ruin_method, sorter, blink_rate, + inter_operators, intra_operators) + +Config ReadConfigFromFile(const std::string &config_path, + const nlohmann::json &default_value, + const nlohmann::json &overwrite_value) { + std::ifstream ifs(config_path); + if (ifs.fail()) { + throw std::invalid_argument("Cannot open config."); + } + nlohmann::json j; + ifs >> j; + j.insert(default_value.begin(), default_value.end()); + if (!overwrite_value.empty()) { + j.update(overwrite_value); + } + Config config{}; + from_json(j, config); + return config; +} + +std::string ConvertConfigToString(const Config &config) { + nlohmann::json j; + to_json(j, config); + return j.dump(); +} + +} // namespace alkaid_sd diff --git a/src/config/config.h b/src/config/config.h new file mode 100644 index 0000000..9ade47a --- /dev/null +++ b/src/config/config.h @@ -0,0 +1,41 @@ +#ifndef ALKAID_SD_SRC_CONFIG_CONFIG_H_ +#define ALKAID_SD_SRC_CONFIG_CONFIG_H_ + +#include +#include +#include +#include + +#include + +#include "algorithm/operators.h" +#include "algorithm/ruin_method.h" +#include "algorithm/sorter.h" +#include "util/acceptance_rule.h" +#include "util/timer.h" + +namespace alkaid_sd { + +struct Config { + std::string problem_dir; + uint32_t random_seed; + std::string processor; + double time_limit; + TimerType time_limit_type; + std::function>()> acceptance_rule; + std::unique_ptr ruin_method; + Sorter sorter; + double blink_rate; + std::vector> inter_operators; + std::vector intra_operators; +}; + +Config ReadConfigFromFile(const std::string &config_path, + const nlohmann::json &default_value, + const nlohmann::json &overwrite_value); + +std::string ConvertConfigToString(const Config &config); + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_CONFIG_CONFIG_H_ diff --git a/src/distance_matrix.cc b/src/distance_matrix.cc new file mode 100644 index 0000000..e0e0b0f --- /dev/null +++ b/src/distance_matrix.cc @@ -0,0 +1,33 @@ +#include "distance_matrix.h" + +#include + +namespace alkaid_sd { + +std::unique_ptr CalcDistanceMatrix(const Problem &problem) { + auto distance_matrix = std::make_unique(); + for (Node i = 0; i < problem.num_customers; ++i) { + for (Node j = 0; j < problem.num_customers; ++j) { + const Customer &task_i = problem.customers[i]; + const Customer &task_j = problem.customers[j]; + (*distance_matrix)[i][j] = + lround(hypot(task_i.x - task_j.x, task_i.y - task_j.y)); + distance_matrix->original[i][j] = (*distance_matrix)[i][j]; + distance_matrix->previous_node_indices[i][j] = -1; + } + } + for (Node k = 1; k < problem.num_customers; ++k) { + for (Node i = 0; i < problem.num_customers; ++i) { + for (Node j = 0; j < problem.num_customers; ++j) { + int distance = (*distance_matrix)[i][k] + (*distance_matrix)[k][j]; + if ((*distance_matrix)[i][j] > distance) { + (*distance_matrix)[i][j] = distance; + distance_matrix->previous_node_indices[i][j] = k; + } + } + } + } + return distance_matrix; +} + +} // namespace alkaid_sd diff --git a/src/distance_matrix.h b/src/distance_matrix.h new file mode 100644 index 0000000..8ff4fd4 --- /dev/null +++ b/src/distance_matrix.h @@ -0,0 +1,23 @@ +#ifndef ALKAID_SD_SRC_DISTANCE_MATRIX_H_ +#define ALKAID_SD_SRC_DISTANCE_MATRIX_H_ + +#include +#include + +#include "problem.h" + +namespace alkaid_sd { + +template +using Matrix = std::array, kMaxNumCustomers>; + +struct DistanceMatrix : Matrix { + Matrix original; + Matrix previous_node_indices; +}; + +std::unique_ptr CalcDistanceMatrix(const Problem &problem); + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_DISTANCE_MATRIX_H_ diff --git a/src/logo.h b/src/logo.h new file mode 100644 index 0000000..f02f392 --- /dev/null +++ b/src/logo.h @@ -0,0 +1,16 @@ +#ifndef ALKAID_SD_SRC_LOGO_H_ +#define ALKAID_SD_SRC_LOGO_H_ + +namespace alkaid_sd { + +const char *kLogo = R"( + _ _ _ _ _ ____ ____ + / \ | | | ____ _(_) __| / ___|| _ \ + / _ \ | | |/ / _` | |/ _` \___ \| | | | + / ___ \| | < (_| | | (_| |___) | |_| | + /_/ \_\_|_|\_\__,_|_|\__,_|____/|____/ +)"; + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_LOGO_H_ diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..e34fa98 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,52 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "algorithm/solver.h" +#include "logo.h" +#include "problem.h" +#include "util/solution_utils.h" + +int main(int argc, char **argv) { + auto start_time = std::chrono::system_clock::now(); + CLI::App app{ + std::string{alkaid_sd::kLogo} + + "A program used to participate in the sdvrp track of DIMACS-12 " + "competition\n(http://dimacs.rutgers.edu/programs/challenge/vrp/vrpsd/)"}; + std::string config_path; + app.add_option("config", config_path, "Config path")->required(); + std::string instance_name; + app.add_option("instance", instance_name, "Instance name")->required(); + std::string output; + app.add_option("output", output, "Output")->required(); + std::optional random_seed; + app.add_option("-s", random_seed, "Random Seed"); + CLI11_PARSE(app, argc, argv); + nlohmann::json default_value = {{"random_seed", std::random_device{}()}}; + nlohmann::json overwrite_value; + if (random_seed) { + overwrite_value["random_seed"] = random_seed.value(); + } + auto config = alkaid_sd::ReadConfigFromFile(config_path, default_value, + overwrite_value); + auto problem_path = + alkaid_sd::GetProblemPath(config.problem_dir, instance_name); + auto problem = alkaid_sd::ReadProblemFromFile(problem_path); + auto distance_matrix = alkaid_sd::CalcDistanceMatrix(problem); + auto solution = alkaid_sd::Solve(config, problem, *distance_matrix); + auto new_solution = *solution; + alkaid_sd::RestoreByFloyd(*distance_matrix, *solution); + auto end_time = std::chrono::system_clock::now(); + auto time = std::chrono::duration_cast>( + end_time - start_time) + .count(); + std::ofstream ofs(output); + WriteSolutionToStream(*distance_matrix, *solution, config.processor, time, + ofs); + return 0; +} diff --git a/src/model/direction.h b/src/model/direction.h new file mode 100644 index 0000000..22db5c4 --- /dev/null +++ b/src/model/direction.h @@ -0,0 +1,40 @@ +#ifndef ALKAID_SD_SRC_MODEL_DIRECTION_H_ +#define ALKAID_SD_SRC_MODEL_DIRECTION_H_ + +namespace alkaid_sd { + +struct Forward { + static constexpr bool kDirection = false; + static Node Head(const RouteContext &context, Node node_index) { + return context.heads[node_index]; + } + static Node Predecessor(const Solution &solution, Node node_index) { + return solution.predecessor(node_index); + } + static Node Successor(const Solution &solution, Node node_index) { + return solution.successor(node_index); + } + static void Link(Node predecessor, Node successor, Solution &solution) { + solution.Link(predecessor, successor); + } +}; + +struct Backward { + static constexpr bool kDirection = true; + static Node Head(const RouteContext &context, Node node_index) { + return context.tails[node_index]; + } + static Node Predecessor(const Solution &solution, Node node_index) { + return solution.successor(node_index); + } + static Node Successor(const Solution &solution, Node node_index) { + return solution.predecessor(node_index); + } + static void Link(Node predecessor, Node successor, Solution &solution) { + solution.Link(successor, predecessor); + } +}; + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_MODEL_DIRECTION_H_ diff --git a/src/model/operator_cache.h b/src/model/operator_cache.h new file mode 100644 index 0000000..f58f5d6 --- /dev/null +++ b/src/model/operator_cache.h @@ -0,0 +1,100 @@ +#ifndef ALKAID_SD_SRC_MODEL_OPERATOR_CACHE_H_ +#define ALKAID_SD_SRC_MODEL_OPERATOR_CACHE_H_ + +#include +#include +#include +#include + +#include "model/route_context.h" +#include "util/delta.h" + +namespace alkaid_sd { + +struct BaseCache { + bool invalidated = true; + Delta delta; + unsigned char move[24]; + + inline bool TryReuse() { + // if (!invalidated) { + // return true; + // } + invalidated = false; + delta = Delta(); + return false; + } +}; + +class OperatorCaches + : std::array, kMaxNumRoutes> { +public: + void Init(const RouteContext &context, bool need) { + need_ = need; + if (!need) { + return; + } + max_index_ = context.num_routes; + route_pool_.clear(); + unused_indices_.clear(); + for (Node i = 0; i < context.num_routes; ++i) { + route_index_mappings_[i] = i; + route_pool_.emplace_back(i); + for (Node j = 0; j < context.num_routes; ++j) { + (*this)[i][j].invalidated = true; + } + } + } + + void AddRoute(Node route_index) { + if (!need_) { + return; + } + Node index; + if (unused_indices_.empty()) { + index = max_index_++; + } else { + index = unused_indices_.back(); + unused_indices_.pop_back(); + } + route_index_mappings_[route_index] = index; + route_pool_.emplace_back(index); + for (Node other : route_pool_) { + (*this)[index][other].invalidated = true; + (*this)[other][index].invalidated = true; + } + } + + void RemoveRoute(Node route_index) { + if (!need_) { + return; + } + Node index = route_index_mappings_[route_index]; + route_pool_.erase(std::find(route_pool_.begin(), route_pool_.end(), index)); + unused_indices_.emplace_back(index); + } + + void MoveRoute(Node dest_route_index, Node src_route_index) { + if (!need_) { + return; + } + route_index_mappings_[dest_route_index] = + route_index_mappings_[src_route_index]; + } + + BaseCache &Get(Node route_a, Node route_b) { + return ( + *this)[route_index_mappings_[route_a]][route_index_mappings_[route_b]]; + } + +private: + bool need_; + Node route_index_mappings_[kMaxNumRoutes]; + std::vector route_pool_; + std::vector unused_indices_; + Node max_index_; +}; + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_MODEL_OPERATOR_CACHE_H_ diff --git a/src/model/route_context.cc b/src/model/route_context.cc new file mode 100644 index 0000000..79643ca --- /dev/null +++ b/src/model/route_context.cc @@ -0,0 +1,47 @@ +#include "model/route_context.h" + +namespace alkaid_sd { + +void GetRoutes(const Solution &solution, Node &num_routes, Node *heads) { + num_routes = 0; + const Node *node_pool = solution.node_pool(); + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = node_pool[i]; + if (solution.predecessor(node_index) == 0) { + heads[num_routes++] = node_index; + } + } +} + +void CalcRouteContext(const Solution &solution, RouteContext &route_context) { + GetRoutes(solution, route_context.num_routes, route_context.heads); + route_context.loads[0] = 0; + for (Node i = 0; i < route_context.num_routes; ++i) { + UpdateRouteContext(solution, i, 0, route_context); + } +} + +void UpdateRouteContext(const Solution &solution, Node route_index, + Node predecessor, RouteContext &route_context) { + Load load = route_context.loads[predecessor]; + Node node_index = predecessor ? solution.successor(predecessor) + : route_context.heads[route_index]; + while (node_index) { + load += solution.load(node_index); + route_context.loads[node_index] = load; + predecessor = node_index; + node_index = solution.successor(node_index); + } + route_context.tails[route_index] = predecessor; + route_context.route_loads[route_index] = load; +} + +void MoveRouteContext(Node dest_route_index, Node src_route_index, + RouteContext &route_context) { + route_context.heads[dest_route_index] = route_context.heads[src_route_index]; + route_context.tails[dest_route_index] = route_context.tails[src_route_index]; + route_context.route_loads[dest_route_index] = + route_context.route_loads[src_route_index]; +} + +} // namespace alkaid_sd diff --git a/src/model/route_context.h b/src/model/route_context.h new file mode 100644 index 0000000..a68e30e --- /dev/null +++ b/src/model/route_context.h @@ -0,0 +1,30 @@ +#ifndef ALKAID_SD_SRC_MODEL_ROUTE_CONTEXT_H_ +#define ALKAID_SD_SRC_MODEL_ROUTE_CONTEXT_H_ + +#include "model/solution.h" +#include "problem.h" + + +namespace alkaid_sd { + +struct RouteContext { + Node num_routes; + Node heads[kMaxNumRoutes]; + Node tails[kMaxNumRoutes]; + int route_loads[kMaxNumRoutes]; + Load loads[kMaxNumNodes]; +}; + +void GetRoutes(const Solution &solution, Node &num_routes, Node *heads); + +void CalcRouteContext(const Solution &solution, RouteContext &route_context); + +void UpdateRouteContext(const Solution &solution, Node route_index, + Node predecessor, RouteContext &route_context); + +void MoveRouteContext(Node dest_route_index, Node src_route_index, + RouteContext &route_context); + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_MODEL_ROUTE_CONTEXT_H_ diff --git a/src/model/route_head_guard.h b/src/model/route_head_guard.h new file mode 100644 index 0000000..29b2eb0 --- /dev/null +++ b/src/model/route_head_guard.h @@ -0,0 +1,22 @@ +#ifndef ALKAID_SD_SRC_MODEL_ROUTE_HEAD_GUARD_H_ +#define ALKAID_SD_SRC_MODEL_ROUTE_HEAD_GUARD_H_ + +#include "model/route_context.h" +#include "model/solution.h" + +namespace alkaid_sd { + +struct RouteHeadGuard { + Solution &solution; + RouteContext &context; + Node route_index; + RouteHeadGuard(Solution &solution, RouteContext &context, Node route_index) + : solution(solution), context(context), route_index(route_index) { + solution.set_successor(0, context.heads[route_index]); + } + ~RouteHeadGuard() { context.heads[route_index] = solution.successor(0); } +}; + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_MODEL_ROUTE_HEAD_GUARD_H_ diff --git a/src/model/solution.h b/src/model/solution.h new file mode 100644 index 0000000..6038331 --- /dev/null +++ b/src/model/solution.h @@ -0,0 +1,131 @@ +#ifndef ALKAID_SD_SRC_MODEL_SOLUTION_H_ +#define ALKAID_SD_SRC_MODEL_SOLUTION_H_ + +#include +#include +#include + +#include "problem.h" + +namespace alkaid_sd { + +class Solution { +public: + Solution(); + [[nodiscard]] Node predecessor(Node node_index) const; + [[nodiscard]] Node successor(Node node_index) const; + [[nodiscard]] Node customer(Node node_index) const; + [[nodiscard]] Load load(Node node_index) const; + [[nodiscard]] Node num_nodes() const; + [[nodiscard]] const Node *node_pool() const; + void set_predecessor(Node node_index, Node predecessor); + void set_successor(Node node_index, Node successor); + void set_customer(Node node_index, Node customer); + void set_load(Node node_index, Load load); + void Remove(Node node_index); + Node Insert(Node customer, Load load, Node predecessor, Node successor); + Node NewNode(Node customer, Load load); + void Link(Node predecessor, Node successor); + +private: + struct NodeImpl { + Node successor; + Node predecessor; + Node customer; + Load load; + }; + NodeImpl node_impls_[kMaxNumNodes]{}; + // Node predecessors_[kMaxNumNodes]{}; + // Node successors_[kMaxNumNodes]{}; + // Node customers_[kMaxNumNodes]{}; + // Load loads_[kMaxNumNodes]{}; + Node node_pool_[kMaxNumNodes]{}; + Node pool_indices_[kMaxNumNodes]{}; + Node num_nodes_; +}; + +inline Solution::Solution() { + set_load(0, 0); + set_customer(0, 0); + num_nodes_ = 0; + std::iota(node_pool_, node_pool_ + kMaxNumNodes, 1); +} + +inline Node Solution::predecessor(Node node_index) const { + // return predecessors_[node_index]; + return node_impls_[node_index].predecessor; +} + +inline Node Solution::successor(Node node_index) const { + // return successors_[node_index]; + return node_impls_[node_index].successor; +} + +inline Node Solution::customer(Node node_index) const { + // return customers_[node_index]; + return node_impls_[node_index].customer; +} + +inline Load Solution::load(Node node_index) const { + // return loads_[node_index]; + return node_impls_[node_index].load; +} + +inline Node Solution::num_nodes() const { return num_nodes_; } + +inline const Node *Solution::node_pool() const { return node_pool_; } + +inline void Solution::set_predecessor(Node node_index, Node predecessor) { + // predecessors_[node_index] = predecessor; + node_impls_[node_index].predecessor = predecessor; +} + +inline void Solution::set_successor(Node node_index, Node successor) { + // successors_[node_index] = successor; + node_impls_[node_index].successor = successor; +} + +inline void Solution::set_customer(Node node_index, Node customer) { + // customers_[node_index] = customer; + node_impls_[node_index].customer = customer; +} + +inline void Solution::set_load(Node node_index, Load load) { + // loads_[node_index] = load; + node_impls_[node_index].load = load; +} + +inline void Solution::Remove(Node node_index) { + Node predecessor = this->predecessor(node_index); + Node successor = this->successor(node_index); + Link(predecessor, successor); + Node pool_index = pool_indices_[node_index]; + pool_indices_[node_pool_[num_nodes_ - 1]] = pool_index; + std::swap(node_pool_[pool_index], node_pool_[num_nodes_ - 1]); + --num_nodes_; +} + +inline Node Solution::Insert(Node customer, Load load, Node predecessor, + Node successor) { + Node node_index = NewNode(customer, load); + Link(predecessor, node_index); + Link(node_index, successor); + return node_index; +} + +inline Node Solution::NewNode(Node customer, Load load) { + Node node_index = node_pool_[num_nodes_]; + pool_indices_[node_index] = num_nodes_++; + set_customer(node_index, customer); + set_load(node_index, load); + return node_index; +} + +inline void Solution::Link(Node predecessor, Node successor) { + set_predecessor(successor, predecessor); + set_successor(predecessor, successor); +} + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_MODEL_SOLUTION_H_ diff --git a/src/problem.cc b/src/problem.cc new file mode 100644 index 0000000..8a97e2f --- /dev/null +++ b/src/problem.cc @@ -0,0 +1,45 @@ +#include "problem.h" + +#include +#include + +namespace alkaid_sd { + +Problem ReadProblemFromFile(const std::string &problem_path) { + std::ifstream ifs(problem_path); + if (ifs.fail()) { + throw std::invalid_argument("Cannot open problem."); + } + Problem problem{}; + ifs >> problem.num_customers >> problem.capacity; + ++problem.num_customers; + problem.customers[0].demand = 0; + for (Node i = 1; i < problem.num_customers; ++i) { + ifs >> problem.customers[i].demand; + } + for (Node i = 0; i < problem.num_customers; ++i) { + ifs >> problem.customers[i].x >> problem.customers[i].y; + } + return problem; +} + +std::string GetProblemPath(const std::string &problem_dir, + const std::string &instance_name) { + if (instance_name.empty()) { + throw std::invalid_argument("Invalid instance name."); + } + std::stringstream ss; + ss << problem_dir; + if (instance_name.rfind("SD", 0) == 0) { + ss << "SET-1/" << instance_name << ".txt"; + } else if (instance_name[0] == 'S') { + ss << "SET-2/" << instance_name << ".sd"; + } else if (instance_name[0] == 'p') { + ss << "SET-3/" << instance_name << ".cri"; + } else { + ss << "SET-4/" << instance_name << ".sd"; + } + return ss.str(); +} + +} // namespace alkaid_sd diff --git a/src/problem.h b/src/problem.h new file mode 100644 index 0000000..9aeb423 --- /dev/null +++ b/src/problem.h @@ -0,0 +1,33 @@ +#ifndef ALKAID_SD_SRC_PROBLEM_H_ +#define ALKAID_SD_SRC_PROBLEM_H_ + +#include + +namespace alkaid_sd { + +using Node = short; +using Load = short; + +constexpr auto kMaxNumCustomers = 289; +constexpr auto kMaxNumRoutes = kMaxNumCustomers - 1; +const Node kMaxNumNodes = kMaxNumCustomers * 3; + +struct Customer { + int x, y; + Load demand; +}; + +struct Problem { + Load capacity; + Node num_customers; + Customer customers[kMaxNumCustomers]; +}; + +Problem ReadProblemFromFile(const std::string &problem_path); + +std::string GetProblemPath(const std::string &problem_dir, + const std::string &instance_name); + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_PROBLEM_H_ diff --git a/src/sdvrp/distance_matrix.cc b/src/sdvrp/distance_matrix.cc new file mode 100644 index 0000000..1f3efb4 --- /dev/null +++ b/src/sdvrp/distance_matrix.cc @@ -0,0 +1,33 @@ +#include "sdvrp/distance_matrix.h" + +#include + +namespace vrp::sdvrp { + +std::unique_ptr CalcDistanceMatrix(const Problem &problem) { + auto distance_matrix = std::make_unique(); + for (Node i = 0; i < problem.num_customers; ++i) { + for (Node j = 0; j < problem.num_customers; ++j) { + const Customer &task_i = problem.customers[i]; + const Customer &task_j = problem.customers[j]; + (*distance_matrix)[i][j] = + lround(hypot(task_i.x - task_j.x, task_i.y - task_j.y)); + distance_matrix->original[i][j] = (*distance_matrix)[i][j]; + distance_matrix->previous_node_indices[i][j] = -1; + } + } + for (Node k = 1; k < problem.num_customers; ++k) { + for (Node i = 0; i < problem.num_customers; ++i) { + for (Node j = 0; j < problem.num_customers; ++j) { + int distance = (*distance_matrix)[i][k] + (*distance_matrix)[k][j]; + if ((*distance_matrix)[i][j] > distance) { + (*distance_matrix)[i][j] = distance; + distance_matrix->previous_node_indices[i][j] = k; + } + } + } + } + return distance_matrix; +} + +} // namespace vrp::sdvrp diff --git a/src/sdvrp/distance_matrix.h b/src/sdvrp/distance_matrix.h new file mode 100644 index 0000000..488c67f --- /dev/null +++ b/src/sdvrp/distance_matrix.h @@ -0,0 +1,24 @@ +#ifndef DIMACS_12_SRC_SDVRP_DISTANCE_MATRIX_H_ +#define DIMACS_12_SRC_SDVRP_DISTANCE_MATRIX_H_ + +#include +#include + +#include "sdvrp/limit.h" +#include "sdvrp/problem.h" + +namespace vrp::sdvrp { + +template +using Matrix = std::array, kMaxNumCustomers>; + +struct DistanceMatrix : Matrix { + Matrix original; + Matrix previous_node_indices; +}; + +std::unique_ptr CalcDistanceMatrix(const Problem &problem); + +} // namespace vrp::sdvrp + +#endif // DIMACS_12_SRC_SDVRP_DISTANCE_MATRIX_H_ diff --git a/src/sdvrp/limit.h b/src/sdvrp/limit.h new file mode 100644 index 0000000..b0de59c --- /dev/null +++ b/src/sdvrp/limit.h @@ -0,0 +1,11 @@ +#ifndef DIMACS_12_SRC_SDVRP_LIMIT_H_ +#define DIMACS_12_SRC_SDVRP_LIMIT_H_ + +namespace vrp::sdvrp { + +constexpr auto kMaxNumCustomers = 289; +constexpr auto kMaxNumRoutes = kMaxNumCustomers - 1; + +} // namespace vrp::sdvrp + +#endif // DIMACS_12_SRC_SDVRP_LIMIT_H_ diff --git a/src/sdvrp/logo.h b/src/sdvrp/logo.h new file mode 100644 index 0000000..adc16bd --- /dev/null +++ b/src/sdvrp/logo.h @@ -0,0 +1,17 @@ +#ifndef DIMACS_12_SRC_SDVRP_LOGO_H_ +#define DIMACS_12_SRC_SDVRP_LOGO_H_ + +namespace vrp::sdvrp { + +const char *kLogo = R"( + .___ + ______ __| _/___ _________ ______ + / ___/ / __ | \ \/ /\_ __ \\____ \ + \___ \ / /_/ | \ / | | \/| |_> > +/____ >\____ | \_/ |__| | __/ + \/ \/ |__| +)"; + +} // namespace vrp::sdvrp + +#endif // DIMACS_12_SRC_SDVRP_LOGO_H_ diff --git a/src/sdvrp/problem.cc b/src/sdvrp/problem.cc new file mode 100644 index 0000000..ceba8f8 --- /dev/null +++ b/src/sdvrp/problem.cc @@ -0,0 +1,45 @@ +#include "sdvrp/problem.h" + +#include +#include + +namespace vrp::sdvrp { + +Problem ReadProblemFromFile(const std::string &problem_path) { + std::ifstream ifs(problem_path); + if (ifs.fail()) { + throw std::invalid_argument("Cannot open problem."); + } + Problem problem{}; + ifs >> problem.num_customers >> problem.capacity; + ++problem.num_customers; + problem.customers[0].demand = 0; + for (Node i = 1; i < problem.num_customers; ++i) { + ifs >> problem.customers[i].demand; + } + for (Node i = 0; i < problem.num_customers; ++i) { + ifs >> problem.customers[i].x >> problem.customers[i].y; + } + return problem; +} + +std::string GetProblemPath(const std::string &problem_dir, + const std::string &instance_name) { + if (instance_name.empty()) { + throw std::invalid_argument("Invalid instance name."); + } + std::stringstream ss; + ss << problem_dir; + if (instance_name.rfind("SD", 0) == 0) { + ss << "SET-1/" << instance_name << ".txt"; + } else if (instance_name[0] == 'S') { + ss << "SET-2/" << instance_name << ".sd"; + } else if (instance_name[0] == 'p') { + ss << "SET-3/" << instance_name << ".cri"; + } else { + ss << "SET-4/" << instance_name << ".sd"; + } + return ss.str(); +} + +} // namespace vrp::sdvrp diff --git a/src/sdvrp/problem.h b/src/sdvrp/problem.h new file mode 100644 index 0000000..aee384f --- /dev/null +++ b/src/sdvrp/problem.h @@ -0,0 +1,31 @@ +#ifndef DIMACS_12_SRC_SDVRP_PROBLEM_H_ +#define DIMACS_12_SRC_SDVRP_PROBLEM_H_ + +#include + +#include "sdvrp/limit.h" + +namespace vrp::sdvrp { + +using Node = short; +using Load = short; + +struct Customer { + int x, y; + Load demand; +}; + +struct Problem { + Load capacity; + Node num_customers; + Customer customers[kMaxNumCustomers]; +}; + +Problem ReadProblemFromFile(const std::string &problem_path); + +std::string GetProblemPath(const std::string &problem_dir, + const std::string &instance_name); + +} // namespace vrp::sdvrp + +#endif // DIMACS_12_SRC_SDVRP_PROBLEM_H_ diff --git a/src/sdvrp/splitils/algorithm/base_star.h b/src/sdvrp/splitils/algorithm/base_star.h new file mode 100644 index 0000000..714a9d8 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/base_star.h @@ -0,0 +1,229 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_BASE_STAR_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_BASE_STAR_H_ + +#include "sdvrp/splitils/algorithm/operators.h" + +namespace vrp::sdvrp::splitils { + +struct Insertion { + Delta delta; + Node predecessor{}, successor{}; +}; + +template struct BestInsertion { + Insertion insertions[num]; + + inline void Reset() { + for (auto &insertion : insertions) { + insertion.delta = Delta(std::numeric_limits::max(), -1); + } + } + + inline void Add(int delta, Node predecessor, Node successor, Random &random) { + for (int i = 0; i < num; ++i) { + if (insertions[i].delta.value == std::numeric_limits::max()) { + insertions[i] = {{delta, 1}, predecessor, successor}; + return; + } else if (delta < insertions[i].delta.value) { + for (int j = num - 1; j > i; --j) { + insertions[j] = insertions[j - 1]; + } + insertions[i] = {{delta, 1}, predecessor, successor}; + return; + } else if (delta == insertions[i].delta.value && + insertions[i].delta.counter != -1) { + if (random.NextInt(1, insertions[i].delta.counter + 1) == 1) { + for (int j = num - 1; j > i; --j) { + insertions[j] = insertions[j - 1]; + } + ++insertions[i].delta.counter; + insertions[i].predecessor = predecessor; + insertions[i].successor = successor; + break; + } else { + ++insertions[i].delta.counter; + } + } + } + } + + inline void Add(const Insertion &insertion, Random &random) { + for (int i = 0; i < num; ++i) { + if (insertion.delta.value < insertions[i].delta.value) { + for (int j = num - 1; j > i; --j) { + insertions[j] = insertions[j - 1]; + } + insertions[i] = insertion; + return; + } else if (insertion.delta.value == insertions[i].delta.value && + insertions[i].delta.counter != -1) { + if (random.NextInt(1, insertions[i].delta.counter + + insertion.delta.counter) == 1) { + for (int j = num - 1; j > i; --j) { + insertions[j] = insertions[j - 1]; + } + insertions[i].delta.counter += insertion.delta.counter; + insertions[i].predecessor = insertion.predecessor; + insertions[i].successor = insertion.successor; + break; + } else { + insertions[i].delta.counter += insertion.delta.counter; + } + } + } + } + + [[nodiscard]] inline const Insertion *FindBest() const { return insertions; } + + [[nodiscard]] inline const Insertion * + FindBestWithoutNode(Node node_index) const { + for (auto &insertion : insertions) { + if (insertion.delta.counter > 0 && insertion.predecessor != node_index && + insertion.successor != node_index) { + return &insertion; + } + } + return nullptr; + } + + [[nodiscard]] inline const Insertion * + FindBestWithoutRoute(Node route_index) const { + for (auto &insertion : insertions) { + if (insertion.delta.counter > 0 && insertion.route_index != route_index) { + return &insertion; + } + } + return nullptr; + } + + template + [[nodiscard]] inline const Insertion *FindBest(Predicate &&predicate) const { + for (auto &insertion : insertions) { + if (insertion.delta.counter > 0 && predicate(insertion)) { + return &insertion; + } + } + return nullptr; + } +}; + +inline int CalcDelta(const DistanceMatrix &distance_matrix, + const Solution &solution, Node node_index, + Node predecessor, Node successor) { + return distance_matrix[solution.customer(node_index)] + [solution.customer(predecessor)] + + distance_matrix[solution.customer(node_index)] + [solution.customer(successor)] - + distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)]; +} + +template +void PreprocessInsertions(const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, + std::vector> &insertions, + Random &random) { + Node node_x = context.heads[route_x]; + while (node_x) { + auto &insertion = insertions[node_x]; + insertion.Reset(); + Node predecessor = 0; + Node successor = context.heads[route_y]; + while (true) { + int delta = + CalcDelta(distance_matrix, solution, node_x, predecessor, successor); + insertion.Add(delta, predecessor, successor, random); + if (!successor) { + break; + } + predecessor = successor; + successor = solution.successor(successor); + } + node_x = solution.successor(node_x); + } +} + +inline std::vector>> star_caches(kMaxNumRoutes); + +inline void PreprocessStarInsertions(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, + const RouteContext &context, Node route, + Random &random) { + auto &&insertions = star_caches[route]; + if (!insertions.empty()) { + return; + } + insertions.resize(problem.num_customers); + for (Node customer = 1; customer < problem.num_customers; ++customer) { + insertions[customer].Reset(); + } + Node predecessor = 0; + Node successor = context.heads[route]; + while (true) { + Node predecessor_customer = solution.customer(predecessor); + Node successor_customer = solution.customer(successor); + auto &&predecessor_distances = distance_matrix[predecessor_customer]; + auto &&successor_distances = distance_matrix[successor_customer]; + auto distance = distance_matrix[predecessor_customer][successor_customer]; + for (Node customer = 1; customer < problem.num_customers; ++customer) { + int delta = predecessor_distances[customer] + + successor_distances[customer] - distance; + insertions[customer].Add(delta, predecessor, successor, random); + } + if (!successor) { + break; + } + predecessor = successor; + successor = solution.successor(successor); + } +} + +template +void PreprocessRouteInsertions(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, + const RouteContext &context, + std::vector> &insertions, + Random &random) { + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + Node node_x = context.heads[route_x]; + while (node_x) { + auto &insertion = insertions[node_x]; + insertion.Reset(); + Load load = solution.load(node_x); + for (Node route_y = 0; route_y < context.num_routes; ++route_y) { + if (route_x == route_y || + context.route_loads[route_y] + load > problem.capacity) { + continue; + } + Node predecessor = 0; + Node successor = context.heads[route_y]; + Insertion best_insertion; + best_insertion.delta = Delta(std::numeric_limits::max(), -1); + while (true) { + int delta = CalcDelta(distance_matrix, solution, node_x, predecessor, + successor); + if (best_insertion.delta.Update(delta, random)) { + best_insertion.predecessor = predecessor; + best_insertion.successor = successor; + } + if (!successor) { + break; + } + predecessor = successor; + successor = solution.successor(successor); + } + if (best_insertion.delta.counter > 0) { + insertion.Add(best_insertion, random); + } + } + node_x = solution.successor(node_x); + } + } +} + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_BASE_STAR_H_ diff --git a/src/sdvrp/splitils/algorithm/construction.cc b/src/sdvrp/splitils/algorithm/construction.cc new file mode 100644 index 0000000..aa55a3c --- /dev/null +++ b/src/sdvrp/splitils/algorithm/construction.cc @@ -0,0 +1,203 @@ +#include "sdvrp/splitils/algorithm/construction.h" + +#include +#include +#include + +#include "sdvrp/splitils/model/route_context.h" +#include "sdvrp/splitils/util/utils.h" + +namespace vrp::sdvrp::splitils { + +enum InsertionCriterion { kMcfic, kNfic }; + +enum InsertionStrategies { kSis, kPis }; + +using CandidateList = std::vector; + +int AddRoute(const Problem &problem, CandidateList &candidate_list, + Random &random, Solution &solution, RouteContext &context) { + int position = random.NextInt(0, static_cast(candidate_list.size()) - 1); + Node customer = candidate_list[position]; + Node node_index = + solution.Insert(customer, problem.customers[customer].demand, 0, 0); + candidate_list[position] = candidate_list.back(); + candidate_list.pop_back(); + context.heads[context.num_routes] = node_index; + context.route_loads[context.num_routes] = problem.customers[customer].demand; + ++context.num_routes; + return position; +} + +template +void SequentialInsertion(const Problem &problem, const Func &func, + CandidateList &candidate_list, Random &random, + Solution &solution, RouteContext &context) { + InsertionWithCost best_insertion{}; + std::vector is_full(context.num_routes, false); + while (!candidate_list.empty()) { + bool inserted = false; + for (Node route_index = 0; route_index < context.num_routes; + ++route_index) { + if (is_full[route_index]) { + continue; + } + int candidate_position = -1; + best_insertion.cost = Delta(std::numeric_limits::max(), -1); + for (int i = 0; i < static_cast(candidate_list.size()); ++i) { + Node customer = candidate_list[i]; + int demand = problem.customers[customer].demand; + if (context.route_loads[route_index] + demand > problem.capacity) { + continue; + } + auto insertion = CalcBestInsertion(solution, func, context, route_index, + customer, random); + if (best_insertion.Update(insertion, random)) { + candidate_position = i; + } + } + if (candidate_position == -1) { + is_full[route_index] = true; + } else { + Node customer = candidate_list[candidate_position]; + candidate_list[candidate_position] = candidate_list.back(); + candidate_list.pop_back(); + int demand = problem.customers[customer].demand; + Node node_index = + solution.Insert(customer, demand, best_insertion.predecessor, + best_insertion.successor); + if (best_insertion.predecessor == 0) { + context.heads[route_index] = node_index; + } + context.route_loads[route_index] += demand; + inserted = true; + } + } + if (!inserted) { + AddRoute(problem, candidate_list, random, solution, context); + is_full.push_back(false); + } + } +} + +template +void ParallelInsertion(const Problem &problem, const Func &func, + CandidateList &candidate_list, Random &random, + Solution &solution, RouteContext &context) { + std::vector>> best_insertions( + candidate_list.size()); + for (int i = 0; i < static_cast(candidate_list.size()); ++i) { + for (Node j = 0; j < context.num_routes; ++j) { + best_insertions[i].push_back(CalcBestInsertion( + solution, func, context, j, candidate_list[i], random)); + } + } + std::vector updated(context.num_routes, false); + InsertionWithCost best_insertion{}; + while (!candidate_list.empty()) { + int candidate_position = -1; + best_insertion.cost = Delta(std::numeric_limits::max(), -1); + for (int i = 0; i < static_cast(best_insertions.size()); ++i) { + for (int j = 0; j < static_cast(best_insertions[i].size()); ++j) { + Node route_index = best_insertions[i][j].route_index; + int demand = problem.customers[candidate_list[i]].demand; + if (context.route_loads[route_index] + demand > problem.capacity) { + best_insertions[i][j] = best_insertions[i].back(); + best_insertions[i].pop_back(); + --j; + } else { + if (updated[route_index]) { + best_insertions[i][j] = + CalcBestInsertion(solution, func, context, route_index, + candidate_list[i], random); + } + if (best_insertion.Update(best_insertions[i][j], random)) { + candidate_position = i; + } + } + } + } + if (candidate_position == -1) { + int position = + AddRoute(problem, candidate_list, random, solution, context); + best_insertions[position] = std::move(best_insertions.back()); + best_insertions.pop_back(); + for (int i = 0; i < static_cast(candidate_list.size()); ++i) { + Node customer = candidate_list[i]; + best_insertions[i].push_back(CalcBestInsertion( + solution, func, context, context.num_routes - 1, customer, random)); + } + updated.push_back(false); + } else { + Node customer = candidate_list[candidate_position]; + candidate_list[candidate_position] = candidate_list.back(); + candidate_list.pop_back(); + best_insertions[candidate_position] = std::move(best_insertions.back()); + best_insertions.pop_back(); + int demand = problem.customers[customer].demand; + Node node_index = + solution.Insert(customer, demand, best_insertion.predecessor, + best_insertion.successor); + Node route_index = best_insertion.route_index; + if (best_insertion.predecessor == 0) { + context.heads[route_index] = node_index; + } + context.route_loads[route_index] += demand; + updated[route_index] = true; + } + } +} + +template +void InsertCandidates(const Problem &problem, const Func &func, + CandidateList &candidate_list, Random &random, + Solution &solution, RouteContext &context) { + int strategy = random.NextInt(0, 1); + if (strategy == kSis) { + SequentialInsertion(problem, func, candidate_list, random, solution, + context); + } else { + ParallelInsertion(problem, func, candidate_list, random, solution, context); + } +} + +std::unique_ptr Construct(const Problem &problem, + const DistanceMatrix &distance_matrix, + Random &random) { + std::vector candidate_list(problem.num_customers - 1); + std::iota(candidate_list.begin(), candidate_list.end(), 1); + Node num_fleets = CalcFleetLowerBound(problem); + auto solution = std::make_unique(); + auto context = std::make_unique(); + for (Node i = 0; i < num_fleets; ++i) { + AddRoute(problem, candidate_list, random, *solution, *context); + } + int criterion = random.NextInt(0, 1); + if (criterion == kMcfic) { + float gamma = static_cast(random.NextInt(0, 34)) * 0.05f; + auto func = [&](Node predecessor, Node successor, Node customer) { + Node pre_customer = solution->customer(predecessor); + Node suc_customer = solution->customer(successor); + return static_cast(distance_matrix[pre_customer][customer] + + distance_matrix[customer][suc_customer] - + distance_matrix[pre_customer][suc_customer]) - + 2 * gamma * distance_matrix[0][customer]; + }; + InsertCandidates(problem, func, candidate_list, random, *solution, + *context); + } else { + auto func = [&](Node predecessor, Node successor, Node customer) { + Node pre_customer = solution->customer(predecessor); + if (pre_customer == 0) { + return std::numeric_limits::max(); + } else { + return static_cast(distance_matrix[pre_customer][customer]); + } + }; + InsertCandidates(problem, func, candidate_list, random, *solution, + *context); + } + return solution; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/construction.h b/src/sdvrp/splitils/algorithm/construction.h new file mode 100644 index 0000000..70dbdb9 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/construction.h @@ -0,0 +1,19 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_CONSTRUCTION_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_CONSTRUCTION_H_ + +#include + +#include "sdvrp/distance_matrix.h" +#include "sdvrp/problem.h" +#include "sdvrp/splitils/model/solution.h" +#include "util/random.h" + +namespace vrp::sdvrp::splitils { + +std::unique_ptr Construct(const Problem &problem, + const DistanceMatrix &distance_matrix, + Random &random); + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_CONSTRUCTION_H_ diff --git a/src/sdvrp/splitils/algorithm/operator/cross.cc b/src/sdvrp/splitils/algorithm/operator/cross.cc new file mode 100644 index 0000000..5818bce --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/cross.cc @@ -0,0 +1,120 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp::splitils { + +struct CrossMove { + bool reversed; + Node route_x, route_y; + Node left_x, left_y; +}; + +void DoCross(const CrossMove &move, Solution &solution, RouteContext &context) { + Node right_x = move.left_x ? solution.successor(move.left_x) + : context.heads[move.route_x]; + Node right_y = move.left_y ? solution.successor(move.left_y) + : context.heads[move.route_y]; + if (!move.reversed) { + solution.Link(move.left_x, right_y); + solution.Link(move.left_y, right_x); + if (move.left_x == 0) { + context.heads[move.route_x] = right_y; + } + if (move.left_y == 0) { + context.heads[move.route_y] = right_x; + } + } else { + Node head_y = context.heads[move.route_y]; + if (right_x) { + Node tail_x = context.tails[move.route_x]; + ReversedLink(solution, right_x, tail_x, 0, right_y); + context.heads[move.route_y] = tail_x; + } else { + solution.Link(0, right_y); + context.heads[move.route_y] = right_y; + } + solution.set_successor(0, context.heads[move.route_x]); + if (move.left_y) { + ReversedLink(solution, head_y, move.left_y, move.left_x, 0); + } else { + solution.Link(move.left_x, 0); + } + context.heads[move.route_x] = solution.successor(0); + } +} + +void Cross(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, Node route_x, + Node route_y, CrossMove &best_move, Delta &best_delta, + Random &random) { + Node left_x = 0; + do { + Node successor_x = + left_x ? solution.successor(left_x) : context.heads[route_x]; + Node left_y = 0; + do { + Node predecessor_y = left_y; + Node successor_y = + left_y ? solution.successor(left_y) : context.heads[route_y]; + int predecessor_load_x = context.loads[left_x]; + int successor_load_x = context.route_loads[route_x] - predecessor_load_x; + int predecessor_load_y = context.loads[left_y]; + int successor_load_y = context.route_loads[route_y] - predecessor_load_y; + int base = -distance_matrix[solution.customer(left_x)] + [solution.customer(successor_x)] - + distance_matrix[solution.customer(left_y)] + [solution.customer(successor_y)]; + for (bool reversed : {false, true}) { + if (predecessor_load_x + successor_load_y <= problem.capacity && + successor_load_x + predecessor_load_y <= problem.capacity) { + int delta = base + + distance_matrix[solution.customer(left_x)] + [solution.customer(successor_y)] + + distance_matrix[solution.customer(successor_x)] + [solution.customer(predecessor_y)]; + if (best_delta.Update(delta, random)) { + best_move = {reversed, route_x, route_y, left_x, left_y}; + } + } + std::swap(predecessor_y, successor_y); + std::swap(predecessor_load_y, successor_load_y); + } + left_y = successor_y; + } while (left_y); + left_x = successor_x; + } while (left_x); +} + +bool Cross(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + CrossMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = route_x + 1; route_y < context.num_routes; ++route_y) { + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + Cross(problem, distance_matrix, solution, context, route_x, route_y, + move, cache.delta, random); + } else { + move.route_x = route_x; + move.route_y = route_y; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoCross(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/exchange.cc b/src/sdvrp/splitils/algorithm/operator/exchange.cc new file mode 100644 index 0000000..fb3700c --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/exchange.cc @@ -0,0 +1,79 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +namespace vrp::sdvrp::splitils { + +struct ExchangeMove { + Node node_a; + Node node_b; +}; + +void DoExchange(const ExchangeMove &move, Node route_index, Solution &solution, + RouteContext &context) { + Node predecessor_a = solution.predecessor(move.node_a); + Node successor_a = solution.successor(move.node_a); + Node predecessor_b = solution.predecessor(move.node_b); + Node successor_b = solution.successor(move.node_b); + solution.Link(predecessor_a, move.node_b); + solution.Link(move.node_b, successor_a); + solution.Link(predecessor_b, move.node_a); + solution.Link(move.node_a, successor_b); + if (!predecessor_a) { + context.heads[route_index] = move.node_b; + } + UpdateRouteContext(solution, route_index, predecessor_a, context); +} + +void Exchange(const DistanceMatrix &distance_matrix, const Solution &solution, + Node node_a, Node node_b, ExchangeMove &best_move, + Delta &best_delta, Random &random) { + Node predecessor_a = solution.predecessor(node_a); + Node successor_a = solution.successor(node_a); + Node predecessor_b = solution.predecessor(node_b); + Node successor_b = solution.successor(node_b); + int delta = distance_matrix[solution.customer(predecessor_a)] + [solution.customer(node_b)] + + distance_matrix[solution.customer(node_b)] + [solution.customer(successor_a)] + + distance_matrix[solution.customer(predecessor_b)] + [solution.customer(node_a)] + + distance_matrix[solution.customer(node_a)] + [solution.customer(successor_b)] - + distance_matrix[solution.customer(predecessor_a)] + [solution.customer(node_a)] - + distance_matrix[solution.customer(node_a)] + [solution.customer(successor_a)] - + distance_matrix[solution.customer(predecessor_b)] + [solution.customer(node_b)] - + distance_matrix[solution.customer(node_b)] + [solution.customer(successor_b)]; + if (best_delta.Update(delta, random)) { + best_move = {node_a, node_b}; + } +} + +bool Exchange(const Problem &problem, const DistanceMatrix &distance_matrix, + Node route_index, Solution &solution, RouteContext &context, + Random &random) { + ExchangeMove best_move{}; + Delta best_delta; + Node node_a = context.heads[route_index]; + while (node_a) { + Node node_b = solution.successor(node_a); + if (node_b) { + node_b = solution.successor(node_b); + while (node_b) { + Exchange(distance_matrix, solution, node_a, node_b, best_move, + best_delta, random); + node_b = solution.successor(node_b); + } + } + node_a = solution.successor(node_a); + } + if (best_delta.value < 0) { + DoExchange(best_move, route_index, solution, context); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/k_split.cc b/src/sdvrp/splitils/algorithm/operator/k_split.cc new file mode 100644 index 0000000..16ee0ae --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/k_split.cc @@ -0,0 +1,68 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp::splitils { + +struct NodeInfo { + Node node_index; + Node route_index; + NodeInfo(Node node_index, Node route_index) + : node_index(node_index), route_index(route_index) {} +}; + +int DoKSplit(const Problem &problem, const DistanceMatrix &distance_matrix, + const std::vector &customer_nodes, Solution &solution, + RouteContext &context, std::set &associated_routes, + Random &random) { + int delta = 0; + for (const auto &customer_info : customer_nodes) { + Node node_index = customer_info.node_index; + Node route_index = customer_info.route_index; + delta += + RemoveNode(distance_matrix, node_index, route_index, solution, context); + associated_routes.insert(route_index); + } + Node customer = solution.customer(customer_nodes[0].node_index); + delta += SplitReinsertion(problem, distance_matrix, customer, + problem.customers[customer].demand, 0.0, solution, + context, associated_routes, random); + return delta; +} + +bool KSplit(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + Delta best_delta; + Node best_customer; + auto solution_bak = std::make_unique(solution); + auto context_bak = std::make_unique(context); + std::vector> customers(problem.num_customers); + for (Node route_index = 0; route_index < context.num_routes; ++route_index) { + Node node_index = context.heads[route_index]; + while (node_index) { + customers[solution.customer(node_index)].emplace_back(node_index, + route_index); + node_index = solution.successor(node_index); + } + } + for (Node i = 1; i < problem.num_customers; ++i) { + int delta = DoKSplit(problem, distance_matrix, customers[i], solution, + context, associated_routes, random); + associated_routes.clear(); + if (best_delta.Update(delta, random)) { + best_customer = i; + } + solution = *solution_bak; + context = *context_bak; + } + if (best_delta.value < 0) { + DoKSplit(problem, distance_matrix, customers[best_customer], solution, + context, associated_routes, random); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/or_opt.cc b/src/sdvrp/splitils/algorithm/operator/or_opt.cc new file mode 100644 index 0000000..5ef24b3 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/or_opt.cc @@ -0,0 +1,118 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp::splitils { + +struct OrOptMove { + bool reversed; + Node head; + Node tail; + Node predecessor; + Node successor; +}; + +void DoOrOpt(const OrOptMove &move, Node route_index, Solution &solution, + RouteContext &context) { + Node predecessor_head = solution.predecessor(move.head); + Node successor_tail = solution.successor(move.tail); + solution.set_successor(0, context.heads[route_index]); + solution.Link(predecessor_head, successor_tail); + if (!move.reversed) { + solution.Link(move.predecessor, move.head); + solution.Link(move.tail, move.successor); + } else { + ReversedLink(solution, move.head, move.tail, move.predecessor, + move.successor); + } + context.heads[route_index] = solution.successor(0); +} + +template +void OrOpt(const DistanceMatrix &distance_matrix, const Solution &solution, + Node head, Node tail, Node predecessor, Node successor, + OrOptMove &best_move, Delta &best_delta, Random &random) { + Node predecessor_head = solution.predecessor(head); + Node successor_tail = solution.successor(tail); + int delta = distance_matrix[solution.customer(predecessor_head)] + [solution.customer(successor_tail)] - + distance_matrix[solution.customer(predecessor_head)] + [solution.customer(head)] - + distance_matrix[solution.customer(tail)] + [solution.customer(successor_tail)] - + distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)]; + bool reversed = false; + int insertion_delta = + distance_matrix[solution.customer(predecessor)][solution.customer(head)] + + distance_matrix[solution.customer(successor)][solution.customer(tail)]; + if (num > 1) { + int reversed_delta = + distance_matrix[solution.customer(predecessor)] + [solution.customer(tail)] + + distance_matrix[solution.customer(successor)][solution.customer(head)]; + if (reversed_delta < insertion_delta) { + insertion_delta = reversed_delta; + reversed = true; + } + } + delta += insertion_delta; + if (best_delta.Update(delta, random)) { + best_move = {reversed, head, tail, predecessor, successor}; + } +} + +template +bool OrOpt(const Problem &problem, const DistanceMatrix &distance_matrix, + Node route_index, Solution &solution, RouteContext &context, + Random &random) { + OrOptMove best_move{}; + Delta best_delta; + Node head = context.heads[route_index]; + Node tail = head; + for (Node i = 0; tail && i < num - 1; ++i) { + tail = solution.successor(tail); + } + while (tail) { + Node predecessor, successor; + predecessor = solution.successor(tail); + while (predecessor) { + successor = solution.successor(predecessor); + OrOpt(distance_matrix, solution, head, tail, predecessor, successor, + best_move, best_delta, random); + predecessor = successor; + } + successor = solution.predecessor(head); + while (successor) { + predecessor = solution.predecessor(successor); + OrOpt(distance_matrix, solution, head, tail, predecessor, successor, + best_move, best_delta, random); + successor = predecessor; + } + head = solution.successor(head); + tail = solution.successor(tail); + } + if (best_delta.value < 0) { + DoOrOpt(best_move, route_index, solution, context); + UpdateRouteContext(solution, route_index, 0, context); + return true; + } + return false; +} + +template bool OrOpt<1>(const Problem &problem, + const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context, + Random &random); + +template bool OrOpt<2>(const Problem &problem, + const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context, + Random &random); + +template bool OrOpt<3>(const Problem &problem, + const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context, + Random &random); + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/relocate.cc b/src/sdvrp/splitils/algorithm/operator/relocate.cc new file mode 100644 index 0000000..ebaa7e0 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/relocate.cc @@ -0,0 +1,88 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/algorithm/base_star.h" +#include "sdvrp/splitils/model/route_head_guard.h" + +namespace vrp::sdvrp::splitils { + +struct RelocateMove { + Node route_x, route_y; + Node node_x, predecessor_x, successor_x; +}; + +inline void DoRelocate(RelocateMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_x = solution.predecessor(move.node_x); + Node successor_x = solution.successor(move.node_x); + { + RouteHeadGuard guard(solution, context, move.route_x); + solution.Link(predecessor_x, successor_x); + } + { + RouteHeadGuard guard(solution, context, move.route_y); + solution.Link(move.predecessor_x, move.node_x); + solution.Link(move.node_x, move.successor_x); + } +} + +void Relocate(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, RelocateMove &best_move, + Delta &best_delta, Random &random) { + PreprocessStarInsertions(problem, distance_matrix, solution, context, route_y, + random); + Node node_x = context.heads[route_x]; + while (node_x) { + if (context.route_loads[route_y] + solution.load(node_x) <= + problem.capacity) { + auto insertion = + star_caches[route_y][solution.customer(node_x)].FindBest(); + Node predecessor_x = solution.predecessor(node_x); + Node successor_x = solution.successor(node_x); + int delta = + insertion->delta.value - CalcDelta(distance_matrix, solution, node_x, + predecessor_x, successor_x); + if (best_delta.Update(delta, random)) { + best_move = {route_x, route_y, node_x, insertion->predecessor, + insertion->successor}; + } + } + node_x = solution.successor(node_x); + } +} + +bool Relocate(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + RelocateMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = 0; route_y < context.num_routes; ++route_y) { + if (route_x == route_y) { + continue; + } + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + Relocate(problem, distance_matrix, solution, context, route_x, route_y, + move, cache.delta, random); + } else { + move.route_x = route_x; + move.route_y = route_y; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoRelocate(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/repair.cc b/src/sdvrp/splitils/algorithm/operator/repair.cc new file mode 100644 index 0000000..2e9f418 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/repair.cc @@ -0,0 +1,69 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include + +namespace vrp::sdvrp::splitils { + +void MergeAdjacentSameCustomers(const DistanceMatrix &distance_matrix, + Node route_index, Solution &solution, + RouteContext &context) { + Node node_index = context.heads[route_index]; + while (true) { + Node successor = solution.successor(node_index); + if (!successor) { + break; + } + if (solution.customer(node_index) == solution.customer(successor)) { + solution.set_load(node_index, + solution.load(node_index) + solution.load(successor)); + solution.Remove(successor); + } else { + node_index = successor; + } + } +} + +int CalcRemovalDelta(const DistanceMatrix &distance_matrix, + const Solution &solution, Node node_index) { + Node predecessor = solution.predecessor(node_index); + Node successor = solution.successor(node_index); + return distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)] - + distance_matrix[solution.customer(predecessor)] + [solution.customer(node_index)] - + distance_matrix[solution.customer(node_index)] + [solution.customer(successor)]; +} + +void Repair(const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context) { + if (!context.heads[route_index]) { + return; + } + MergeAdjacentSameCustomers(distance_matrix, route_index, solution, context); + std::map customer_node_map; + Node node_index = context.heads[route_index]; + solution.set_successor(0, node_index); + while (node_index) { + Node successor = solution.successor(node_index); + Node customer = solution.customer(node_index); + auto it = customer_node_map.find(customer); + if (it == customer_node_map.end()) { + customer_node_map[customer] = node_index; + } else { + Node &last_node_index = it->second; + if (CalcRemovalDelta(distance_matrix, solution, last_node_index) < + CalcRemovalDelta(distance_matrix, solution, node_index)) { + std::swap(last_node_index, node_index); + } + solution.set_load(last_node_index, solution.load(last_node_index) + + solution.load(node_index)); + solution.Remove(node_index); + } + node_index = successor; + } + context.heads[route_index] = solution.successor(0); + UpdateRouteContext(solution, route_index, 0, context); +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/route_addition.cc b/src/sdvrp/splitils/algorithm/operator/route_addition.cc new file mode 100644 index 0000000..94efc70 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/route_addition.cc @@ -0,0 +1,207 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include +#include + +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp::splitils { + +struct RouteAdditionMove { + Node customer_index; + Node before; + Node after; +}; + +struct RouteNodeLoadDelta { + Node route_index; + Node node_index; + int load; + int delta; +}; + +std::vector RouteAdditionPreprocess( + const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, Node customer_index, + const std::vector> &route_node_indices) { + std::vector route_node_load_deltas; + route_node_load_deltas.reserve(route_node_indices.size() * 2); + int load_limit = problem.capacity - problem.customers[customer_index].demand; + for (auto &&[route_index, node_index] : route_node_indices) { + Node predecessor = solution.predecessor(node_index); + Node successor = solution.successor(node_index); + int before_load = context.loads[predecessor]; + int after_load = + context.route_loads[route_index] - context.loads[node_index]; + if (predecessor && before_load <= load_limit) { + int delta = + distance_matrix[solution.customer(predecessor)][customer_index] + + distance_matrix[0][solution.customer(successor)] - + distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)]; + route_node_load_deltas.emplace_back( + RouteNodeLoadDelta{route_index, predecessor, before_load, delta}); + } + if (successor && after_load <= load_limit) { + int delta = + distance_matrix[solution.customer(successor)][customer_index] + + distance_matrix[0][solution.customer(predecessor)] - + distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)]; + route_node_load_deltas.emplace_back( + RouteNodeLoadDelta{route_index, successor, after_load, delta}); + } + } + return route_node_load_deltas; +} + +void RouteAdditionType0(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node customer_index, int base_delta, + RouteAdditionMove &best_move, Delta &best_delta, + Random &random) { + int delta = base_delta + distance_matrix[0][customer_index] * 2; + if (best_delta.Update(delta, random)) { + best_move = {customer_index, 0, 0}; + } +} + +void RouteAdditionType1( + const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, Node customer_index, + const std::vector &route_node_load_deltas, + int base_delta, RouteAdditionMove &best_move, Delta &best_delta, + Random &random) { + auto it = std::min_element( + route_node_load_deltas.begin(), route_node_load_deltas.end(), + [](auto &&lhs, auto &&rhs) { return lhs.delta < rhs.delta; }); + if (it != route_node_load_deltas.end()) { + int delta = base_delta + distance_matrix[0][customer_index] + it->delta; + if (best_delta.Update(delta, random)) { + best_move = {customer_index, it->node_index, 0}; + } + } +} + +void RouteAdditionType2( + const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, Node customer_index, + const std::vector &route_node_load_deltas, + int base_delta, RouteAdditionMove &best_move, Delta &best_delta, + Random &random) { + Load load_limit = problem.capacity - problem.customers[customer_index].demand; + int size = route_node_load_deltas.size(); + for (int i = 0; i < size; ++i) { + auto &&route_node_load_delta1 = route_node_load_deltas[i]; + for (int j = i + 1; j < size; ++j) { + auto &&route_node_load_delta2 = route_node_load_deltas[j]; + if (route_node_load_delta1.route_index == + route_node_load_delta2.route_index || + route_node_load_delta1.load + route_node_load_delta2.load > + load_limit) { + continue; + } + int delta = base_delta + route_node_load_delta1.delta + + route_node_load_delta2.delta; + if (best_delta.Update(delta, random)) { + best_move = {customer_index, route_node_load_delta1.node_index, + route_node_load_delta2.node_index}; + } + } + } +} + +bool RouteAddition(const Problem &problem, + const DistanceMatrix &distance_matrix, Solution &solution, + RouteContext &context, std::set &associated_routes, + Random &random, OperatorCaches &operator_caches) { + RouteAdditionMove best_move{}; + Delta best_delta; + std::map>> + customer_to_route_node_indices; + for (Node route_index = 0; route_index < context.num_routes; ++route_index) { + for (Node node_index = context.heads[route_index]; node_index; + node_index = solution.successor(node_index)) { + Node customer_index = solution.customer(node_index); + customer_to_route_node_indices[customer_index].emplace_back(route_index, + node_index); + } + } + for (auto &&[customer_index, route_node_indices] : + customer_to_route_node_indices) { + if (route_node_indices.size() <= 1) { + continue; + } + int base_delta = 0; + for (auto &&[route_index, node_index] : route_node_indices) { + Node predecessor = solution.predecessor(node_index); + Node successor = solution.successor(node_index); + base_delta += + distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)] - + distance_matrix[solution.customer(predecessor)][customer_index] - + distance_matrix[customer_index][solution.customer(successor)]; + } + auto route_node_load_delta = + RouteAdditionPreprocess(problem, distance_matrix, solution, context, + customer_index, route_node_indices); + RouteAdditionType0(problem, distance_matrix, solution, context, + customer_index, base_delta, best_move, best_delta, + random); + RouteAdditionType1(problem, distance_matrix, solution, context, + customer_index, route_node_load_delta, base_delta, + best_move, best_delta, random); + RouteAdditionType2(problem, distance_matrix, solution, context, + customer_index, route_node_load_delta, base_delta, + best_move, best_delta, random); + } + if (best_delta.value < 0) { + Node customer_index = best_move.customer_index; + auto &&route_node_indices = customer_to_route_node_indices[customer_index]; + Node new_node_index = solution.NewNode( + customer_index, problem.customers[customer_index].demand); + Node new_head = new_node_index; + for (auto &&[route_index, node_index] : route_node_indices) { + Node predecessor = solution.predecessor(node_index); + Node successor = solution.successor(node_index); + solution.Remove(node_index); + solution.Link(predecessor, successor); + if (successor) { + if (successor == best_move.before) { + solution.Link(predecessor, 0); + ReversedLink(solution, successor, context.tails[route_index], 0, + new_node_index); + new_head = context.tails[route_index]; + successor = 0; + } else if (successor == best_move.after) { + solution.Link(predecessor, 0); + successor = 0; + } + } + if (predecessor) { + if (predecessor == best_move.before) { + solution.Link(0, successor); + new_head = context.heads[route_index]; + context.heads[route_index] = successor; + } else if (predecessor == best_move.after) { + solution.Link(0, successor); + ReversedLink(solution, context.heads[route_index], predecessor, + new_node_index, 0); + context.heads[route_index] = successor; + } + } else { + context.heads[route_index] = successor; + } + associated_routes.insert(route_index); + } + solution.Link(best_move.before, new_node_index); + solution.Link(new_node_index, best_move.after); + associated_routes.insert(context.num_routes); + context.heads[context.num_routes++] = new_head; + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/sd_swap_one_in.cc b/src/sdvrp/splitils/algorithm/operator/sd_swap_one_in.cc new file mode 100644 index 0000000..bb0a419 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/sd_swap_one_in.cc @@ -0,0 +1,155 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp::splitils { + +struct SdSwapOneInMove { + bool direction_one; + bool reversed_in; + Node route_one; + Node node_one; + Node route_in; + Node head_in; + Node tail_in; + int one_split_load; +}; + +void DoSdSwapOneIn(const SdSwapOneInMove &move, Solution &solution, + RouteContext &context) { + Node node_one = move.node_one; + Node head_in = move.head_in; + Node tail_in = move.tail_in; + Node predecessor_one = solution.predecessor(node_one); + Node successor_one = solution.successor(node_one); + Node predecessor_head_in = solution.predecessor(head_in); + Node successor_tail_in = solution.successor(tail_in); + Node split_index = + solution.Insert(solution.customer(node_one), + solution.load(node_one) - move.one_split_load, + predecessor_head_in, successor_tail_in); + if (!predecessor_head_in) { + context.heads[move.route_in] = split_index; + } + solution.set_load(node_one, move.one_split_load); + Node predecessor, successor; + if (!move.direction_one) { + predecessor = predecessor_one; + successor = node_one; + } else { + predecessor = node_one; + successor = successor_one; + } + if (move.direction_one ^ move.reversed_in) { + ReversedLink(solution, head_in, tail_in, predecessor, successor); + } else { + solution.Link(predecessor, head_in); + solution.Link(tail_in, successor); + } + if (!predecessor) { + context.heads[move.route_one] = solution.successor(0); + } +} + +inline void SdSwapOneIn(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_one, Node node_one, Node route_in, + Node head_in, Node tail_in, int one_split_load, + SdSwapOneInMove &best_move, Delta &best_delta, + Random &random) { + Node customer_node_one = solution.customer(node_one); + Node customer_head_in = solution.customer(head_in); + Node customer_tail_in = solution.customer(tail_in); + Node predecessor_one = solution.customer(solution.predecessor(node_one)); + Node successor_one = solution.customer(solution.successor(node_one)); + Node predecessor_head_in = solution.customer(solution.predecessor(head_in)); + Node successor_tail_in = solution.customer(solution.successor(tail_in)); + int delta = distance_matrix[customer_node_one][predecessor_head_in] + + distance_matrix[customer_node_one][successor_tail_in] - + distance_matrix[customer_head_in][predecessor_head_in] - + distance_matrix[customer_tail_in][successor_tail_in]; + bool best_direction_one, best_reversed_in; + int min_delta = std::numeric_limits::max(); + for (bool direction_one : {false, true}) { + int link_delta = -distance_matrix[customer_node_one][predecessor_one]; + for (bool reversed_in : {false, true}) { + int current_delta = distance_matrix[customer_head_in][predecessor_one] + + distance_matrix[customer_node_one][customer_tail_in] + + link_delta; + if (current_delta < min_delta) { + min_delta = current_delta; + best_direction_one = direction_one; + best_reversed_in = reversed_in; + } + std::swap(customer_head_in, customer_tail_in); + } + std::swap(predecessor_one, successor_one); + } + delta += min_delta; + if (best_delta.Update(delta, random)) { + best_move = { + best_direction_one, best_reversed_in, route_one, node_one, + route_in, head_in, tail_in, one_split_load}; + } +} + +void SdSwapOneIn(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_one, Node node_one, Node route_in, + SdSwapOneInMove &best_move, Delta &best_delta, + Random &random) { + Load load_one = solution.load(node_one); + Node head_in = context.heads[route_in]; + while (head_in) { + Load load_in = solution.load(head_in); + Node tail_in = head_in; + while (tail_in && load_in < load_one) { + SdSwapOneIn(problem, distance_matrix, solution, context, route_one, + node_one, route_in, head_in, tail_in, load_one - load_in, + best_move, best_delta, random); + tail_in = solution.successor(tail_in); + load_in += solution.load(tail_in); + } + head_in = solution.successor(head_in); + } +} + +bool SdSwapOneIn(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SdSwapOneInMove best_move{}; + Delta best_delta; + for (Node route_one = 0; route_one < context.num_routes; ++route_one) { + for (Node route_in = 0; route_in < context.num_routes; ++route_in) { + if (route_one == route_in) { + continue; + } + auto &cache = operator_caches.Get(route_one, route_in); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + for (Node node_one = context.heads[route_one]; node_one; + node_one = solution.successor(node_one)) { + SdSwapOneIn(problem, distance_matrix, solution, context, route_one, + node_one, route_in, move, cache.delta, random); + } + } else { + move.route_one = route_one; + move.route_in = route_in; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSdSwapOneIn(best_move, solution, context); + associated_routes.insert(best_move.route_one); + associated_routes.insert(best_move.route_in); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/sd_swap_one_one.cc b/src/sdvrp/splitils/algorithm/operator/sd_swap_one_one.cc new file mode 100644 index 0000000..18b93e3 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/sd_swap_one_one.cc @@ -0,0 +1,129 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/algorithm/base_star.h" +#include "sdvrp/splitils/model/route_head_guard.h" +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp::splitils { + +struct SdSwapOneOneMove { + bool swapped; + Node route_x, route_y; + Node node_x, predecessor_x, successor_x; + Node node_y, predecessor_y, successor_y; + int split_load; +}; + +void DoSdSwapOneOne(const SdSwapOneOneMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_y = solution.predecessor(move.node_y); + Node successor_y = solution.successor(move.node_y); + { + RouteHeadGuard guard(solution, context, move.route_x); + solution.set_load(move.node_x, move.split_load); + solution.Link(move.predecessor_y, move.node_y); + solution.Link(move.node_y, move.successor_y); + } + { + RouteHeadGuard guard(solution, context, move.route_y); + solution.Link(predecessor_y, successor_y); + solution.Insert(solution.customer(move.node_x), solution.load(move.node_y), + move.predecessor_x, move.successor_x); + } +} + +inline void SdSwapOneOne(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + bool swapped, Node route_x, Node route_y, Node node_x, + Node node_y, int split_load, + SdSwapOneOneMove &best_move, Delta &best_delta, + Random &random) { + Node predecessor_x = solution.predecessor(node_x); + Node successor_x = solution.successor(node_x); + Node predecessor_y = solution.predecessor(node_y); + Node successor_y = solution.successor(node_y); + int delta = + -CalcDelta(distance_matrix, solution, node_y, predecessor_y, successor_y); + int delta_x = + CalcDelta(distance_matrix, solution, node_x, predecessor_y, successor_y); + int before = + CalcDelta(distance_matrix, solution, node_y, predecessor_x, node_x); + int after = CalcDelta(distance_matrix, solution, node_y, node_x, successor_x); + int delta_y; + Node predecessor; + Node successor; + if (before <= after) { + predecessor = predecessor_x; + successor = node_x; + } else { + predecessor = node_x; + successor = successor_x; + } + delta += delta_x + delta_y; + if (best_delta.Update(delta, random)) { + best_move = {swapped, route_x, route_y, node_x, predecessor_y, + successor_y, node_y, predecessor, successor, split_load}; + } +} + +void SdSwapOneOne(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, SdSwapOneOneMove &best_move, + Delta &best_delta, Random &random) { + for (Node node_x = context.heads[route_x]; node_x; + node_x = solution.successor(node_x)) { + Load load_x = solution.load(node_x); + for (Node node_y = context.heads[route_y]; node_y; + node_y = solution.successor(node_y)) { + Load load_y = solution.load(node_y); + if (load_x > load_y) { + SdSwapOneOne(problem, distance_matrix, solution, context, false, + route_x, route_y, node_x, node_y, load_x - load_y, + best_move, best_delta, random); + } else if (load_y > load_x) { + SdSwapOneOne(problem, distance_matrix, solution, context, true, route_y, + route_x, node_y, node_x, load_y - load_x, best_move, + best_delta, random); + } + } + } +} + +bool SdSwapOneOne(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SdSwapOneOneMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = route_x + 1; route_y < context.num_routes; ++route_y) { + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + SdSwapOneOne(problem, distance_matrix, solution, context, route_x, + route_y, move, cache.delta, random); + } else { + if (!move.swapped) { + move.route_x = route_x; + move.route_y = route_y; + } else { + move.route_x = route_y; + move.route_y = route_x; + } + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSdSwapOneOne(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/sd_swap_one_out.cc b/src/sdvrp/splitils/algorithm/operator/sd_swap_one_out.cc new file mode 100644 index 0000000..d54a3e5 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/sd_swap_one_out.cc @@ -0,0 +1,201 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/model/direction.h" + +namespace vrp::sdvrp::splitils { + +struct SdSwapOneOutMove { + bool direction_out; + bool reversed_out; + bool before_tail_out; + Node route_one; + Node node_one; + Node route_out; + Node head_out; + Node tail_out; + int tail_split_load; +}; + +template +void DoSdSwapOneOut(const SdSwapOneOutMove &move, Solution &solution, + RouteContext &context) { + Node tail_out = move.tail_out; + Node predecessor_one = solution.predecessor(move.node_one); + Node successor_one = solution.successor(move.node_one); + Node predecessor_head_out = + DirectionOut::Predecessor(solution, move.head_out); + Node successor_tail_out = DirectionOut::Successor(solution, tail_out); + Node split_index = + solution.NewNode(solution.customer(tail_out), move.tail_split_load); + solution.set_load(tail_out, solution.load(tail_out) - move.tail_split_load); + Node predecessor = predecessor_one; + if (!move.reversed_out) { + for (Node node_index = move.head_out; node_index != successor_tail_out;) { + Node next_node_index = DirectionOut::Successor(solution, node_index); + solution.Link(predecessor, node_index); + predecessor = node_index; + node_index = next_node_index; + } + } else { + for (Node node_index = move.tail_out; node_index != predecessor_head_out;) { + Node next_node_index = DirectionOut::Predecessor(solution, node_index); + solution.Link(predecessor, node_index); + predecessor = node_index; + node_index = next_node_index; + } + } + solution.Link(predecessor, successor_one); + if (!predecessor_one) { + context.heads[move.route_one] = solution.successor(0); + } + if (move.before_tail_out) { + DirectionOut::Link(predecessor_head_out, move.node_one, solution); + DirectionOut::Link(move.node_one, split_index, solution); + DirectionOut::Link(split_index, successor_tail_out, solution); + } else { + DirectionOut::Link(predecessor_head_out, split_index, solution); + DirectionOut::Link(split_index, move.node_one, solution); + DirectionOut::Link(move.node_one, successor_tail_out, solution); + } + Node predecessor_out = + move.direction_out ? successor_tail_out : predecessor_head_out; + if (!predecessor_out) { + context.heads[move.route_out] = solution.successor(0); + } +} + +template +inline void +SdSwapOneOut(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_one, Node node_one, Node route_out, Node head_out, + Node tail_out, int tail_split_load, SdSwapOneOutMove &best_move, + Delta &best_delta, Random &random) { + Node customer_node_one = solution.customer(node_one); + Node customer_head_out = solution.customer(head_out); + Node customer_tail_out = solution.customer(tail_out); + Node predecessor_one = solution.customer(solution.predecessor(node_one)); + Node successor_one = solution.customer(solution.successor(node_one)); + Node predecessor_head_out = + solution.customer(DirectionOut::Predecessor(solution, head_out)); + Node successor_tail_out = + solution.customer(DirectionOut::Successor(solution, tail_out)); + int delta = -distance_matrix[customer_node_one][predecessor_one] - + distance_matrix[customer_node_one][successor_one] - + distance_matrix[customer_head_out][predecessor_head_out] + + distance_matrix[customer_node_one][customer_tail_out]; + int non_reversed_delta = distance_matrix[customer_head_out][predecessor_one] + + distance_matrix[customer_tail_out][successor_one]; + int reversed_delta = distance_matrix[customer_tail_out][predecessor_one] + + distance_matrix[customer_head_out][successor_one]; + bool reversed_out; + if (non_reversed_delta <= reversed_delta) { + delta += non_reversed_delta; + reversed_out = false; + } else { + delta += reversed_delta; + reversed_out = true; + } + int before_tail_delta = + distance_matrix[customer_node_one][predecessor_head_out]; + int after_tail_delta = + distance_matrix[customer_tail_out][predecessor_head_out] + + distance_matrix[customer_node_one][successor_tail_out] - + distance_matrix[customer_tail_out][successor_tail_out]; + bool before_tail_out; + if (before_tail_delta <= after_tail_delta) { + delta += before_tail_delta; + before_tail_out = true; + } else { + delta += after_tail_delta; + before_tail_out = false; + } + if (best_delta.Update(delta, random)) { + best_move = {DirectionOut::kDirection, + reversed_out, + before_tail_out, + route_one, + node_one, + route_out, + head_out, + tail_out, + tail_split_load}; + } +} + +template +void SdSwapOneOut(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_one, Node node_one, Node route_out, + SdSwapOneOutMove &best_move, Delta &best_delta, + Random &random) { + Load load_one = solution.load(node_one); + Node head_out = DirectionOut::Head(context, route_out); + Node tail_out = head_out; + Load load_out = solution.load(head_out); + while (true) { + while (load_out < load_one) { + tail_out = DirectionOut::Successor(solution, tail_out); + if (!tail_out) { + return; + } + load_out += solution.load(tail_out); + } + int tail_split_load = load_out - load_one; + if (tail_split_load > 0) { + SdSwapOneOut(problem, distance_matrix, solution, context, + route_one, node_one, route_out, head_out, + tail_out, tail_split_load, best_move, + best_delta, random); + } + load_out -= solution.load(head_out); + head_out = DirectionOut::Successor(solution, head_out); + } +} + +bool SdSwapOneOut(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SdSwapOneOutMove best_move{}; + Delta best_delta; + for (Node route_one = 0; route_one < context.num_routes; ++route_one) { + for (Node route_out = 0; route_out < context.num_routes; ++route_out) { + if (route_one == route_out) { + continue; + } + auto &cache = operator_caches.Get(route_one, route_out); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + for (Node node_one = context.heads[route_one]; node_one; + node_one = solution.successor(node_one)) { + SdSwapOneOut(problem, distance_matrix, solution, context, + route_one, node_one, route_out, move, + cache.delta, random); + SdSwapOneOut(problem, distance_matrix, solution, context, + route_one, node_one, route_out, move, + cache.delta, random); + } + } else { + move.route_one = route_one; + move.route_out = route_out; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + if (!best_move.direction_out) { + DoSdSwapOneOut(best_move, solution, context); + } else { + DoSdSwapOneOut(best_move, solution, context); + } + associated_routes.insert(best_move.route_one); + associated_routes.insert(best_move.route_out); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/sd_swap_star.cc b/src/sdvrp/splitils/algorithm/operator/sd_swap_star.cc new file mode 100644 index 0000000..2fac348 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/sd_swap_star.cc @@ -0,0 +1,135 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/algorithm/base_star.h" +#include "sdvrp/splitils/model/route_head_guard.h" + +namespace vrp::sdvrp::splitils { + +struct SdSwapStarMove { + bool swapped; + Node route_x, route_y; + Node node_x, predecessor_x, successor_x; + Node node_y, predecessor_y, successor_y; + int split_load; +}; + +inline void DoSdSwapStar(SdSwapStarMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_y = solution.predecessor(move.node_y); + Node successor_y = solution.successor(move.node_y); + { + RouteHeadGuard guard(solution, context, move.route_x); + solution.set_load(move.node_x, move.split_load); + solution.Link(move.predecessor_y, move.node_y); + solution.Link(move.node_y, move.successor_y); + } + { + RouteHeadGuard guard(solution, context, move.route_y); + solution.Link(predecessor_y, successor_y); + solution.Insert(solution.customer(move.node_x), solution.load(move.node_y), + move.predecessor_x, move.successor_x); + } +} + +inline void SdSwapStar(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + bool swapped, Node route_x, Node route_y, Node node_x, + Node node_y, int split_load, SdSwapStarMove &best_move, + Delta &best_delta, Random &random) { + auto &&insertion_x = star_caches[route_y][solution.customer(node_x)]; + auto &&insertion_y = star_caches[route_x][solution.customer(node_y)]; + Node predecessor_y = solution.predecessor(node_y); + Node successor_y = solution.successor(node_y); + int delta = + -CalcDelta(distance_matrix, solution, node_y, predecessor_y, successor_y); + int delta_x = + CalcDelta(distance_matrix, solution, node_x, predecessor_y, successor_y); + auto best_insertion_y = insertion_y.FindBest(); + auto best_insertion_x = insertion_x.FindBestWithoutNode(node_y); + if (best_insertion_x && best_insertion_x->delta.value < delta_x) { + delta_x = best_insertion_x->delta.value; + predecessor_y = best_insertion_x->predecessor; + successor_y = best_insertion_x->successor; + } + delta += delta_x + best_insertion_y->delta.value; + if (best_delta.Update(delta, random)) { + best_move = {swapped, + route_x, + route_y, + node_x, + predecessor_y, + successor_y, + node_y, + best_insertion_y->predecessor, + best_insertion_y->successor, + split_load}; + } +} + +void SdSwapStar(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, SdSwapStarMove &best_move, + Delta &best_delta, Random &random) { + PreprocessStarInsertions(problem, distance_matrix, solution, context, route_x, + random); + PreprocessStarInsertions(problem, distance_matrix, solution, context, route_y, + random); + Node node_x = context.heads[route_x]; + while (node_x) { + Load load_x = solution.load(node_x); + Node node_y = context.heads[route_y]; + while (node_y) { + Load load_y = solution.load(node_y); + if (load_x > load_y) { + SdSwapStar(problem, distance_matrix, solution, context, false, route_x, + route_y, node_x, node_y, load_x - load_y, best_move, + best_delta, random); + } else if (load_y > load_x) { + SdSwapStar(problem, distance_matrix, solution, context, true, route_y, + route_x, node_y, node_x, load_y - load_x, best_move, + best_delta, random); + } + node_y = solution.successor(node_y); + } + node_x = solution.successor(node_x); + } +} + +bool SdSwapStar(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SdSwapStarMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = route_x + 1; route_y < context.num_routes; ++route_y) { + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + SdSwapStar(problem, distance_matrix, solution, context, route_x, + route_y, move, cache.delta, random); + } else { + if (!move.swapped) { + move.route_x = route_x; + move.route_y = route_y; + } else { + move.route_x = route_y; + move.route_y = route_x; + } + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSdSwapStar(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/sd_swap_two_one.cc b/src/sdvrp/splitils/algorithm/operator/sd_swap_two_one.cc new file mode 100644 index 0000000..35a2954 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/sd_swap_two_one.cc @@ -0,0 +1,272 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/model/route_head_guard.h" +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp::splitils { + +struct SdSwapTwoOneMove { + bool type; + Node route_ij, route_k; + Node predecessor_ij, successor_ij; + Node node_i, node_j, node_k; + int split_load; + bool direction_ij, direction_ijk; +}; + +void DoSdSwapTwoOne(const SdSwapTwoOneMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_k = solution.predecessor(move.node_k); + Node successor_k = solution.successor(move.node_k); + { + RouteHeadGuard guard(solution, context, move.route_ij); + solution.Link(move.predecessor_ij, move.successor_ij); + } + { + RouteHeadGuard guard(solution, context, move.route_k); + solution.Link(predecessor_k, successor_k); + } + if (move.type == 0) { + int new_node_j = + solution.NewNode(solution.customer(move.node_j), move.split_load); + solution.set_load(move.node_j, + solution.load(move.node_j) - move.split_load); + { + RouteHeadGuard guard(solution, context, move.route_ij); + if (move.direction_ijk) { + solution.Link(move.predecessor_ij, new_node_j); + solution.Link(new_node_j, move.node_k); + solution.Link(move.node_k, move.successor_ij); + } else { + solution.Link(move.predecessor_ij, move.node_k); + solution.Link(move.node_k, new_node_j); + solution.Link(new_node_j, move.successor_ij); + } + } + { + RouteHeadGuard guard(solution, context, move.route_k); + if (move.direction_ij) { + solution.Link(predecessor_k, move.node_i); + solution.Link(move.node_i, move.node_j); + solution.Link(move.node_j, successor_k); + } else { + solution.Link(predecessor_k, move.node_j); + solution.Link(move.node_j, move.node_i); + solution.Link(move.node_i, successor_k); + } + } + } else { + int new_node_k = + solution.NewNode(solution.customer(move.node_k), move.split_load); + solution.set_load(move.node_k, + solution.load(move.node_k) - move.split_load); + { + RouteHeadGuard guard(solution, context, move.route_ij); + solution.Link(move.predecessor_ij, move.node_k); + solution.Link(move.node_k, move.successor_ij); + } + { + RouteHeadGuard guard(solution, context, move.route_k); + int before_ij; + int after_ij; + if (move.direction_ij) { + solution.Link(predecessor_k, move.node_i); + solution.Link(move.node_i, move.node_j); + solution.Link(move.node_j, successor_k); + before_ij = move.node_i; + after_ij = move.node_j; + } else { + solution.Link(predecessor_k, move.node_j); + solution.Link(move.node_j, move.node_i); + solution.Link(move.node_i, successor_k); + before_ij = move.node_j; + after_ij = move.node_i; + } + if (move.direction_ijk) { + solution.Link(after_ij, new_node_k); + solution.Link(new_node_k, successor_k); + } else { + solution.Link(predecessor_k, new_node_k); + solution.Link(new_node_k, before_ij); + } + } + } +} + +inline void SdSwapTwoOne0(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_ij, Node route_k, Node node_i, Node node_j, + Node node_k, Node predecessor_ij, Node successor_ij, + int split_load, int base_delta, + SdSwapTwoOneMove &best_move, Delta &best_delta, + Random &random) { + Node predecessor_k = solution.predecessor(node_k); + Node successor_k = solution.successor(node_k); + int delta_ij = distance_matrix[solution.customer(predecessor_k)] + [solution.customer(node_i)] + + distance_matrix[solution.customer(node_j)] + [solution.customer(successor_k)]; + int delta_ji = distance_matrix[solution.customer(predecessor_k)] + [solution.customer(node_j)] + + distance_matrix[solution.customer(node_i)] + [solution.customer(successor_k)]; + int delta_jk = distance_matrix[solution.customer(predecessor_ij)] + [solution.customer(node_j)] + + distance_matrix[solution.customer(node_k)] + [solution.customer(successor_ij)]; + int delta_kj = distance_matrix[solution.customer(predecessor_ij)] + [solution.customer(node_k)] + + distance_matrix[solution.customer(node_j)] + [solution.customer(successor_ij)]; + bool direction_ij = true; + if (delta_ij > delta_ji) { + delta_ij = delta_ji; + direction_ij = false; + } + bool direction_jk = true; + if (delta_jk > delta_kj) { + delta_jk = delta_kj; + direction_jk = false; + } + int delta = + base_delta + + distance_matrix[solution.customer(node_j)][solution.customer(node_k)] + + delta_ij + delta_jk; + if (best_delta.Update(delta, random)) { + best_move = {0, route_ij, route_k, predecessor_ij, successor_ij, + node_i, node_j, node_k, split_load, direction_ij, + direction_jk}; + } +} + +inline void SdSwapTwoOne1(const Problem &problem, + const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_ij, Node route_k, Node node_i, Node node_j, + Node node_k, Node predecessor_ij, Node successor_ij, + int split_load, int base_delta, + SdSwapTwoOneMove &best_move, Delta &best_delta, + Random &random) { + Node predecessor_k = solution.predecessor(node_k); + Node successor_k = solution.successor(node_k); + base_delta += distance_matrix[solution.customer(predecessor_ij)] + [solution.customer(node_k)] + + distance_matrix[solution.customer(node_k)] + [solution.customer(successor_ij)]; + for (bool direction_ij : {true, false}) { + int before_ij = node_i; + int after_ij = node_j; + if (!direction_ij) { + std::swap(before_ij, after_ij); + } + for (bool direction_ijk : {true, false}) { + int delta_ijk; + if (direction_ijk) { + delta_ijk = distance_matrix[solution.customer(predecessor_k)] + [solution.customer(before_ij)] + + distance_matrix[solution.customer(after_ij)] + [solution.customer(node_k)] + + distance_matrix[solution.customer(node_k)] + [solution.customer(successor_k)]; + } else { + delta_ijk = distance_matrix[solution.customer(predecessor_k)] + [solution.customer(node_k)] + + distance_matrix[solution.customer(node_k)] + [solution.customer(before_ij)] + + distance_matrix[solution.customer(after_ij)] + [solution.customer(successor_k)]; + } + int delta = base_delta + delta_ijk; + if (best_delta.Update(delta, random)) { + best_move = { + 1, route_ij, route_k, predecessor_ij, successor_ij, node_i, + node_j, node_k, split_load, direction_ij, direction_ijk}; + } + } + } +} + +void SdSwapTwoOne(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_ij, Node route_k, SdSwapTwoOneMove &best_move, + Delta &best_delta, Random &random) { + Node node_i = context.heads[route_ij]; + Node node_j = solution.successor(node_i); + while (node_j) { + Load load_i = solution.load(node_i); + Load load_j = solution.load(node_j); + for (Node node_k = context.heads[route_k]; node_k; + node_k = solution.successor(node_k)) { + Load load_k = solution.load(node_k); + int predecessor_ij = solution.predecessor(node_i); + int successor_ij = solution.successor(node_j); + int base_delta = + -distance_matrix[solution.customer(predecessor_ij)] + [solution.customer(node_i)] - + distance_matrix[solution.customer(node_j)] + [solution.customer(successor_ij)] - + distance_matrix[solution.customer(solution.predecessor(node_k))] + [solution.customer(node_k)] - + distance_matrix[solution.customer(node_k)] + [solution.customer(solution.successor(node_k))]; + if (load_i + load_j > load_k) { + if (load_i < load_k) { + SdSwapTwoOne0(problem, distance_matrix, solution, context, route_ij, + route_k, node_i, node_j, node_k, predecessor_ij, + successor_ij, load_i + load_j - load_k, base_delta, + best_move, best_delta, random); + } + if (load_j < load_k) { + SdSwapTwoOne0(problem, distance_matrix, solution, context, route_ij, + route_k, node_j, node_i, node_k, predecessor_ij, + successor_ij, load_i + load_j - load_k, base_delta, + best_move, best_delta, random); + } + } else if (load_k > load_i + load_j) { + SdSwapTwoOne1(problem, distance_matrix, solution, context, route_ij, + route_k, node_i, node_j, node_k, predecessor_ij, + successor_ij, load_k - load_i - load_j, base_delta, + best_move, best_delta, random); + } + } + node_i = node_j; + node_j = solution.successor(node_j); + } +} + +bool SdSwapTwoOne(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SdSwapTwoOneMove best_move{}; + Delta best_delta; + for (Node route_ij = 0; route_ij < context.num_routes; ++route_ij) { + for (Node route_k = 0; route_k < context.num_routes; ++route_k) { + if (route_ij == route_k) { + continue; + } + auto &cache = operator_caches.Get(route_ij, route_k); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + SdSwapTwoOne(problem, distance_matrix, solution, context, route_ij, + route_k, move, cache.delta, random); + } else { + move.route_ij = route_ij; + move.route_k = route_k; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSdSwapTwoOne(best_move, solution, context); + associated_routes.insert(best_move.route_ij); + associated_routes.insert(best_move.route_k); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/split_reinsertion.cc b/src/sdvrp/splitils/algorithm/operator/split_reinsertion.cc new file mode 100644 index 0000000..bad906a --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/split_reinsertion.cc @@ -0,0 +1,74 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include + +#include "sdvrp/splitils/util/utils.h" + +namespace vrp::sdvrp::splitils { + +struct SplitReinsertionMove { + InsertionWithCost insertion; + int residual; + SplitReinsertionMove(const InsertionWithCost &insertion, int residual) + : insertion(insertion), residual(residual) {} +}; + +int SplitReinsertion(const Problem &problem, + const DistanceMatrix &distance_matrix, Node customer, + int demand, double blink_rate, Solution &solution, + RouteContext &context, std::set &associated_routes, + Random &random) { + auto func = [&](Node predecessor, Node successor, Node customer) { + Node pre_customer = solution.customer(predecessor); + Node suc_customer = solution.customer(successor); + return distance_matrix[customer][pre_customer] + + distance_matrix[customer][suc_customer] - + distance_matrix[pre_customer][suc_customer]; + }; + std::vector moves; + moves.reserve(context.num_routes); + int sum_residual = 0; + for (Node route_index = 0; route_index < context.num_routes; ++route_index) { + int residual = + std::min(demand, problem.capacity - context.route_loads[route_index]); + if (residual > 0) { + auto insertion = CalcBestInsertion(solution, func, context, route_index, + customer, random); + moves.emplace_back(insertion, residual); + sum_residual += residual; + } + } + if (sum_residual < demand) { + return -1; + } + std::stable_sort( + moves.begin(), moves.end(), + [](const SplitReinsertionMove &lhs, const SplitReinsertionMove &rhs) { + return lhs.insertion.cost.value * rhs.residual < + rhs.insertion.cost.value * lhs.residual; + }); + int delta = 0; + for (const auto &move : moves) { + sum_residual -= move.residual; + if (sum_residual >= demand && random.NextFloat() < blink_rate) { + continue; + } + Load load = std::min(demand, move.residual); + delta += move.insertion.cost.value; + Node node_index = solution.Insert( + customer, load, move.insertion.predecessor, move.insertion.successor); + if (move.insertion.predecessor == 0) { + context.heads[move.insertion.route_index] = node_index; + } + UpdateRouteContext(solution, move.insertion.route_index, + move.insertion.predecessor, context); + associated_routes.insert(move.insertion.route_index); + demand -= load; + if (demand == 0) { + break; + } + } + return delta; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/swap.cc b/src/sdvrp/splitils/algorithm/operator/swap.cc new file mode 100644 index 0000000..38e0758 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/swap.cc @@ -0,0 +1,239 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp::splitils { + +struct SwapMove { + Node route_x, route_y; + int direction_x, direction_y; + Node left_x, left_y; + Node right_x, right_y; +}; + +inline void SegmentInsertion(Solution &solution, RouteContext &context, + Node left, Node right, Node predecessor, + Node successor, Node route_index, int direction) { + if (direction) { + ReversedLink(solution, left, right, predecessor, successor); + } else { + solution.Link(predecessor, left); + solution.Link(right, successor); + } + if (predecessor == 0) { + context.heads[route_index] = direction ? right : left; + } +} + +inline void DoSwap(SwapMove &move, Solution &solution, RouteContext &context) { + if (move.direction_y == -1) { + Node predecessor = solution.predecessor(move.left_x); + Node successor = solution.successor(move.right_x); + solution.Link(predecessor, successor); + if (predecessor == 0) { + context.heads[move.route_x] = successor; + } + SegmentInsertion(solution, context, move.left_x, move.right_x, move.left_y, + move.right_y, move.route_y, move.direction_x); + } else { + Node predecessor_x = solution.predecessor(move.left_x); + Node successor_x = solution.successor(move.right_x); + Node predecessor_y = solution.predecessor(move.left_y); + Node successor_y = solution.successor(move.right_y); + SegmentInsertion(solution, context, move.left_x, move.right_x, + predecessor_y, successor_y, move.route_y, + move.direction_x); + SegmentInsertion(solution, context, move.left_y, move.right_y, + predecessor_x, successor_x, move.route_x, + move.direction_y); + } +} + +inline void UpdateShift(const DistanceMatrix &distance_matrix, + const Solution &solution, Node route_x, Node route_y, + Node left, Node right, Node predecessor, Node successor, + Node base_x, SwapMove &best_move, + Delta &best_delta, Random &random) { + Node customer_left = solution.customer(left); + Node customer_predecessor = solution.customer(predecessor); + Node customer_right = solution.customer(right); + Node customer_successor = solution.customer(successor); + int d1 = distance_matrix[customer_left][customer_predecessor] + + distance_matrix[customer_right][customer_successor]; + int d2 = distance_matrix[customer_left][customer_successor] + + distance_matrix[customer_right][customer_predecessor]; + int direction = d1 >= d2; + int delta = base_x + (direction ? d2 : d1) - + distance_matrix[customer_predecessor][customer_successor]; + if (best_delta.Update(delta, random)) { + best_move = {route_x, route_y, direction, -1, + left, predecessor, right, successor}; + } +} + +inline void UpdateSwap(const DistanceMatrix &distance_matrix, + const Solution &solution, Node route_x, Node route_y, + Node left_x, Node right_x, Node left_y, Node right_y, + int base_x, SwapMove &best_move, Delta &best_delta, + Random &random) { + Node customer_left_x = solution.customer(left_x); + Node customer_right_x = solution.customer(right_x); + Node customer_left_y = solution.customer(left_y); + Node customer_right_y = solution.customer(right_y); + Node predecessor_x = solution.customer(solution.predecessor(left_x)); + Node successor_x = solution.customer(solution.successor(right_x)); + Node predecessor_y = solution.customer(solution.predecessor(left_y)); + Node successor_y = solution.customer(solution.successor(right_y)); + int d1 = distance_matrix[customer_left_x][predecessor_y] + + distance_matrix[customer_right_x][successor_y]; + int d2 = distance_matrix[customer_left_x][successor_y] + + distance_matrix[customer_right_x][predecessor_y]; + int d3 = distance_matrix[customer_left_y][predecessor_x] + + distance_matrix[customer_right_y][successor_x]; + int d4 = distance_matrix[customer_left_y][successor_x] + + distance_matrix[customer_right_y][predecessor_x]; + int direction_x = d1 >= d2; + int direction_y = d3 >= d4; + int delta = base_x + (direction_x ? d2 : d1) + (direction_y ? d4 : d3) - + distance_matrix[customer_left_y][predecessor_y] - + distance_matrix[customer_right_y][successor_y]; + if (best_delta.Update(delta, random)) { + best_move = {route_x, route_y, direction_x, direction_y, + left_x, left_y, right_x, right_y}; + } +} + +template +void Swap(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, Node route_x, Node route_y, + SwapMove &best_move, Delta &best_delta, Random &random) { + Node left_x = context.heads[route_x]; + Load load_x = solution.load(left_x); + Node right_x = left_x; + for (int i = 1; right_x && i < num_x; ++i) { + right_x = solution.successor(right_x); + load_x += solution.load(right_x); + } + while (right_x) { + int base_x = + -distance_matrix[solution.customer(left_x)] + [solution.customer(solution.predecessor(left_x))] - + distance_matrix[solution.customer(right_x)] + [solution.customer(solution.successor(right_x))]; + if (num_y == 0) { + base_x += distance_matrix[solution.customer(solution.predecessor(left_x))] + [solution.customer(solution.successor(right_x))]; + } + Load load_y_lower = + -problem.capacity + context.route_loads[route_y] + load_x; + if (num_y == 0) { + if (load_y_lower <= 0) { + Node predecessor = 0; + Node successor = context.heads[route_y]; + while (true) { + UpdateShift(distance_matrix, solution, route_x, route_y, left_x, + right_x, predecessor, successor, base_x, best_move, + best_delta, random); + if (!successor) { + break; + } + predecessor = successor; + successor = solution.successor(successor); + } + } + } else { + Load load_y_upper = + problem.capacity - context.route_loads[route_x] + load_x; + Node left_y = context.heads[route_y]; + Load load_y = solution.load(left_y); + Node right_y = left_y; + for (int i = 1; right_y && i < num_y; ++i) { + right_y = solution.successor(right_y); + load_y += solution.load(right_y); + } + while (right_y) { + if (load_y >= load_y_lower && load_y <= load_y_upper) { + UpdateSwap(distance_matrix, solution, route_x, route_y, left_x, + right_x, left_y, right_y, base_x, best_move, best_delta, + random); + } + load_y -= solution.load(left_y); + left_y = solution.successor(left_y); + right_y = solution.successor(right_y); + load_y += solution.load(right_y); + } + } + load_x -= solution.load(left_x); + left_x = solution.successor(left_x); + right_x = solution.successor(right_x); + load_x += solution.load(right_x); + } +} + +template +bool Swap(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SwapMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = num_x != num_y ? 0 : route_x + 1; + route_y < context.num_routes; ++route_y) { + if (num_x != num_y && route_x == route_y) { + continue; + } + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + Swap(problem, distance_matrix, solution, context, route_x, + route_y, move, cache.delta, random); + } else { + move.route_x = route_x; + move.route_y = route_y; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSwap(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +template bool Swap<1, 0>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +template bool Swap<2, 0>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +template bool Swap<1, 1>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +template bool Swap<2, 1>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +template bool Swap<2, 2>(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches); + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/swap_star.cc b/src/sdvrp/splitils/algorithm/operator/swap_star.cc new file mode 100644 index 0000000..257fdb7 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/swap_star.cc @@ -0,0 +1,122 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/algorithm/base_star.h" +#include "sdvrp/splitils/model/route_head_guard.h" + +namespace vrp::sdvrp::splitils { + +struct SwapStarMove { + Node route_x, route_y; + Node node_x, predecessor_x, successor_x; + Node node_y, predecessor_y, successor_y; +}; + +inline void DoSwapStar(SwapStarMove &move, Solution &solution, + RouteContext &context) { + Node predecessor_x = solution.predecessor(move.node_x); + Node successor_x = solution.successor(move.node_x); + Node predecessor_y = solution.predecessor(move.node_y); + Node successor_y = solution.successor(move.node_y); + { + RouteHeadGuard guard(solution, context, move.route_x); + solution.Link(predecessor_x, successor_x); + solution.Link(move.predecessor_y, move.node_y); + solution.Link(move.node_y, move.successor_y); + } + { + RouteHeadGuard guard(solution, context, move.route_y); + solution.Link(predecessor_y, successor_y); + solution.Link(move.predecessor_x, move.node_x); + solution.Link(move.node_x, move.successor_x); + } +} + +void SwapStar(const Problem &problem, const DistanceMatrix &distance_matrix, + const Solution &solution, const RouteContext &context, + Node route_x, Node route_y, SwapStarMove &best_move, + Delta &best_delta, Random &random) { + PreprocessStarInsertions(problem, distance_matrix, solution, context, route_x, + random); + PreprocessStarInsertions(problem, distance_matrix, solution, context, route_y, + random); + Node node_x = context.heads[route_x]; + while (node_x) { + auto &&insertion_x = star_caches[route_y][solution.customer(node_x)]; + Load load_x = solution.load(node_x); + Load load_y_lower = + -problem.capacity + context.route_loads[route_y] + load_x; + Load load_y_upper = + problem.capacity - context.route_loads[route_x] + load_x; + Node node_y = context.heads[route_y]; + while (node_y) { + Load load_y = solution.load(node_y); + if (load_y >= load_y_lower && load_y <= load_y_upper) { + auto &&insertion_y = star_caches[route_x][solution.customer(node_y)]; + Node predecessor_x = solution.predecessor(node_x); + Node successor_x = solution.successor(node_x); + Node predecessor_y = solution.predecessor(node_y); + Node successor_y = solution.successor(node_y); + int delta = -CalcDelta(distance_matrix, solution, node_x, predecessor_x, + successor_x) - + CalcDelta(distance_matrix, solution, node_y, predecessor_y, + successor_y); + int delta_x = CalcDelta(distance_matrix, solution, node_x, + predecessor_y, successor_y); + int delta_y = CalcDelta(distance_matrix, solution, node_y, + predecessor_x, successor_x); + auto best_insertion_x = insertion_x.FindBestWithoutNode(node_y); + if (best_insertion_x && best_insertion_x->delta.value < delta_x) { + delta_x = best_insertion_x->delta.value; + predecessor_y = best_insertion_x->predecessor; + successor_y = best_insertion_x->successor; + } + auto best_insertion_y = insertion_y.FindBestWithoutNode(node_x); + if (best_insertion_y && best_insertion_y->delta.value < delta_y) { + delta_y = best_insertion_y->delta.value; + predecessor_x = best_insertion_y->predecessor; + successor_x = best_insertion_y->successor; + } + delta += delta_x + delta_y; + if (best_delta.Update(delta, random)) { + best_move = {route_x, route_y, node_x, predecessor_y, + successor_y, node_y, predecessor_x, successor_x}; + } + } + node_y = solution.successor(node_y); + } + node_x = solution.successor(node_x); + } +} + +bool SwapStar(const Problem &problem, const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + std::set &associated_routes, Random &random, + OperatorCaches &operator_caches) { + SwapStarMove best_move{}; + Delta best_delta; + for (Node route_x = 0; route_x < context.num_routes; ++route_x) { + for (Node route_y = route_x + 1; route_y < context.num_routes; ++route_y) { + auto &cache = operator_caches.Get(route_x, route_y); + auto &move = *reinterpret_cast(cache.move); + if (!cache.TryReuse()) { + SwapStar(problem, distance_matrix, solution, context, route_x, route_y, + move, cache.delta, random); + } else { + move.route_x = route_x; + move.route_y = route_y; + } + if (best_delta.Update(cache.delta, random)) { + best_move = move; + } + } + } + if (best_delta.value < 0) { + DoSwapStar(best_move, solution, context); + associated_routes.insert(best_move.route_x); + associated_routes.insert(best_move.route_y); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operator/two_opt.cc b/src/sdvrp/splitils/algorithm/operator/two_opt.cc new file mode 100644 index 0000000..9f289c3 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operator/two_opt.cc @@ -0,0 +1,64 @@ +#include "sdvrp/splitils/algorithm/operators.h" + +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp::splitils { + +struct TwoOptMove { + Node head; + Node tail; +}; + +void DoTwoOpt(const TwoOptMove &move, Node route_index, Solution &solution, + RouteContext &context) { + Node predecessor_head = solution.predecessor(move.head); + Node successor_tail = solution.successor(move.tail); + ReversedLink(solution, move.head, move.tail, predecessor_head, + successor_tail); + if (!predecessor_head) { + context.heads[route_index] = move.tail; + } + UpdateRouteContext(solution, route_index, predecessor_head, context); +} + +void TwoOpt(const DistanceMatrix &distance_matrix, const Solution &solution, + Node head, Node tail, TwoOptMove &best_move, Delta &best_delta, + Random &random) { + Node predecessor_head = solution.predecessor(head); + Node successor_tail = solution.successor(tail); + int delta = distance_matrix[solution.customer(predecessor_head)] + [solution.customer(tail)] + + distance_matrix[solution.customer(head)] + [solution.customer(successor_tail)] - + distance_matrix[solution.customer(predecessor_head)] + [solution.customer(head)] - + distance_matrix[solution.customer(tail)] + [solution.customer(successor_tail)]; + if (best_delta.Update(delta, random)) { + best_move = {head, tail}; + } +} + +bool TwoOpt(const Problem &problem, const DistanceMatrix &distance_matrix, + Node route_index, Solution &solution, RouteContext &context, + Random &random) { + TwoOptMove best_move{}; + Delta best_delta; + Node head = context.heads[route_index]; + while (head) { + Node tail = solution.successor(head); + while (tail) { + TwoOpt(distance_matrix, solution, head, tail, best_move, best_delta, + random); + tail = solution.successor(tail); + } + head = solution.successor(head); + } + if (best_delta.value < 0) { + DoTwoOpt(best_move, route_index, solution, context); + return true; + } + return false; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/operators.h b/src/sdvrp/splitils/algorithm/operators.h new file mode 100644 index 0000000..edfd037 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/operators.h @@ -0,0 +1,108 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_OPERATORS_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_OPERATORS_H_ + +#include +#include +#include + +#include + +#include "sdvrp/distance_matrix.h" +#include "sdvrp/splitils/model/operator_cache.h" +#include "sdvrp/splitils/model/route_context.h" +#include "sdvrp/splitils/model/solution.h" +#include "util/random.h" + +namespace vrp::sdvrp::splitils { + +using InterOperatorFunction = bool(const Problem &, const DistanceMatrix &, + Solution &, RouteContext &, std::set &, + Random &, OperatorCaches &); + +template InterOperatorFunction Swap; +InterOperatorFunction Relocate; +InterOperatorFunction SwapStar; +InterOperatorFunction Cross; +InterOperatorFunction SdSwapStar; +InterOperatorFunction SdSwapOneIn; +InterOperatorFunction SdSwapOneOne; +InterOperatorFunction SdSwapOneOut; +InterOperatorFunction SdSwapTwoOne; +InterOperatorFunction RouteAddition; +InterOperatorFunction KSplit; + +static const std::map + kInterOperatorFunctionMap = {{"Swap<1, 0>", Swap<1, 0>}, + {"Swap<2, 0>", Swap<2, 0>}, + {"Swap<1, 1>", Swap<1, 1>}, + {"Swap<2, 1>", Swap<2, 1>}, + {"Swap<2, 2>", Swap<2, 2>}, + {"Relocate", Relocate}, + {"SwapStar", SwapStar}, + {"Cross", Cross}, + {"SdSwapStar", SdSwapStar}, + {"SdSwapOneIn", SdSwapOneIn}, + {"SdSwapOneOne", SdSwapOneOne}, + {"SdSwapOneOut", SdSwapOneOut}, + {"SdSwapTwoOne", SdSwapTwoOne}, + {"RouteAddition", RouteAddition}, + {"KSplit", KSplit}}; + +static const std::map kInterOperatorCacheMap = { + {Swap<1, 0>, true}, {Swap<2, 0>, true}, {Swap<1, 1>, true}, + {Swap<2, 1>, true}, {Swap<2, 2>, true}, {Relocate, true}, + {SwapStar, true}, {Cross, true}, {SdSwapStar, true}, + {SdSwapOneIn, true}, {SdSwapOneOne, true}, {SdSwapOneOut, true}, + {SdSwapTwoOne, true}, {RouteAddition, false}, {KSplit, false}}; + +using IntraOperatorFunction = bool(const Problem &, const DistanceMatrix &, + Node, Solution &, RouteContext &, Random &); + +IntraOperatorFunction TwoOpt; +IntraOperatorFunction Exchange; +template IntraOperatorFunction OrOpt; + +static const std::map + kIntraOperatorFunctionMap = {{"TwoOpt", TwoOpt}, + {"Exchange", Exchange}, + {"OrOpt<1>", OrOpt<1>}, + {"OrOpt<2>", OrOpt<2>}, + {"OrOpt<3>", OrOpt<3>}}; + +int SplitReinsertion(const Problem &problem, + const DistanceMatrix &distance_matrix, Node customer, + int demand, double blink_rate, Solution &solution, + RouteContext &context, std::set &associated_routes, + Random &random); + +void Repair(const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context); + +} // namespace vrp::sdvrp::splitils + +namespace nlohmann { +using vrp::sdvrp::splitils::InterOperatorFunction; +template <> struct adl_serializer> { + static void from_json(const json &j, + std::vector &functions) { + for (auto &&name : j) { + functions.emplace_back( + vrp::sdvrp::splitils::kInterOperatorFunctionMap.at(name)); + } + } + static void to_json(json &, const std::vector &) {} +}; +using vrp::sdvrp::splitils::IntraOperatorFunction; +template <> struct adl_serializer> { + static void from_json(const json &j, + std::vector &functions) { + for (auto &&name : j) { + functions.emplace_back( + vrp::sdvrp::splitils::kIntraOperatorFunctionMap.at(name)); + } + } + static void to_json(json &, const std::vector &) {} +}; +}; // namespace nlohmann + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_OPERATORS_H_ diff --git a/src/sdvrp/splitils/algorithm/ruin_method.cc b/src/sdvrp/splitils/algorithm/ruin_method.cc new file mode 100644 index 0000000..555adab --- /dev/null +++ b/src/sdvrp/splitils/algorithm/ruin_method.cc @@ -0,0 +1,125 @@ +#include "sdvrp/splitils/algorithm/ruin_method.h" + +#include +#include +#include +#include + +#include "util/utils.h" + +namespace vrp::sdvrp::splitils { + +RandomRuin::RandomRuin(std::vector num_perturb_customers) + : num_perturb_customers_(std::move(num_perturb_customers)) {} + +std::vector RandomRuin::Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) { + int num_perturb = num_perturb_customers_[random.NextInt( + 0, static_cast(num_perturb_customers_.size()) - 1)]; + std::vector customers(problem.num_customers - 1); + std::iota(customers.begin(), customers.end(), 1); + Shuffle(customers.begin(), customers.end(), random); + customers.erase(customers.begin() + num_perturb, customers.end()); + return customers; +} + +SisrsRuin::SisrsRuin(int average_customers, int max_length, double split_rate, + double preserved_probability) + : average_customers_(average_customers), max_length_(max_length), + split_rate_(split_rate), preserved_probability_(preserved_probability) {} + +std::vector SisrsRuin::Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) { + double average_length = + static_cast(problem.num_customers - 1) / context.num_routes; + double max_length = + std::min(static_cast(max_length_), average_length); + double max_strings = 4.0 * average_customers_ / (1 + max_length_) - 1; + int num_strings = static_cast(random.NextFloat() * max_strings) + 1; + int customer_seed = random.NextInt(1, problem.num_customers - 1); + std::vector node_indices(solution.node_pool(), + solution.node_pool() + solution.num_nodes()); + auto &&seed_distances = distance_matrix[customer_seed]; + std::stable_sort(node_indices.begin(), node_indices.end(), + [&](Node lhs, Node rhs) { + return seed_distances[solution.customer(lhs)] < + seed_distances[solution.customer(rhs)]; + }); + std::set visited_heads; + std::vector route; + std::vector customer_indices; + for (Node node_index : node_indices) { + if (visited_heads.size() >= num_strings) { + break; + } + int position; + int head = GetRouteHead(solution, node_index, position); + if (!visited_heads.insert(head).second) { + continue; + } + GetRoute(solution, head, route); + int route_length = route.size(); + double max_ruin_length = + std::min(static_cast(route_length), max_length); + int ruin_length = + static_cast(random.NextFloat() * max_ruin_length) + 1; + int num_preserved = 0; + int preserved_start_position = -1; + if (ruin_length >= 2 && ruin_length < route_length && + random.NextFloat() < split_rate_) { + while (ruin_length < route_length) { + if (random.NextFloat() < preserved_probability_) { + break; + } + ++num_preserved; + ++ruin_length; + } + preserved_start_position = + random.NextInt(1, ruin_length - num_preserved - 1); + } + int min_start_position = std::max(0, position - ruin_length + 1); + int max_start_position = std::min(route_length - ruin_length, position); + int start_position = random.NextInt(min_start_position, max_start_position); + for (int j = 0; j < ruin_length; ++j) { + if (j < preserved_start_position || + j >= preserved_start_position + num_preserved) { + customer_indices.emplace_back( + solution.customer(route[start_position + j])); + } + } + } + std::sort(customer_indices.begin(), customer_indices.end()); + customer_indices.erase( + std::unique(customer_indices.begin(), customer_indices.end()), + customer_indices.end()); + Shuffle(customer_indices.begin(), customer_indices.end(), random); + return customer_indices; +} + +Node SisrsRuin::GetRouteHead(Solution &solution, Node node_index, + int &position) { + position = 0; + while (true) { + Node predecessor = solution.predecessor(node_index); + if (!predecessor) { + return node_index; + } + node_index = predecessor; + ++position; + } +} + +void SisrsRuin::GetRoute(const Solution &solution, Node head, + std::vector &route) { + route.clear(); + while (head) { + route.emplace_back(head); + head = solution.successor(head); + } +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/ruin_method.h b/src/sdvrp/splitils/algorithm/ruin_method.h new file mode 100644 index 0000000..0c3c405 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/ruin_method.h @@ -0,0 +1,87 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_RUIN_METHOD_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_RUIN_METHOD_H_ + +#include +#include +#include + +#include + +#include "sdvrp/distance_matrix.h" +#include "sdvrp/problem.h" +#include "sdvrp/splitils/model/route_context.h" +#include "sdvrp/splitils/model/solution.h" +#include "util/random.h" + +namespace vrp::sdvrp::splitils { + +class RuinMethod { +public: + virtual ~RuinMethod() = default; + virtual std::vector Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) = 0; +}; + +class RandomRuin : public RuinMethod { +public: + explicit RandomRuin(std::vector num_perturb_customers); + std::vector Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) override; + +private: + std::vector num_perturb_customers_; +}; + +class SisrsRuin : public RuinMethod { +public: + SisrsRuin(int average_customers, int max_length, double split_rate, + double preserved_probability); + std::vector Ruin(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution, RouteContext &context, + Random &random) override; + +private: + static Node GetRouteHead(Solution &solution, Node node_index, int &position); + static void GetRoute(const Solution &solution, Node head, + std::vector &route); + int average_customers_; + int max_length_; + double split_rate_; + double preserved_probability_; +}; + +} // namespace vrp::sdvrp::splitils + +namespace nlohmann { +template <> +struct adl_serializer> { + static void + from_json(const json &j, + std::unique_ptr &ruin_method) { + if (j["type"].get() == "SISRs") { + auto average_customers = j["average_customers"].get(); + auto max_length = j["max_length"].get(); + auto split_rate = j["split_rate"].get(); + auto preserved_probability = j["preserved_probability"].get(); + ruin_method = std::make_unique( + average_customers, max_length, split_rate, preserved_probability); + } else { + auto num_perturb_customers = + j["num_perturb_customers"].get>(); + ruin_method = std::make_unique( + num_perturb_customers); + } + } + + static void + to_json(json &, const std::unique_ptr &) {} +}; + +} // namespace nlohmann + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_RUIN_METHOD_H_ diff --git a/src/sdvrp/splitils/algorithm/sorter.cc b/src/sdvrp/splitils/algorithm/sorter.cc new file mode 100644 index 0000000..67ba5db --- /dev/null +++ b/src/sdvrp/splitils/algorithm/sorter.cc @@ -0,0 +1,68 @@ +#include "sdvrp/splitils/algorithm/sorter.h" + +#include +#include +#include + +#include "util/utils.h" + +namespace vrp::sdvrp::splitils { + +void Sorter::AddSortFunction(SortFunction *sort_function, double weight) { + sum_weights_ += weight; + sort_functions_.emplace_back(sort_function, weight); +} + +void Sorter::Sort(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) const { + double r = random.NextFloat() * sum_weights_; + for (auto &&[sort_function, weight] : sort_functions_) { + r -= weight; + if (r < 0) { + sort_function(problem, distance_matrix, customers, random); + return; + } + } +} + +void SortByRandom(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) { + Shuffle(customers.begin(), customers.end(), random); +} + +void SortByDemand(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) { + std::stable_sort(customers.begin(), customers.end(), [&](Node lhs, Node rhs) { + return problem.customers[lhs].demand > problem.customers[rhs].demand; + }); +} + +void SortByFar(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) { + std::stable_sort(customers.begin(), customers.end(), [&](Node lhs, Node rhs) { + return distance_matrix[0][lhs] > distance_matrix[0][rhs]; + }); +} + +void SortByClose(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) { + std::stable_sort(customers.begin(), customers.end(), [&](Node lhs, Node rhs) { + return distance_matrix[0][lhs] < distance_matrix[0][rhs]; + }); +} + +static const std::map kSortFunctionMap = { + {"random", SortByRandom}, + {"demand", SortByDemand}, + {"far", SortByFar}, + {"close", SortByClose}}; + +} // namespace vrp::sdvrp::splitils + +void nlohmann::adl_serializer::from_json( + const json &j, vrp::sdvrp::splitils::Sorter &sorter) { + for (auto &[key, value] : j.items()) { + sorter.AddSortFunction(vrp::sdvrp::splitils::kSortFunctionMap.at(key), + value); + } +} diff --git a/src/sdvrp/splitils/algorithm/sorter.h b/src/sdvrp/splitils/algorithm/sorter.h new file mode 100644 index 0000000..f21b08c --- /dev/null +++ b/src/sdvrp/splitils/algorithm/sorter.h @@ -0,0 +1,41 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_SORTER_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_SORTER_H_ + +#include +#include +#include + +#include + +#include "sdvrp/distance_matrix.h" +#include "sdvrp/problem.h" +#include "util/random.h" + +namespace vrp::sdvrp::splitils { + +using SortFunction = void(const Problem &, const DistanceMatrix &, + std::vector &, Random &); + +class Sorter { +public: + void AddSortFunction(SortFunction *sort_function, double weight); + void Sort(const Problem &problem, const DistanceMatrix &distance_matrix, + std::vector &customers, Random &random) const; + +private: + double sum_weights_ = 0; + std::vector> sort_functions_; +}; + +} // namespace vrp::sdvrp::splitils + +namespace nlohmann { + +template <> struct adl_serializer { + static void from_json(const json &j, vrp::sdvrp::splitils::Sorter &sorter); + static void to_json(json &, const vrp::sdvrp::splitils::Sorter &) {} +}; + +} // namespace nlohmann + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_SORTER_H_ diff --git a/src/sdvrp/splitils/algorithm/splitils.cc b/src/sdvrp/splitils/algorithm/splitils.cc new file mode 100644 index 0000000..080f2e4 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/splitils.cc @@ -0,0 +1,447 @@ +#include "sdvrp/splitils/algorithm/splitils.h" + +#include +#include +#include +#include +#include + +#include "sdvrp/splitils/algorithm/base_star.h" +#include "sdvrp/splitils/algorithm/construction.h" +#include "sdvrp/splitils/algorithm/operators.h" +#include "sdvrp/splitils/util/mcmf.h" +#include "sdvrp/splitils/util/solution_utils.h" +#include "sdvrp/splitils/util/submit.h" +#include "sdvrp/splitils/util/utils.h" +#include "util/utils.h" + +namespace vrp::sdvrp::splitils { + +void IntraRouteSearch(const Problem &problem, const Config &config, + const DistanceMatrix &distance_matrix, Node route_index, + Solution &solution, RouteContext &context, + Random &random) { + Repair(distance_matrix, route_index, solution, context); + std::vector intra_neighborhoods(config.intra_operators.size()); + std::iota(intra_neighborhoods.begin(), intra_neighborhoods.end(), 0); + while (true) { + Shuffle(intra_neighborhoods.begin(), intra_neighborhoods.end(), random); + bool improved = false; + for (Node neighborhood : intra_neighborhoods) { + improved = config.intra_operators[neighborhood]( + problem, distance_matrix, route_index, solution, context, random); + if (improved) { + break; + } + } + if (!improved) { + break; + } + } +} + +void RandomizedVariableNeighborhoodDescent( + const Problem &problem, const Config &config, + const DistanceMatrix &distance_matrix, Solution &solution, + RouteContext &context, Random &random, + std::vector> &operators_caches) { + static std::vector> routes; + std::set associated_routes; + for (auto tier = 0; tier < config.inter_operators.size(); ++tier) { + for (auto i = 0; i < config.inter_operators[tier].size(); ++i) { + operators_caches[tier][i].Init( + context, kInterOperatorCacheMap.at(config.inter_operators[tier][i])); + } + } + for (Node route_index = 0; route_index < routes.size(); ++route_index) { + bool same_route = false; + if (route_index < context.num_routes) { + same_route = true; + Node head = context.heads[route_index]; + for (Node node : routes[route_index]) { + if (head != node) { + same_route = false; + break; + } + head = solution.successor(head); + } + if (head != 0) { + same_route = false; + } + } + if (!same_route) { + for (Node i = 0; i < problem.num_customers; ++i) { + star_caches[route_index].clear(); + } + } + } + for (auto tier = 0; tier < config.inter_operators.size();) { + std::vector inter_neighborhoods(config.inter_operators[tier].size()); + std::iota(inter_neighborhoods.begin(), inter_neighborhoods.end(), 0); + Shuffle(inter_neighborhoods.begin(), inter_neighborhoods.end(), random); + bool improved = false; + for (int neighborhood : inter_neighborhoods) { + Node original_num_routes = context.num_routes; + improved = config.inter_operators[tier][neighborhood]( + problem, distance_matrix, solution, context, associated_routes, + random, operators_caches[tier][neighborhood]); + if (improved) { + std::vector heads; + for (Node route_index : associated_routes) { + Node head = context.heads[route_index]; + if (head) { + heads.emplace_back(head); + } + if (route_index < original_num_routes) { + for (auto &&tier_operator_caches : operators_caches) { + for (auto &&operator_cache : tier_operator_caches) { + operator_cache.RemoveRoute(route_index); + } + } + for (Node i = 0; i < problem.num_customers; ++i) { + star_caches[route_index].clear(); + } + } + } + Node num_routes = 0; + for (Node route_index = 0; route_index < context.num_routes; + ++route_index) { + if (!associated_routes.count(route_index)) { + MoveRouteContext(num_routes, route_index, context); + for (auto &&tier_operator_caches : operators_caches) { + for (auto &&operator_cache : tier_operator_caches) { + operator_cache.MoveRoute(num_routes, route_index); + } + } + star_caches[num_routes].swap(star_caches[route_index]); + ++num_routes; + } + } + for (Node head : heads) { + context.heads[num_routes] = head; + UpdateRouteContext(solution, num_routes, 0, context); + IntraRouteSearch(problem, config, distance_matrix, num_routes, + solution, context, random); + for (auto &&tier_operator_caches : operators_caches) { + for (auto &&operator_cache : tier_operator_caches) { + operator_cache.AddRoute(num_routes); + } + } + ++num_routes; + } + context.num_routes = num_routes; + associated_routes.clear(); + break; + } + } + if (!improved) { + tier += 1; + } else if (tier > 0) { + tier = 0; + } + } + routes.resize(context.num_routes); + for (Node route_index = 0; route_index < routes.size(); ++route_index) { + auto &route = routes[route_index]; + route.clear(); + for (Node node = context.heads[route_index]; node; + node = solution.successor(node)) { + route.push_back(node); + } + } +} + +void Perturb(const Problem &problem, const DistanceMatrix &distance_matrix, + const Config &config, Solution &solution, RouteContext &context, + Random &random) { + CalcRouteContext(solution, context); + std::vector customers = config.ruin_method->Ruin( + problem, distance_matrix, solution, context, random); + config.sorter.Sort(problem, distance_matrix, customers, random); + for (Node customer : customers) { + for (Node route_index = 0; route_index < context.num_routes; + ++route_index) { + Node node_index = context.heads[route_index]; + while (node_index) { + Node successor = solution.successor(node_index); + if (solution.customer(node_index) == customer) { + RemoveNode(distance_matrix, node_index, route_index, solution, + context); + } + node_index = successor; + } + } + } + std::set associated_routes; + for (Node customer : customers) { + SplitReinsertion(problem, distance_matrix, customer, + problem.customers[customer].demand, config.blink_rate, + solution, context, associated_routes, random); + associated_routes.clear(); + } +} + +std::unique_ptr +SplitIlsAlgorithm(const Config &config, const Problem &problem, + const DistanceMatrix &distance_matrix, int lower_bound) { + Random random(config.random_seed); + auto context = std::make_unique(); + std::vector> operators_caches; + for (auto &&operators : config.inter_operators) { + operators_caches.emplace_back(operators.size()); + } + auto best_solution = std::make_unique(); + int best_objective = std::numeric_limits::max(); + auto timer = CreateTimer(config.time_limit_type); + timer->Set(config.time_limit); + const int kMaxStagnation = + std::min(5000, static_cast(problem.num_customers) * + static_cast(CalcFleetLowerBound(problem))); + int counter = 0; + while (!timer->IsTimeOut() && best_objective > lower_bound) { + auto solution = Construct(problem, distance_matrix, random); + int objective = CalcObjective(*solution, distance_matrix); + int iter_best_objective = objective; + auto new_solution = std::make_unique(*solution); + auto acceptance_rule = config.acceptance_rule(); + int num_stagnation = 0; + while (num_stagnation < kMaxStagnation && !timer->IsTimeOut()) { + ++counter; + ++num_stagnation; + CalcRouteContext(*new_solution, *context); + for (Node i = 0; i < context->num_routes; ++i) { + IntraRouteSearch(problem, config, distance_matrix, i, *new_solution, + *context, random); + } + RandomizedVariableNeighborhoodDescent(problem, config, distance_matrix, + *new_solution, *context, random, + operators_caches); + int new_objective = CalcObjective(*new_solution, distance_matrix); + if (new_objective < iter_best_objective) { + num_stagnation = 0; + iter_best_objective = new_objective; + } + if (new_objective < best_objective) { + Submit(*timer, new_objective); + best_objective = new_objective; + *best_solution = *new_solution; + if (best_objective <= lower_bound) { + break; + } + } + if (acceptance_rule->Accept(objective, new_objective, random)) { + objective = new_objective; + *solution = *new_solution; + } else { + *new_solution = *solution; + } + Perturb(problem, distance_matrix, config, *new_solution, *context, + random); + } + } + std::cout << counter << std::endl; + return best_solution; +} + +void Recover(const DistanceMatrix &distance_matrix, Solution &solution, Node i, + Node j) { + Node customer = + distance_matrix + .previous_node_indices[solution.customer(i)][solution.customer(j)]; + if (customer != -1) { + Node k = solution.Insert(customer, 0, i, j); + Recover(distance_matrix, solution, i, k); + Recover(distance_matrix, solution, k, j); + } +} + +void RecoverFloyd(const DistanceMatrix &distance_matrix, Solution &solution) { + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + Node predecessor = 0; + while (node_index) { + Recover(distance_matrix, solution, predecessor, node_index); + predecessor = node_index; + node_index = solution.successor(node_index); + } + Recover(distance_matrix, solution, predecessor, 0); + } + } +} + +void RecoverDijkstra(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution) { + std::vector> distances(problem.num_customers); + std::vector predecessors(problem.num_customers); + std::vector visited(problem.num_customers); + std::vector forbid(problem.num_customers); + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + std::vector route; + route.push_back(0); + std::fill(forbid.begin(), forbid.end(), false); + while (node_index) { + route.push_back(node_index); + forbid[solution.customer(node_index)] = true; + node_index = solution.successor(node_index); + } + route.push_back(0); + for (auto j = 0; j + 1 < route.size(); ++j) { + Node predecessor = route[j]; + Node successor = route[j + 1]; + Node s = solution.customer(predecessor); + Node t = solution.customer(successor); + for (Node k = t > 0; k < problem.num_customers; ++k) { + distances[k] = {distance_matrix.original[s][k], forbid[k], 0}; + predecessors[k] = s; + visited[k] = false; + } + while (true) { + Node u = -1; + for (Node k = t > 0; k < problem.num_customers; ++k) { + if (!visited[k]) { + if (u == -1 || distances[k] < distances[u]) { + u = k; + } + } + } + if (u == t) { + break; + } + visited[u] = true; + for (Node k = t > 0; k < problem.num_customers; ++k) { + if (!visited[k]) { + std::tuple new_distance{ + std::get<0>(distances[u]) + distance_matrix.original[u][k], + std::get<1>(distances[u]) + forbid[k], + std::get<2>(distances[u]) + 1}; + if (distances[k] > new_distance) { + distances[k] = new_distance; + predecessors[k] = u; + } + } + } + } + Node u = predecessors[t]; + while (u != s) { + forbid[u] = true; + successor = solution.Insert(u, 0, predecessor, successor); + u = predecessors[u]; + } + } + } + } +} + +void RecoverDijkstraForce(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution) { + std::vector> distances(problem.num_customers); + std::vector predecessors(problem.num_customers); + std::vector visited(problem.num_customers); + std::vector forbid(problem.num_customers); + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + std::vector route; + route.push_back(0); + std::fill(forbid.begin(), forbid.end(), false); + forbid[0] = true; + while (node_index) { + route.push_back(node_index); + forbid[solution.customer(node_index)] = true; + node_index = solution.successor(node_index); + } + route.push_back(0); + for (auto j = 0; j + 1 < route.size(); ++j) { + Node predecessor = route[j]; + Node successor = route[j + 1]; + Node s = solution.customer(predecessor); + Node t = solution.customer(successor); + for (Node k = 0; k < problem.num_customers; ++k) { + if (!forbid[k] || k == t) { + distances[k] = {distance_matrix.original[s][k], 0}; + predecessors[k] = s; + visited[k] = false; + } + } + while (true) { + Node u = -1; + for (Node k = 0; k < problem.num_customers; ++k) { + if (!visited[k] && (!forbid[k] || k == t)) { + if (u == -1 || distances[k] < distances[u]) { + u = k; + } + } + } + if (u == t) { + break; + } + visited[u] = true; + for (Node k = 0; k < problem.num_customers; ++k) { + if (!visited[k] && (!forbid[k] || k == t)) { + std::tuple new_distance{ + std::get<0>(distances[u]) + distance_matrix.original[u][k], + std::get<1>(distances[u]) + 1}; + if (distances[k] > new_distance) { + distances[k] = new_distance; + predecessors[k] = u; + } + } + } + } + Node u = predecessors[t]; + while (u != s) { + forbid[u] = true; + successor = solution.Insert(u, 0, predecessor, successor); + u = predecessors[u]; + } + } + } + } +} + +void Reflow(const Problem &problem, Solution &solution) { + std::vector heads; + Node max_index = 0; + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node index = solution.node_pool()[i]; + max_index = std::max(max_index, index); + if (!solution.predecessor(index)) { + heads.push_back(index); + } + } + int num_routes = static_cast(heads.size()); + int num_customers = problem.num_customers; + int s = num_routes + num_customers; + int t = s + 1; + MCMF mcmf(t + 1); + for (int i = 0; i < num_routes; ++i) { + mcmf.Link(s, i, problem.capacity, 0); + } + for (int i = 0; i < num_customers; ++i) { + mcmf.Link(num_routes + i, t, problem.customers[i].demand, 0); + } + std::vector edges(max_index + 1); + for (int i = 0; i < num_routes; ++i) { + Node u = heads[i]; + while (u) { + int j = num_routes + solution.customer(u); + edges[u] = mcmf.Link(i, j, 1, 0); + mcmf.Link(i, j, problem.capacity, 1); + u = solution.successor(u); + } + } + mcmf.Solve(s, t); + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node index = solution.node_pool()[i]; + int edge = edges[index]; + int load = mcmf.e[edge].flow + mcmf.e[edge + 2].flow; + solution.set_load(index, load); + } +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/algorithm/splitils.h b/src/sdvrp/splitils/algorithm/splitils.h new file mode 100644 index 0000000..810d088 --- /dev/null +++ b/src/sdvrp/splitils/algorithm/splitils.h @@ -0,0 +1,30 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_SPLITILS_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_SPLITILS_H_ + +#include +#include + +#include "sdvrp/problem.h" +#include "sdvrp/splitils/config/config.h" +#include "sdvrp/splitils/model/solution.h" + +namespace vrp::sdvrp::splitils { + +std::unique_ptr +SplitIlsAlgorithm(const Config &config, const Problem &problem, + const DistanceMatrix &distance_matrix, int lower_bound); + +void RecoverFloyd(const DistanceMatrix &distance_matrix, Solution &solution); + +void RecoverDijkstra(const Problem &problem, + const DistanceMatrix &distance_matrix, Solution &solution); + +void RecoverDijkstraForce(const Problem &problem, + const DistanceMatrix &distance_matrix, + Solution &solution); + +void Reflow(const Problem &problem, Solution &solution); + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_ALGORITHM_SPLITILS_H_ diff --git a/src/sdvrp/splitils/config/config.cc b/src/sdvrp/splitils/config/config.cc new file mode 100644 index 0000000..2aa1181 --- /dev/null +++ b/src/sdvrp/splitils/config/config.cc @@ -0,0 +1,40 @@ +#include "sdvrp/splitils/config/config.h" + +#include +#include + +namespace vrp::sdvrp::splitils { + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Config, problem_dir, output_dir, random_seed, + time_limit, time_limit_type, acceptance_rule, + ruin_method, sorter, blink_rate, + inter_operators, intra_operators) + +Config ReadConfigFromFile(const std::string &config_path, + const nlohmann::json &default_value, + const nlohmann::json &overwrite_value) { + std::ifstream ifs(config_path); + if (ifs.fail()) { + throw std::invalid_argument("Cannot open config."); + } + nlohmann::json j; + ifs >> j; + j.insert(default_value.begin(), default_value.end()); + if (!overwrite_value.empty()) { + j.update(overwrite_value); + } + Config config{}; + from_json(j, config); +#ifdef DEBUG + std::cerr << j.dump(4) << std::endl; +#endif + return config; +} + +std::string ConvertConfigToString(const Config &config) { + nlohmann::json j; + to_json(j, config); + return j.dump(); +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/config/config.h b/src/sdvrp/splitils/config/config.h new file mode 100644 index 0000000..fbb6272 --- /dev/null +++ b/src/sdvrp/splitils/config/config.h @@ -0,0 +1,41 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_CONFIG_CONFIG_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_CONFIG_CONFIG_H_ + +#include +#include +#include +#include + +#include + +#include "sdvrp/splitils/algorithm/operators.h" +#include "sdvrp/splitils/algorithm/ruin_method.h" +#include "sdvrp/splitils/algorithm/sorter.h" +#include "util/acceptance_rule.h" +#include "util/timer.h" + +namespace vrp::sdvrp::splitils { + +struct Config { + std::string problem_dir; + std::string output_dir; + uint32_t random_seed; + double time_limit; + TimerType time_limit_type; + std::function>()> acceptance_rule; + std::unique_ptr ruin_method; + Sorter sorter; + double blink_rate; + std::vector> inter_operators; + std::vector intra_operators; +}; + +Config ReadConfigFromFile(const std::string &config_path, + const nlohmann::json &default_value, + const nlohmann::json &overwrite_value); + +std::string ConvertConfigToString(const Config &config); + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_CONFIG_CONFIG_H_ diff --git a/src/sdvrp/splitils/limit.h b/src/sdvrp/splitils/limit.h new file mode 100644 index 0000000..433f993 --- /dev/null +++ b/src/sdvrp/splitils/limit.h @@ -0,0 +1,12 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_LIMIT_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_LIMIT_H_ + +#include "sdvrp/limit.h" + +namespace vrp::sdvrp::splitils { + +const Node kMaxNumNodes = kMaxNumCustomers * 3; + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_LIMIT_H_ diff --git a/src/sdvrp/splitils/main.cc b/src/sdvrp/splitils/main.cc new file mode 100644 index 0000000..8713119 --- /dev/null +++ b/src/sdvrp/splitils/main.cc @@ -0,0 +1,68 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "sdvrp/logo.h" +#include "sdvrp/problem.h" +#include "sdvrp/splitils/algorithm/splitils.h" +#include "sdvrp/splitils/util/solution_utils.h" +#include "sdvrp/visualization.h" + +int main(int argc, char **argv) { + std::cerr << vrp::sdvrp::splitils::ConvertConfigToString( + vrp::sdvrp::splitils::Config()) + << std::endl; + auto start_time = std::chrono::system_clock::now(); + CLI::App app{ + std::string{vrp::sdvrp::kLogo} + + "A program used to participate in the sdvrp track of DIMACS-12 " + "competition\n(http://dimacs.rutgers.edu/programs/challenge/vrp/vrpsd/)"}; + std::string config_path; + app.add_option("config", config_path, "Config path")->required(); + std::string instance_name; + app.add_option("instance", instance_name, "Instance name")->required(); + std::string output1; + app.add_option("output1", output1, "Output1")->required(); + std::string output2; + app.add_option("output2", output2, "Output2")->required(); + std::optional random_seed; + app.add_option("-s", random_seed, "Random Seed"); + int lower_bound = 0; + app.add_option("-l", lower_bound, "Lower bound"); + CLI11_PARSE(app, argc, argv); + nlohmann::json default_value = {{"random_seed", std::random_device{}()}}; + nlohmann::json overwrite_value; + if (random_seed) { + overwrite_value["random_seed"] = random_seed.value(); + } + auto config = vrp::sdvrp::splitils::ReadConfigFromFile( + config_path, default_value, overwrite_value); + auto problem_path = + vrp::sdvrp::GetProblemPath(config.problem_dir, instance_name); + auto problem = vrp::sdvrp::ReadProblemFromFile(problem_path); + auto distance_matrix = vrp::sdvrp::CalcDistanceMatrix(problem); + auto solution = vrp::sdvrp::splitils::SplitIlsAlgorithm( + config, problem, *distance_matrix, lower_bound); + // vrp::sdvrp::splitils::RecoverFloyd(*distance_matrix, *solution); + // vrp::sdvrp::splitils::Reflow(problem, *solution); + auto new_solution = *solution; + auto end_time = std::chrono::system_clock::now(); + auto time = std::chrono::duration_cast>( + end_time - start_time) + .count(); + vrp::sdvrp::splitils::RecoverDijkstra(problem, *distance_matrix, *solution); + vrp::sdvrp::splitils::RecoverDijkstraForce(problem, *distance_matrix, + new_solution); + std::ofstream ofs1(output1); + WriteSolutionToStream(*distance_matrix, *solution, time, ofs1); + std::ofstream ofs2(output2); + WriteSolutionToStream(*distance_matrix, new_solution, time, ofs2); + vrp::sdvrp::ShowGraphUrl( + vrp::sdvrp::SolutionToGraph(problem, *distance_matrix, new_solution)); + return 0; +} diff --git a/src/sdvrp/splitils/model/direction.h b/src/sdvrp/splitils/model/direction.h new file mode 100644 index 0000000..a5b480b --- /dev/null +++ b/src/sdvrp/splitils/model/direction.h @@ -0,0 +1,40 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_DIRECTION_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_DIRECTION_H_ + +namespace vrp::sdvrp::splitils { + +struct Forward { + static constexpr bool kDirection = false; + static Node Head(const RouteContext &context, Node node_index) { + return context.heads[node_index]; + } + static Node Predecessor(const Solution &solution, Node node_index) { + return solution.predecessor(node_index); + } + static Node Successor(const Solution &solution, Node node_index) { + return solution.successor(node_index); + } + static void Link(Node predecessor, Node successor, Solution &solution) { + solution.Link(predecessor, successor); + } +}; + +struct Backward { + static constexpr bool kDirection = true; + static Node Head(const RouteContext &context, Node node_index) { + return context.tails[node_index]; + } + static Node Predecessor(const Solution &solution, Node node_index) { + return solution.successor(node_index); + } + static Node Successor(const Solution &solution, Node node_index) { + return solution.predecessor(node_index); + } + static void Link(Node predecessor, Node successor, Solution &solution) { + solution.Link(successor, predecessor); + } +}; + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_DIRECTION_H_ diff --git a/src/sdvrp/splitils/model/operator_cache.h b/src/sdvrp/splitils/model/operator_cache.h new file mode 100644 index 0000000..9d11228 --- /dev/null +++ b/src/sdvrp/splitils/model/operator_cache.h @@ -0,0 +1,101 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_OPERATOR_CACHE_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_OPERATOR_CACHE_H_ + +#include +#include +#include +#include + +#include "sdvrp/splitils/limit.h" +#include "sdvrp/splitils/model/route_context.h" +#include "util/delta.h" + +namespace vrp::sdvrp::splitils { + +struct BaseCache { + bool invalidated = true; + Delta delta; + unsigned char move[24]; + + inline bool TryReuse() { + if (!invalidated) { + return true; + } + invalidated = false; + delta = Delta(); + return false; + } +}; + +class OperatorCaches + : std::array, kMaxNumRoutes> { +public: + void Init(const RouteContext &context, bool need) { + need_ = need; + if (!need) { + return; + } + max_index_ = context.num_routes; + route_pool_.clear(); + unused_indices_.clear(); + for (Node i = 0; i < context.num_routes; ++i) { + route_index_mappings_[i] = i; + route_pool_.emplace_back(i); + for (Node j = 0; j < context.num_routes; ++j) { + (*this)[i][j].invalidated = true; + } + } + } + + void AddRoute(Node route_index) { + if (!need_) { + return; + } + Node index; + if (unused_indices_.empty()) { + index = max_index_++; + } else { + index = unused_indices_.back(); + unused_indices_.pop_back(); + } + route_index_mappings_[route_index] = index; + route_pool_.emplace_back(index); + for (Node other : route_pool_) { + (*this)[index][other].invalidated = true; + (*this)[other][index].invalidated = true; + } + } + + void RemoveRoute(Node route_index) { + if (!need_) { + return; + } + Node index = route_index_mappings_[route_index]; + route_pool_.erase(std::find(route_pool_.begin(), route_pool_.end(), index)); + unused_indices_.emplace_back(index); + } + + void MoveRoute(Node dest_route_index, Node src_route_index) { + if (!need_) { + return; + } + route_index_mappings_[dest_route_index] = + route_index_mappings_[src_route_index]; + } + + BaseCache &Get(Node route_a, Node route_b) { + return ( + *this)[route_index_mappings_[route_a]][route_index_mappings_[route_b]]; + } + +private: + bool need_; + Node route_index_mappings_[kMaxNumRoutes]; + std::vector route_pool_; + std::vector unused_indices_; + Node max_index_; +}; + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_OPERATOR_CACHE_H_ diff --git a/src/sdvrp/splitils/model/route_context.cc b/src/sdvrp/splitils/model/route_context.cc new file mode 100644 index 0000000..9b3f20c --- /dev/null +++ b/src/sdvrp/splitils/model/route_context.cc @@ -0,0 +1,47 @@ +#include "sdvrp/splitils/model/route_context.h" + +namespace vrp::sdvrp::splitils { + +void GetRoutes(const Solution &solution, Node &num_routes, Node *heads) { + num_routes = 0; + const Node *node_pool = solution.node_pool(); + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = node_pool[i]; + if (solution.predecessor(node_index) == 0) { + heads[num_routes++] = node_index; + } + } +} + +void CalcRouteContext(const Solution &solution, RouteContext &route_context) { + GetRoutes(solution, route_context.num_routes, route_context.heads); + route_context.loads[0] = 0; + for (Node i = 0; i < route_context.num_routes; ++i) { + UpdateRouteContext(solution, i, 0, route_context); + } +} + +void UpdateRouteContext(const Solution &solution, Node route_index, + Node predecessor, RouteContext &route_context) { + Load load = route_context.loads[predecessor]; + Node node_index = predecessor ? solution.successor(predecessor) + : route_context.heads[route_index]; + while (node_index) { + load += solution.load(node_index); + route_context.loads[node_index] = load; + predecessor = node_index; + node_index = solution.successor(node_index); + } + route_context.tails[route_index] = predecessor; + route_context.route_loads[route_index] = load; +} + +void MoveRouteContext(Node dest_route_index, Node src_route_index, + RouteContext &route_context) { + route_context.heads[dest_route_index] = route_context.heads[src_route_index]; + route_context.tails[dest_route_index] = route_context.tails[src_route_index]; + route_context.route_loads[dest_route_index] = + route_context.route_loads[src_route_index]; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/model/route_context.h b/src/sdvrp/splitils/model/route_context.h new file mode 100644 index 0000000..456650e --- /dev/null +++ b/src/sdvrp/splitils/model/route_context.h @@ -0,0 +1,31 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_ROUTE_CONTEXT_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_ROUTE_CONTEXT_H_ + +#include "sdvrp/limit.h" +#include "sdvrp/problem.h" +#include "sdvrp/splitils/limit.h" +#include "sdvrp/splitils/model/solution.h" + +namespace vrp::sdvrp::splitils { + +struct RouteContext { + Node num_routes; + Node heads[kMaxNumRoutes]; + Node tails[kMaxNumRoutes]; + int route_loads[kMaxNumRoutes]; + Load loads[kMaxNumNodes]; +}; + +void GetRoutes(const Solution &solution, Node &num_routes, Node *heads); + +void CalcRouteContext(const Solution &solution, RouteContext &route_context); + +void UpdateRouteContext(const Solution &solution, Node route_index, + Node predecessor, RouteContext &route_context); + +void MoveRouteContext(Node dest_route_index, Node src_route_index, + RouteContext &route_context); + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_ROUTE_CONTEXT_H_ diff --git a/src/sdvrp/splitils/model/route_head_guard.h b/src/sdvrp/splitils/model/route_head_guard.h new file mode 100644 index 0000000..57dc93e --- /dev/null +++ b/src/sdvrp/splitils/model/route_head_guard.h @@ -0,0 +1,22 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_ROUTE_HEAD_GUARD_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_ROUTE_HEAD_GUARD_H_ + +#include "sdvrp/splitils/model/route_context.h" +#include "sdvrp/splitils/model/solution.h" + +namespace vrp::sdvrp::splitils { + +struct RouteHeadGuard { + Solution &solution; + RouteContext &context; + Node route_index; + RouteHeadGuard(Solution &solution, RouteContext &context, Node route_index) + : solution(solution), context(context), route_index(route_index) { + solution.set_successor(0, context.heads[route_index]); + } + ~RouteHeadGuard() { context.heads[route_index] = solution.successor(0); } +}; + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_ROUTE_HEAD_GUARD_H_ diff --git a/src/sdvrp/splitils/model/solution.h b/src/sdvrp/splitils/model/solution.h new file mode 100644 index 0000000..4793899 --- /dev/null +++ b/src/sdvrp/splitils/model/solution.h @@ -0,0 +1,132 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_SOLUTION_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_SOLUTION_H_ + +#include +#include +#include + +#include "sdvrp/limit.h" +#include "sdvrp/splitils/limit.h" + +namespace vrp::sdvrp::splitils { + +class Solution { +public: + Solution(); + [[nodiscard]] Node predecessor(Node node_index) const; + [[nodiscard]] Node successor(Node node_index) const; + [[nodiscard]] Node customer(Node node_index) const; + [[nodiscard]] Load load(Node node_index) const; + [[nodiscard]] Node num_nodes() const; + [[nodiscard]] const Node *node_pool() const; + void set_predecessor(Node node_index, Node predecessor); + void set_successor(Node node_index, Node successor); + void set_customer(Node node_index, Node customer); + void set_load(Node node_index, Load load); + void Remove(Node node_index); + Node Insert(Node customer, Load load, Node predecessor, Node successor); + Node NewNode(Node customer, Load load); + void Link(Node predecessor, Node successor); + +private: + struct NodeImpl { + Node successor; + Node predecessor; + Node customer; + Load load; + }; + NodeImpl node_impls_[kMaxNumNodes]{}; + // Node predecessors_[kMaxNumNodes]{}; + // Node successors_[kMaxNumNodes]{}; + // Node customers_[kMaxNumNodes]{}; + // Load loads_[kMaxNumNodes]{}; + Node node_pool_[kMaxNumNodes]{}; + Node pool_indices_[kMaxNumNodes]{}; + Node num_nodes_; +}; + +inline Solution::Solution() { + set_load(0, 0); + set_customer(0, 0); + num_nodes_ = 0; + std::iota(node_pool_, node_pool_ + kMaxNumNodes, 1); +} + +inline Node Solution::predecessor(Node node_index) const { + // return predecessors_[node_index]; + return node_impls_[node_index].predecessor; +} + +inline Node Solution::successor(Node node_index) const { + // return successors_[node_index]; + return node_impls_[node_index].successor; +} + +inline Node Solution::customer(Node node_index) const { + // return customers_[node_index]; + return node_impls_[node_index].customer; +} + +inline Load Solution::load(Node node_index) const { + // return loads_[node_index]; + return node_impls_[node_index].load; +} + +inline Node Solution::num_nodes() const { return num_nodes_; } + +inline const Node *Solution::node_pool() const { return node_pool_; } + +inline void Solution::set_predecessor(Node node_index, Node predecessor) { + // predecessors_[node_index] = predecessor; + node_impls_[node_index].predecessor = predecessor; +} + +inline void Solution::set_successor(Node node_index, Node successor) { + // successors_[node_index] = successor; + node_impls_[node_index].successor = successor; +} + +inline void Solution::set_customer(Node node_index, Node customer) { + // customers_[node_index] = customer; + node_impls_[node_index].customer = customer; +} + +inline void Solution::set_load(Node node_index, Load load) { + // loads_[node_index] = load; + node_impls_[node_index].load = load; +} + +inline void Solution::Remove(Node node_index) { + Node predecessor = this->predecessor(node_index); + Node successor = this->successor(node_index); + Link(predecessor, successor); + Node pool_index = pool_indices_[node_index]; + pool_indices_[node_pool_[num_nodes_ - 1]] = pool_index; + std::swap(node_pool_[pool_index], node_pool_[num_nodes_ - 1]); + --num_nodes_; +} + +inline Node Solution::Insert(Node customer, Load load, Node predecessor, + Node successor) { + Node node_index = NewNode(customer, load); + Link(predecessor, node_index); + Link(node_index, successor); + return node_index; +} + +inline Node Solution::NewNode(Node customer, Load load) { + Node node_index = node_pool_[num_nodes_]; + pool_indices_[node_index] = num_nodes_++; + set_customer(node_index, customer); + set_load(node_index, load); + return node_index; +} + +inline void Solution::Link(Node predecessor, Node successor) { + set_predecessor(successor, predecessor); + set_successor(predecessor, successor); +} + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_MODEL_SOLUTION_H_ diff --git a/src/sdvrp/splitils/util/mcmf.h b/src/sdvrp/splitils/util/mcmf.h new file mode 100644 index 0000000..d776092 --- /dev/null +++ b/src/sdvrp/splitils/util/mcmf.h @@ -0,0 +1,76 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_MCMF_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_MCMF_H_ + +#include + +namespace vrp::sdvrp::splitils { + +struct MCMF { + const int inf = 1e9; + + struct Node { + int from, to, nxt, cap, flow, cost; + }; + + std::vector dis, g, pre, vis; + std::vector e; + int n; + + MCMF(int n) : n(n), dis(n), g(n, -1), pre(n), vis(n) {} + + int Link(int u, int v, int f, int c) { + int m = static_cast(e.size()); + e.push_back(Node{u, v, g[u], f, 0, c}); + g[u] = m; + e.push_back(Node{v, u, g[v], 0, 0, -c}); + g[v] = m + 1; + return m; + } + + bool Extend(int s, int t) { + std::fill(vis.begin(), vis.end(), 0); + std::fill(dis.begin(), dis.end(), inf); + std::queue queue; + dis[s] = 0; + queue.push(s); + while (!queue.empty()) { + int u = queue.front(); + queue.pop(); + vis[u] = false; + for (int it = g[u]; ~it; it = e[it].nxt) { + int v = e[it].to; + if (e[it].cap > e[it].flow && dis[v] > dis[u] + e[it].cost) { + dis[v] = dis[u] + e[it].cost; + pre[v] = it; + if (!vis[v]) { + queue.push(v); + } + vis[v] = true; + } + } + } + return dis[t] < inf; + } + + std::pair Solve(int s, int t) { + int max_flow = 0; + int min_cost = 0; + while (Extend(s, t)) { + int delta = inf; + for (int u = t; u != s; u = e[pre[u]].from) { + delta = std::min(delta, e[pre[u]].cap - e[pre[u]].flow); + } + min_cost += delta * dis[t]; + max_flow += delta; + for (int u = t; u != s; u = e[pre[u]].from) { + e[pre[u]].flow += delta; + e[pre[u] ^ 1].flow -= delta; + } + } + return {max_flow, min_cost}; + } +}; + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_MCMF_H_ diff --git a/src/sdvrp/splitils/util/solution_utils.cc b/src/sdvrp/splitils/util/solution_utils.cc new file mode 100644 index 0000000..34fbc6d --- /dev/null +++ b/src/sdvrp/splitils/util/solution_utils.cc @@ -0,0 +1,179 @@ +#include "sdvrp/splitils/util/solution_utils.h" + +#include +#include +#include +#include +#include + +namespace vrp::sdvrp::splitils { + +int RemoveNode(const DistanceMatrix &distance_matrix, Node node_index, + Node route_index, Solution &solution, RouteContext &context) { + Node customer = solution.customer(node_index); + Node predecessor = solution.predecessor(node_index); + Node successor = solution.successor(node_index); + int delta = distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)]; + delta -= distance_matrix[customer][solution.customer(predecessor)] + + distance_matrix[customer][solution.customer(successor)]; + solution.Remove(node_index); + if (predecessor == 0) { + context.heads[route_index] = successor; + } + UpdateRouteContext(solution, route_index, predecessor, context); + return delta; +} + +int CalcObjective(const Solution &solution, + const DistanceMatrix &distance_matrix) { + int objective = 0; + const Node *node_pool = solution.node_pool(); + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = node_pool[i]; + Node predecessor = solution.predecessor(node_index); + Node successor = solution.successor(node_index); + objective += distance_matrix[solution.customer(node_index)] + [solution.customer(predecessor)]; + if (successor == 0) { + objective += distance_matrix[solution.customer(node_index)][0]; + } + } + return objective; +} + +void CheckFeasible(const Problem &problem, const Solution &solution) { + std::set unused_node_indices( + solution.node_pool(), solution.node_pool() + solution.num_nodes()); + std::vector customer_load_sums(problem.num_customers); + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + Node predecessor = 0; + Node route_load = 0; + while (node_index) { + if (solution.predecessor(node_index) != predecessor) { + throw std::runtime_error( + "Predecessor and successor are not consistent."); + } + if (!unused_node_indices.erase(node_index)) { + throw std::runtime_error("Some node is visited twice."); + } + Load load = solution.load(node_index); + Node customer = solution.customer(node_index); + if (customer <= 0 || customer >= problem.num_customers) { + throw std::runtime_error("Invalid customer index."); + } + customer_load_sums[customer] += load; + route_load += load; + predecessor = node_index; + node_index = solution.successor(node_index); + } + if (route_load > problem.capacity) { + throw std::runtime_error("Route load exceeds the capacity."); + } + } + } + if (!unused_node_indices.empty()) { + throw std::runtime_error("Some node leaks."); + } + for (Node i = 1; i < problem.num_customers; ++i) { + if (customer_load_sums[i] < problem.customers[i].demand) { + throw std::runtime_error("Some customer is not served fully."); + } + if (customer_load_sums[i] > problem.customers[i].demand) { + throw std::runtime_error("Some customer is served exceedingly."); + } + } +} + +void CheckRouteContextHeads(const Solution &solution, + const RouteContext &context) { + std::set heads; + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + heads.insert(node_index); + } + } + for (Node i = 0; i < context.num_routes; ++i) { + Node head = context.heads[i]; + if (head == 0) { + continue; + } + if (!heads.erase(head)) { + throw std::runtime_error("Some head is not truly head."); + } + } + if (!heads.empty()) { + throw std::runtime_error("Some head is not recorded."); + } +} + +void ReversedLink(Solution &solution, Node left, Node right, Node predecessor, + Node successor) { + while (true) { + Node original_predecessor = solution.predecessor(right); + solution.Link(predecessor, right); + if (right == left) { + break; + } + predecessor = right; + right = original_predecessor; + } + solution.Link(left, successor); +} + +void IgnoreUntilChar(char c, std::istream &stream) { + stream.ignore(std::numeric_limits::max(), stream.widen(c)); +} + +Solution ReadSolutionToStream(std::istream &stream) { + Solution solution; + std::string prefix; + while (stream >> prefix && prefix == "Route") { + std::string line; + std::getline(stream, line); + std::istringstream is(line); + IgnoreUntilChar('-', is); + Node predecessor = 0; + Node customer; + while (is >> customer && customer) { + IgnoreUntilChar('(', is); + Load load; + is >> load; + predecessor = solution.Insert(customer, load, predecessor, 0); + IgnoreUntilChar('-', is); + } + } + return solution; +} + +void WriteSolutionToStream(const DistanceMatrix &distance_matrix, + const Solution &solution, double time, + std::ostream &stream) { + Node num_routes = 0; + int total_cost = 0; + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + stream << "Route " << ++num_routes << ": 0"; + Node predecessor = 0; + while (node_index) { + Node customer = solution.customer(node_index); + stream << " - " << customer << " ( " << solution.load(node_index) + << " )"; + total_cost += distance_matrix.original[predecessor][customer]; + predecessor = customer; + node_index = solution.successor(node_index); + } + stream << " - 0\n"; + total_cost += distance_matrix.original[predecessor][0]; + } + } + stream << total_cost << '\n'; + stream << "Intel Core i7-8700 @ 3.20GHz" << std::endl; + stream << time << std::endl; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/util/solution_utils.h b/src/sdvrp/splitils/util/solution_utils.h new file mode 100644 index 0000000..6eab041 --- /dev/null +++ b/src/sdvrp/splitils/util/solution_utils.h @@ -0,0 +1,36 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_SOLUTION_UTILS_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_SOLUTION_UTILS_H_ + +#include +#include +#include + +#include "sdvrp/distance_matrix.h" +#include "sdvrp/splitils/model/route_context.h" +#include "sdvrp/splitils/model/solution.h" + +namespace vrp::sdvrp::splitils { + +int RemoveNode(const DistanceMatrix &distance_matrix, Node node_index, + Node route_index, Solution &solution, RouteContext &context); + +int CalcObjective(const Solution &solution, + const DistanceMatrix &distance_matrix); + +void CheckFeasible(const Problem &problem, const Solution &solution); + +void CheckRouteContextHeads(const Solution &solution, + const RouteContext &context); + +void ReversedLink(Solution &solution, Node left, Node right, Node predecessor, + Node successor); + +Solution ReadSolutionToStream(std::istream &stream); + +void WriteSolutionToStream(const DistanceMatrix &distance_matrix, + const Solution &solution, double time, + std::ostream &stream); + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_SOLUTION_UTILS_H_ diff --git a/src/sdvrp/splitils/util/submit.cc b/src/sdvrp/splitils/util/submit.cc new file mode 100644 index 0000000..1422bed --- /dev/null +++ b/src/sdvrp/splitils/util/submit.cc @@ -0,0 +1,11 @@ +#include "sdvrp/splitils/util/submit.h" + +#include + +namespace vrp::sdvrp::splitils { + +void Submit(const BaseTimer &baseTimer, int objective) { + std::cout << baseTimer.ElapsedTime() << ',' << objective << std::endl; +} + +} // namespace vrp::sdvrp::splitils diff --git a/src/sdvrp/splitils/util/submit.h b/src/sdvrp/splitils/util/submit.h new file mode 100644 index 0000000..d4862f1 --- /dev/null +++ b/src/sdvrp/splitils/util/submit.h @@ -0,0 +1,12 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_SUBMIT_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_SUBMIT_H_ + +#include "util/timer.h" + +namespace vrp::sdvrp::splitils { + +void Submit(const BaseTimer &timer, int objective); + +} + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_SUBMIT_H_ diff --git a/src/sdvrp/splitils/util/utils.h b/src/sdvrp/splitils/util/utils.h new file mode 100644 index 0000000..915daac --- /dev/null +++ b/src/sdvrp/splitils/util/utils.h @@ -0,0 +1,57 @@ +#ifndef DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_UTILS_H_ +#define DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_UTILS_H_ + +#include "sdvrp/splitils/model/route_context.h" +#include "sdvrp/splitils/model/solution.h" +#include "util/delta.h" + +namespace vrp::sdvrp::splitils { + +template struct InsertionWithCost { + Node predecessor; + Node successor; + Node route_index; + Delta cost; + + inline bool Update(const InsertionWithCost &insertion, Random &random) { + if (cost.Update(insertion.cost, random)) { + predecessor = insertion.predecessor; + successor = insertion.successor; + route_index = insertion.route_index; + return true; + } + return false; + } +}; + +template +auto CalcBestInsertion(const Solution &solution, const Func &func, + const RouteContext &context, Node route_index, + Node customer, Random &random) { + Node head = context.heads[route_index]; + auto head_cost = func(0, head, customer); + InsertionWithCost best_insertion{0, head, route_index, + Delta(head_cost, 1)}; + Node node_index = head; + while (node_index) { + auto cost = func(node_index, solution.successor(node_index), customer); + if (best_insertion.cost.Update(cost, random)) { + best_insertion.predecessor = node_index; + best_insertion.successor = solution.successor(node_index); + } + node_index = solution.successor(node_index); + } + return best_insertion; +} + +inline Node CalcFleetLowerBound(const Problem &problem) { + int sum_demands = 0; + for (Node i = 1; i < problem.num_customers; ++i) { + sum_demands += problem.customers[i].demand; + } + return (sum_demands + problem.capacity - 1) / problem.capacity; +} + +} // namespace vrp::sdvrp::splitils + +#endif // DIMACS_12_SRC_SDVRP_SPLITILS_UTIL_UTILS_H_ diff --git a/src/sdvrp/visualization.cc b/src/sdvrp/visualization.cc new file mode 100644 index 0000000..b47b1e3 --- /dev/null +++ b/src/sdvrp/visualization.cc @@ -0,0 +1,140 @@ +#include "sdvrp/visualization.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "sdvrp/splitils/util/solution_utils.h" + +namespace vrp::sdvrp { + +std::string HslToRgb(double hue, double saturation, double lightness) { + double chroma = (1 - std::abs(2 * lightness - 1)) * saturation; + hue /= 60; + double second_component = chroma * (1 - std::abs(std::fmod(hue, 2) - 1)); + double red = 0, green = 0, blue = 0; + if (hue < 1) { + red = chroma; + green = second_component; + } else if (hue < 2) { + green = chroma; + red = second_component; + } else if (hue < 3) { + green = chroma; + blue = second_component; + } else if (hue < 4) { + blue = chroma; + green = second_component; + } else if (hue < 5) { + blue = chroma; + red = second_component; + } else { + red = chroma; + blue = second_component; + } + double match_lightness = lightness - chroma / 2; + red += match_lightness; + green += match_lightness; + blue += match_lightness; + std::stringstream ss; + ss << "#"; + ss << std::hex << std::setfill('0') << std::setw(6) + << (static_cast(red * 255) << 16 | + static_cast(green * 255) << 8 | static_cast(blue * 255)); + return ss.str(); +} + +std::string SolutionToGraph(const Problem &problem, + const DistanceMatrix &distance_matrix, + const splitils::Solution &solution, float scale, + bool load, bool open) { + std::stringstream ss; + ss << "digraph{\n"; + ss << "layout=neato\n"; + ss << "splines=curved\n"; + ss << "node[margin=0shape=circle]\n"; + ss << "scale=" << scale << '\n'; + std::vector route_heads; + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + route_heads.emplace_back(node_index); + } + } + ss << "label=\"num_route=" << route_heads.size() + << " total_distance=" << CalcObjective(solution, distance_matrix) + << "\"\n"; + std::vector>> color_labels( + problem.num_customers); + double hue_base = 360.0 / route_heads.size(); + for (Node i = 0; i < static_cast(route_heads.size()); ++i) { + Node predecessor = 0; + Node successor = route_heads[i]; + std::string color = "color=\"" + HslToRgb(i * hue_base, 1, 0.4) + "\""; + while (true) { + if (!open || (predecessor && successor)) { + ss << solution.customer(predecessor) << "->" + << solution.customer(successor) << "[" << color << "]\n"; + } + if (!successor) { + break; + } + color_labels[solution.customer(successor)].emplace_back( + solution.load(successor), color); + predecessor = successor; + successor = solution.successor(successor); + } + } + for (Node i = 0; i < problem.num_customers; ++i) { + auto customer = problem.customers[i]; + ss << i << "[pos=\"" << customer.x << "," << customer.y << "!\""; + if (load && !color_labels[i].empty()) { + if (color_labels[i].size() == 1) { + ss << "font" << color_labels[i][0].second + << "xlabel=" << color_labels[i][0].first; + } else { + ss << "xlabel=<"; + bool first = true; + for (auto &&kv : color_labels[i]) { + if (!first) { + ss << "
"; + } + first = false; + ss << "" << kv.first << ""; + } + ss << '>'; + } + } + ss << "]\n"; + } + ss << '}'; + return ss.str(); +} + +std::string UrlHashEncode(const std::string &value) { + std::stringstream escaped; + escaped.fill('0'); + escaped << std::hex; + for (char c : value) { + if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~' || + c == ',' || c == '=' || c == '/' || c == '!') { + escaped << c; + continue; + } + escaped << std::uppercase; + escaped << '%' << std::setw(2) << int((unsigned char)c); + escaped << std::nouppercase; + } + return escaped.str(); +} + +void ShowGraphUrl(const std::string &graph) { + std::cerr << "https://edotor.net/?engine=dot#" + UrlHashEncode(graph) + << std::endl; +} + +} // namespace vrp::sdvrp diff --git a/src/sdvrp/visualization.h b/src/sdvrp/visualization.h new file mode 100644 index 0000000..877c15f --- /dev/null +++ b/src/sdvrp/visualization.h @@ -0,0 +1,22 @@ +#ifndef DIMACS_12_SRC_SDVRP_VISUALIZATION_H_ +#define DIMACS_12_SRC_SDVRP_VISUALIZATION_H_ + +#include + +#include "sdvrp/distance_matrix.h" +#include "sdvrp/problem.h" +#include "sdvrp/splitils/model/solution.h" + +namespace vrp::sdvrp { + +std::string SolutionToGraph(const Problem &problem, + const DistanceMatrix &distance_matrix, + const splitils::Solution &solution, + float scale = 0.5, bool load = true, + bool open = true); + +void ShowGraphUrl(const std::string &graph); + +} // namespace vrp::sdvrp + +#endif // DIMACS_12_SRC_SDVRP_VISUALIZATION_H_ diff --git a/src/util/acceptance_rule.h b/src/util/acceptance_rule.h new file mode 100644 index 0000000..978ad75 --- /dev/null +++ b/src/util/acceptance_rule.h @@ -0,0 +1,121 @@ +#ifndef ALKAID_SD_SRC_UTIL_ACCEPTANCE_RULE_H_ +#define ALKAID_SD_SRC_UTIL_ACCEPTANCE_RULE_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#include "util/random.h" + +namespace alkaid_sd { + +template class AcceptanceRule { +public: + virtual ~AcceptanceRule() = default; + virtual bool Accept(T old_value, T new_value, Random &random) = 0; +}; + +template class HillClimbing : public AcceptanceRule { +public: + bool Accept(T old_value, T new_value, Random &random) override { + return new_value < old_value; + } +}; + +template class HillClimbingWithEqual : public AcceptanceRule { +public: + bool Accept(T old_value, T new_value, Random &random) override { + return new_value <= old_value; + } +}; + +template class LateAcceptanceHillClimbing : public AcceptanceRule { +public: + explicit LateAcceptanceHillClimbing(int length) + : length_(length), position_(0), + values_(length, std::numeric_limits::max()) {} + + bool Accept(T old_value, T new_value, Random &random) override { + bool accepted = false; + if (new_value <= old_value || new_value < values_[position_]) { + old_value = new_value; + accepted = true; + } + if (old_value < values_[position_]) { + values_[position_] = old_value; + } + position_ = (position_ + 1) % length_; + return accepted; + } + +private: + int length_; + int position_; + std::vector values_; +}; + +template class SimulatedAnnealing : public AcceptanceRule { +public: + SimulatedAnnealing(double initial_temperature, double decay) + : temperature_(initial_temperature), decay_(decay) {} + + bool Accept(T old_value, T new_value, Random &random) override { + bool accepted = + new_value <= old_value || + random.NextFloat() < exp((old_value - new_value) / temperature_); + temperature_ *= decay_; + return accepted; + } + +private: + double temperature_; + double decay_; +}; + +} // namespace alkaid_sd + +namespace nlohmann { +template +struct adl_serializer< + std::function>()>> { + static void + from_json(const json &j, + std::function>()> + &acceptance_criteria) { + if (j["type"].get() == "LAHC") { + auto length = j["length"].get(); + acceptance_criteria = [length]() { + return std::make_unique>( + length); + }; + } else if (j["type"].get() == "SA") { + auto initial_temperature = j["initial_temperature"].get(); + auto decay = j["decay"].get(); + acceptance_criteria = [initial_temperature, decay]() { + return std::make_unique>( + initial_temperature, decay); + }; + } else if (j["type"].get() == "HCWE") { + acceptance_criteria = []() { + return std::make_unique>(); + }; + } else { + acceptance_criteria = []() { + return std::make_unique>(); + }; + } + } + + static void to_json( + json &, + const std::function>()> &) {} +}; + +} // namespace nlohmann + +#endif // ALKAID_SD_SRC_UTIL_ACCEPTANCE_RULE_H_ diff --git a/src/util/delta.h b/src/util/delta.h new file mode 100644 index 0000000..8924376 --- /dev/null +++ b/src/util/delta.h @@ -0,0 +1,43 @@ +#ifndef ALKAID_SD_SRC_UTIL_DELTA_H_ +#define ALKAID_SD_SRC_UTIL_DELTA_H_ + +#include "util/random.h" + +namespace alkaid_sd { + +template struct Delta { + T value; + int counter; + + inline Delta() : value(T()), counter(-1) {} + + inline Delta(T value, int counter) : value(value), counter(counter) {} + + inline bool Update(T new_value, Random &random) { + if (new_value < value) { + value = new_value; + counter = 1; + return true; + } else if (new_value == value && counter != -1) { + ++counter; + return random.NextInt(1, counter) == 1; + } + return false; + } + + inline bool Update(const Delta &delta, Random &random) { + if (delta.value < value) { + value = delta.value; + counter = delta.counter; + return true; + } else if (delta.value == value && counter != -1) { + counter += delta.counter; + return random.NextInt(1, counter) <= delta.counter; + } + return false; + } +}; + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_UTIL_DELTA_H_ diff --git a/src/util/mcmf.h b/src/util/mcmf.h new file mode 100644 index 0000000..0ddc22f --- /dev/null +++ b/src/util/mcmf.h @@ -0,0 +1,76 @@ +#ifndef ALKAID_SD_SRC_UTIL_MCMF_H_ +#define ALKAID_SD_SRC_UTIL_MCMF_H_ + +#include + +namespace alkaid_sd { + +struct MCMF { + const int inf = static_cast(1e9); + + struct Node { + int from, to, nxt, cap, flow, cost; + }; + + std::vector dis, g, pre, vis; + std::vector e; + int n; + + MCMF(int n) : n(n), dis(n), g(n, -1), pre(n), vis(n) {} + + int Link(int u, int v, int f, int c) { + int m = static_cast(e.size()); + e.push_back(Node{u, v, g[u], f, 0, c}); + g[u] = m; + e.push_back(Node{v, u, g[v], 0, 0, -c}); + g[v] = m + 1; + return m; + } + + bool Extend(int s, int t) { + std::fill(vis.begin(), vis.end(), 0); + std::fill(dis.begin(), dis.end(), inf); + std::queue queue; + dis[s] = 0; + queue.push(s); + while (!queue.empty()) { + int u = queue.front(); + queue.pop(); + vis[u] = false; + for (int it = g[u]; ~it; it = e[it].nxt) { + int v = e[it].to; + if (e[it].cap > e[it].flow && dis[v] > dis[u] + e[it].cost) { + dis[v] = dis[u] + e[it].cost; + pre[v] = it; + if (!vis[v]) { + queue.push(v); + } + vis[v] = true; + } + } + } + return dis[t] < inf; + } + + std::pair Solve(int s, int t) { + int max_flow = 0; + int min_cost = 0; + while (Extend(s, t)) { + int delta = inf; + for (int u = t; u != s; u = e[pre[u]].from) { + delta = std::min(delta, e[pre[u]].cap - e[pre[u]].flow); + } + min_cost += delta * dis[t]; + max_flow += delta; + for (int u = t; u != s; u = e[pre[u]].from) { + e[pre[u]].flow += delta; + e[pre[u] ^ 1].flow -= delta; + } + } + return {max_flow, min_cost}; + } +}; + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_UTIL_MCMF_H_ diff --git a/src/util/random.h b/src/util/random.h new file mode 100644 index 0000000..a1eadb4 --- /dev/null +++ b/src/util/random.h @@ -0,0 +1,105 @@ +#ifndef ALKAID_SD_SRC_UTIL_RANDOM_H_ +#define ALKAID_SD_SRC_UTIL_RANDOM_H_ + +#include + +namespace alkaid_sd { + +class Random { +public: + explicit Random(uint32_t seed) : s() { + s[0] = seed; + for (int i = 1; i < 4; ++i) { + s[i] = static_cast(ScrambleWell(s[i - 1], i)); + } + } + + inline int NextInt(int a, int b) { + return static_cast(NextInt(b - a + 1)) + a; + } + + inline bool NextBoolean() { return NextInt() & 1u; } + + inline float NextFloat() { return (NextInt() >> 8u) * kFloatMultiplier; } + + inline Random Jump() { + Random random = *this; + uint32_t s0 = 0; + uint32_t s1 = 0; + uint32_t s2 = 0; + uint32_t s3 = 0; + for (unsigned int i : kJump) + for (uint32_t b = 0; b < 32; b++) { + if (i & 1u << b) { + s0 ^= s[0]; + s1 ^= s[1]; + s2 ^= s[2]; + s3 ^= s[3]; + } + NextInt(); + } + s[0] = s0; + s[1] = s1; + s[2] = s2; + s[3] = s3; + return random; + } + + template void Shuffle(RandomIt first, RandomIt last) { + typename std::iterator_traits::difference_type i, n; + n = last - first; + for (i = n - 1; i > 0; --i) { + std::swap(first[i], first[NextInt(0, static_cast(i))]); + } + } + +private: + static constexpr uint32_t kJump[] = {0x8764000b, 0xf542d2d3, 0x6fa035c3, + 0x77f2db5b}; + static constexpr uint64_t kPow32 = uint64_t(1) << 32u; + static constexpr float kFloatMultiplier = 0x1.0p-24f; + + uint32_t s[4]; + + static inline uint32_t RotateLeft(const uint32_t x, uint32_t k) { + return (x << k) | (x >> (32 - k)); + } + + static inline uint64_t Scramble(uint64_t n, uint64_t multiple, uint32_t shift, + uint32_t add) { + return multiple * (n ^ (n >> shift)) + add; + } + + static inline uint64_t ScrambleWell(uint64_t n, int add) { + return Scramble(n, 1812433253, 30, add); + } + + inline uint32_t NextInt() { + const uint32_t result = RotateLeft(s[0] + s[3], 7) + s[0]; + const uint32_t t = s[1] << 9u; + s[2] ^= s[0]; + s[3] ^= s[1]; + s[1] ^= s[2]; + s[0] ^= s[3]; + s[2] ^= t; + s[3] = RotateLeft(s[3], 11); + return result; + } + + inline uint32_t NextInt(uint32_t n) { + uint64_t m = static_cast(NextInt()) * n; + uint64_t l = m & 0xffffffff; + if (l < n) { + uint64_t t = kPow32 % n; + while (l < t) { + m = (NextInt() & 0xffffffff) * n; + l = m & 0xffffffff; + } + } + return m >> 32u; + } +}; + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_UTIL_RANDOM_H_ diff --git a/src/util/solution_utils.cc b/src/util/solution_utils.cc new file mode 100644 index 0000000..e9baf0f --- /dev/null +++ b/src/util/solution_utils.cc @@ -0,0 +1,180 @@ +#include "util/solution_utils.h" + +#include +#include +#include +#include +#include + +namespace alkaid_sd { + +int RemoveNode(const DistanceMatrix &distance_matrix, Node node_index, + Node route_index, Solution &solution, RouteContext &context) { + Node customer = solution.customer(node_index); + Node predecessor = solution.predecessor(node_index); + Node successor = solution.successor(node_index); + int delta = distance_matrix[solution.customer(predecessor)] + [solution.customer(successor)]; + delta -= distance_matrix[customer][solution.customer(predecessor)] + + distance_matrix[customer][solution.customer(successor)]; + solution.Remove(node_index); + if (predecessor == 0) { + context.heads[route_index] = successor; + } + UpdateRouteContext(solution, route_index, predecessor, context); + return delta; +} + +int CalcObjective(const Solution &solution, + const DistanceMatrix &distance_matrix) { + int objective = 0; + const Node *node_pool = solution.node_pool(); + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = node_pool[i]; + Node predecessor = solution.predecessor(node_index); + Node successor = solution.successor(node_index); + objective += distance_matrix[solution.customer(node_index)] + [solution.customer(predecessor)]; + if (successor == 0) { + objective += distance_matrix[solution.customer(node_index)][0]; + } + } + return objective; +} + +void CheckFeasible(const Problem &problem, const Solution &solution) { + std::set unused_node_indices( + solution.node_pool(), solution.node_pool() + solution.num_nodes()); + std::vector customer_load_sums(problem.num_customers); + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + Node predecessor = 0; + Node route_load = 0; + while (node_index) { + if (solution.predecessor(node_index) != predecessor) { + throw std::runtime_error( + "Predecessor and successor are not consistent."); + } + if (!unused_node_indices.erase(node_index)) { + throw std::runtime_error("Some node is visited twice."); + } + Load load = solution.load(node_index); + Node customer = solution.customer(node_index); + if (customer <= 0 || customer >= problem.num_customers) { + throw std::runtime_error("Invalid customer index."); + } + customer_load_sums[customer] += load; + route_load += load; + predecessor = node_index; + node_index = solution.successor(node_index); + } + if (route_load > problem.capacity) { + throw std::runtime_error("Route load exceeds the capacity."); + } + } + } + if (!unused_node_indices.empty()) { + throw std::runtime_error("Some node leaks."); + } + for (Node i = 1; i < problem.num_customers; ++i) { + if (customer_load_sums[i] < problem.customers[i].demand) { + throw std::runtime_error("Some customer is not served fully."); + } + if (customer_load_sums[i] > problem.customers[i].demand) { + throw std::runtime_error("Some customer is served exceedingly."); + } + } +} + +void CheckRouteContextHeads(const Solution &solution, + const RouteContext &context) { + std::set heads; + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + heads.insert(node_index); + } + } + for (Node i = 0; i < context.num_routes; ++i) { + Node head = context.heads[i]; + if (head == 0) { + continue; + } + if (!heads.erase(head)) { + throw std::runtime_error("Some head is not truly head."); + } + } + if (!heads.empty()) { + throw std::runtime_error("Some head is not recorded."); + } +} + +void ReversedLink(Solution &solution, Node left, Node right, Node predecessor, + Node successor) { + while (true) { + Node original_predecessor = solution.predecessor(right); + solution.Link(predecessor, right); + if (right == left) { + break; + } + predecessor = right; + right = original_predecessor; + } + solution.Link(left, successor); +} + +void IgnoreUntilChar(char c, std::istream &stream) { + stream.ignore(std::numeric_limits::max(), stream.widen(c)); +} + +Solution ReadSolutionToStream(std::istream &stream) { + Solution solution; + std::string prefix; + while (stream >> prefix && prefix == "Route") { + std::string line; + std::getline(stream, line); + std::istringstream is(line); + IgnoreUntilChar('-', is); + Node predecessor = 0; + Node customer; + while (is >> customer && customer) { + IgnoreUntilChar('(', is); + Load load; + is >> load; + predecessor = solution.Insert(customer, load, predecessor, 0); + IgnoreUntilChar('-', is); + } + } + return solution; +} + +void WriteSolutionToStream(const DistanceMatrix &distance_matrix, + const Solution &solution, + const std::string &processor, double time, + std::ostream &stream) { + Node num_routes = 0; + int total_cost = 0; + for (Node i = 0; i < solution.num_nodes(); ++i) { + Node node_index = solution.node_pool()[i]; + if (!solution.predecessor(node_index)) { + stream << "Route " << ++num_routes << ": 0"; + Node predecessor = 0; + while (node_index) { + Node customer = solution.customer(node_index); + stream << " - " << customer << " ( " << solution.load(node_index) + << " )"; + total_cost += distance_matrix.original[predecessor][customer]; + predecessor = customer; + node_index = solution.successor(node_index); + } + stream << " - 0\n"; + total_cost += distance_matrix.original[predecessor][0]; + } + } + stream << total_cost << '\n'; + stream << processor << std::endl; + stream << time << std::endl; +} + +} // namespace alkaid_sd diff --git a/src/util/solution_utils.h b/src/util/solution_utils.h new file mode 100644 index 0000000..75ee4fd --- /dev/null +++ b/src/util/solution_utils.h @@ -0,0 +1,37 @@ +#ifndef ALKAID_SD_SRC_UTIL_SOLUTION_UTILS_H_ +#define ALKAID_SD_SRC_UTIL_SOLUTION_UTILS_H_ + +#include +#include +#include + +#include "distance_matrix.h" +#include "model/route_context.h" +#include "model/solution.h" + +namespace alkaid_sd { + +int RemoveNode(const DistanceMatrix &distance_matrix, Node node_index, + Node route_index, Solution &solution, RouteContext &context); + +int CalcObjective(const Solution &solution, + const DistanceMatrix &distance_matrix); + +void CheckFeasible(const Problem &problem, const Solution &solution); + +void CheckRouteContextHeads(const Solution &solution, + const RouteContext &context); + +void ReversedLink(Solution &solution, Node left, Node right, Node predecessor, + Node successor); + +Solution ReadSolutionToStream(std::istream &stream); + +void WriteSolutionToStream(const DistanceMatrix &distance_matrix, + const Solution &solution, + const std::string &processor, double time, + std::ostream &stream); + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_UTIL_SOLUTION_UTILS_H_ diff --git a/src/util/submit.cc b/src/util/submit.cc new file mode 100644 index 0000000..4eee9b6 --- /dev/null +++ b/src/util/submit.cc @@ -0,0 +1,11 @@ +#include "util/submit.h" + +#include + +namespace alkaid_sd { + +void Submit(const BaseTimer &baseTimer, int objective) { + std::cout << baseTimer.ElapsedTime() << ',' << objective << std::endl; +} + +} // namespace alkaid_sd diff --git a/src/util/submit.h b/src/util/submit.h new file mode 100644 index 0000000..c5028ca --- /dev/null +++ b/src/util/submit.h @@ -0,0 +1,12 @@ +#ifndef ALKAID_SD_SRC_UTIL_SUBMIT_H_ +#define ALKAID_SD_SRC_UTIL_SUBMIT_H_ + +#include "util/timer.h" + +namespace alkaid_sd { + +void Submit(const BaseTimer &timer, int objective); + +} + +#endif // ALKAID_SD_SRC_UTIL_SUBMIT_H_ diff --git a/src/util/timer.cc b/src/util/timer.cc new file mode 100644 index 0000000..c2eb5ff --- /dev/null +++ b/src/util/timer.cc @@ -0,0 +1,35 @@ +#include "util/timer.h" + +namespace alkaid_sd { + +bool BaseTimer::IsTimeOut() const { return ElapsedTime() > time_limit_; } + +void CpuTimer::Set(double time_limit) { + time_limit_ = time_limit; + start_ = std::clock(); +} + +double CpuTimer::ElapsedTime() const { + return static_cast(std::clock() - start_) / CLOCKS_PER_SEC; +} + +void RealTimer::Set(double time_limit) { + time_limit_ = time_limit; + start_ = std::chrono::high_resolution_clock::now(); +} + +double RealTimer::ElapsedTime() const { + return std::chrono::duration_cast>( + std::chrono::high_resolution_clock::now() - start_) + .count(); +} + +std::unique_ptr CreateTimer(TimerType timer_type) { + if (timer_type == kCpu) { + return std::make_unique(); + } else { + return std::make_unique(); + } +} + +} // namespace alkaid_sd diff --git a/src/util/timer.h b/src/util/timer.h new file mode 100644 index 0000000..cfcf2ce --- /dev/null +++ b/src/util/timer.h @@ -0,0 +1,49 @@ +#ifndef ALKAID_SD_SRC_UTIL_TIMER_H_ +#define ALKAID_SD_SRC_UTIL_TIMER_H_ + +#include +#include +#include + +#include + +namespace alkaid_sd { + +class BaseTimer { +public: + virtual ~BaseTimer() = default; + virtual void Set(double time_limit) = 0; + [[nodiscard]] bool IsTimeOut() const; + [[nodiscard]] virtual double ElapsedTime() const = 0; + +protected: + double time_limit_{}; +}; + +class CpuTimer : public BaseTimer { +public: + void Set(double time_limit) override; + [[nodiscard]] double ElapsedTime() const override; + +private: + std::clock_t start_{}; +}; + +class RealTimer : public BaseTimer { +public: + void Set(double time_limit) override; + [[nodiscard]] double ElapsedTime() const override; + +private: + std::chrono::high_resolution_clock::time_point start_{}; +}; + +enum TimerType { kCpu, kReal }; + +NLOHMANN_JSON_SERIALIZE_ENUM(TimerType, {{kCpu, "cpu"}, {kReal, "real"}}) + +std::unique_ptr CreateTimer(TimerType timer_type); + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_UTIL_TIMER_H_ diff --git a/src/util/utils.h b/src/util/utils.h new file mode 100644 index 0000000..8836529 --- /dev/null +++ b/src/util/utils.h @@ -0,0 +1,57 @@ +#ifndef ALKAID_SD_SRC_UTIL_UTILS_H_ +#define ALKAID_SD_SRC_UTIL_UTILS_H_ + +#include "model/route_context.h" +#include "model/solution.h" +#include "util/delta.h" + +namespace alkaid_sd { + +template struct InsertionWithCost { + Node predecessor; + Node successor; + Node route_index; + Delta cost; + + inline bool Update(const InsertionWithCost &insertion, Random &random) { + if (cost.Update(insertion.cost, random)) { + predecessor = insertion.predecessor; + successor = insertion.successor; + route_index = insertion.route_index; + return true; + } + return false; + } +}; + +template +auto CalcBestInsertion(const Solution &solution, const Func &func, + const RouteContext &context, Node route_index, + Node customer, Random &random) { + Node head = context.heads[route_index]; + auto head_cost = func(0, head, customer); + InsertionWithCost best_insertion{0, head, route_index, + Delta(head_cost, 1)}; + Node node_index = head; + while (node_index) { + auto cost = func(node_index, solution.successor(node_index), customer); + if (best_insertion.cost.Update(cost, random)) { + best_insertion.predecessor = node_index; + best_insertion.successor = solution.successor(node_index); + } + node_index = solution.successor(node_index); + } + return best_insertion; +} + +inline Node CalcFleetLowerBound(const Problem &problem) { + int sum_demands = 0; + for (Node i = 1; i < problem.num_customers; ++i) { + sum_demands += problem.customers[i].demand; + } + return (sum_demands + problem.capacity - 1) / problem.capacity; +} + +} // namespace alkaid_sd + +#endif // ALKAID_SD_SRC_UTIL_UTILS_H_