UOJ Logo ClHg2的博客

博客

改进蚁群算法

2023-06-28 03:18:29 By ClHg2
  1. 每轮迭代只允许一只蚂蚁留下信息素。交替使用当前轮最优和历史最优。
  2. 令蒸发量比较小以探索全图,并控制信息素在一个合理的区间。

令 $\beta=4$,$m=1.5n$。

对于 $n$ 较小的数据,令 $\alpha=1$ 或 $1.5$,$\rho=0.1$;对于 $n$ 较大的数据,令 $\alpha=2$,$\rho=0.05$。然后基本上就能一遍过大部分的点,并且能在大部分的点吊打 std。

#include <algorithm>
#include <array>
#include <chrono>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <random>
#include <string>

namespace {
using std::cin;
using std::clog;
using std::cout;
using std::endl;
using std::int64_t;
using std::size_t;

namespace base {
template <typename T, size_t... sizes>
struct NestedArray {};

template <typename T, size_t size, size_t... sizes>
struct NestedArray<T, size, sizes...> {
  using Type = std::array<typename NestedArray<T, sizes...>::Type, size>;
};

template <typename T>
struct NestedArray<T> {
  using Type = T;
};

template <typename T, size_t... sizes>
using Array = typename NestedArray<T, sizes...>::Type;

void OptimizeIO() {
  std::ios::sync_with_stdio(false);
  cin.tie(nullptr), cout.tie(nullptr);
}

void OptimizeIO(const std::string &input_file, const std::string &output_file) {
  static std::ifstream input_stream(input_file);
  static std::ofstream output_stream(output_file);
  cin.rdbuf(input_stream.rdbuf());
  cout.rdbuf(output_stream.rdbuf());
  cin.tie(nullptr), cout.tie(nullptr);
}
}  // namespace base

using base::Array;

const int kMaxN = 1005, kT = 200, kMinPheromone = 1, kMaxPheromone = 20;
const int64_t kInf = 1.0e18 + 5, kQ = 5.0e9;
const double kAlpha = 2, kBeta = 4, kRho = 0.9,
             kTau = std::pow(kMaxPheromone, kAlpha);
int n, m, type;
int64_t ans = kInf, res;
Array<int, kMaxN> path, it_best_path, his_best_path;
Array<bool, kMaxN> vis;
Array<int, kMaxN, kMaxN> g;
Array<double, kMaxN, kMaxN> eta, p, pheromone;
std::mt19937 rng(std::chrono::system_clock().now().time_since_epoch().count());

inline int RandInt(int l, int r) {
  return std::uniform_int_distribution<>(l, r)(rng);
}

inline double RandDouble(double l, double r) {
  return std::uniform_real_distribution<>(l, r)(rng);
}

void Go(int s) {
  path[1] = path[n + 1] = s;
  std::fill_n(vis.begin() + 1, n, false), vis[s] = true;

  for (int i = 2; i <= n; ++i) {
    int u = path[i - 1];
    double sum = 0;

    for (int j = 1; j <= n; ++j) {
      if (!vis[j]) sum += p[u][j];
    }

    double r = RandDouble(0, sum);

    for (int j = 1; j <= n; ++j) {
      if (vis[j]) continue;

      if (r <= p[u][j]) {
        path[i] = j, vis[j] = true;
        break;
      } else {
        r -= p[u][j];
      }
    }
  }

  int64_t len = 0;
  for (int i = 1; i <= n; ++i) len += g[path[i]][path[i + 1]];

  if (res > len) {
    res = len, std::copy_n(path.begin() + 1, n + 1, it_best_path.begin() + 1);
  }
}

void Update(const Array<int, kMaxN> &path, int64_t len) {
  double x = static_cast<double>(kQ) / len;
  for (int i = 1; i <= n; ++i) pheromone[path[i]][path[i + 1]] += x;
}

void Iterate() {
  res = kInf;
  for (int i = 1; i <= m; ++i) Go(RandInt(1, n));

  if (ans > res) {
    ans = res, clog << ans << endl;
    std::copy_n(it_best_path.begin() + 1, n + 1, his_best_path.begin() + 1);
  }

  type ^= 1;

  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) pheromone[i][j] *= kRho;
  }

  type ? Update(his_best_path, ans) : Update(it_best_path, res);

  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
      if (pheromone[i][j] > kMaxPheromone) pheromone[i][j] = kMaxPheromone;
      if (pheromone[i][j] < kMinPheromone) pheromone[i][j] = kMinPheromone;
      p[i][j] = std::pow(pheromone[i][j], kAlpha) * eta[i][j];
    }
  }
}

int Main() {
  base::OptimizeIO();
  cin >> n, m = n * 1.5;

  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
      cin >> g[i][j];
      eta[i][j] = 1 / std::pow(g[i][j], kBeta), p[i][j] = kTau * eta[i][j];
    }

    std::fill_n(pheromone[i].begin() + 1, n, kMaxPheromone);
  }

  for (int i = 1; i <= kT; ++i) {
    Iterate();
    clog << "it #" << i << " done." << endl;
  }

  cout << ans << "\n";
  for (int i = 1; i <= n; ++i) cout << his_best_path[i] << "\n";
  return 0;
}
}  // namespace

int main() { return Main(); }

评论

ClHg2
我还剩最后两个点没跑完的时候把这两个优化告诉了 Reliauk。他原本还剩下 2 个小数据点跑不过去,知道这两个优化之后光速码完并通过了(小数据跑得比大数据快,所以他甚至稍微比我早跑完一些)。/qd
Reliauk
感谢 Calvin 的援助,Calvin 好闪

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。