Numbers from 1 to N with two adjacent numbers being perfect squares - l1t1/note GitHub Wiki

https://github.1git.de/FluorineDog/square_sum

#include <vector>
#include <tuple>
#include <iostream>
#include <random>
#include <cmath>
#include <assert.h>
#include <algorithm>
using std::cout;
using std::vector;
int get_square_rt(int N) {
	static vector<int> cache(1, 0);
	if (cache.size() <= N) {
		for (int i = cache.size(); i <= 2 * N + 1; ++i) {
			int tmp = (int)sqrt(i + 0.5);
			assert((tmp + 1) * (tmp + 1) > i && i >= tmp * tmp);
			cache.push_back(tmp);
		}
	}
	return cache[N];
}
vector<int> search(int N) {
	vector<int> prefix;
	vector<char> slot(N + 3, false);
	int remained_max = N;
	prefix.push_back(N);
	remained_max = N - 1;
	slot[N] = true;
	slot[N + 1] = slot[N + 2] = slot[0] = true;
	int hint = N - 1;
	while (prefix.size() < N) {
		int record1 = prefix.back();
		int count = N - prefix.size();
		int s_beg = get_square_rt(hint + record1);
		for (int sum_it = s_beg; sum_it > 0; --sum_it) {
			int sum = sum_it * sum_it;
			int record2 = sum - record1;
			if (record2 < 1) {
				break;
			}
			if (!slot[record2]) {
				slot[record2] = true;
				if (record2 == remained_max) {
					for (int i = record2; i > 0; --i) {
						if (!slot[i]) {
							remained_max = i;
							break;
						}
					}
				}
				hint = remained_max;
				prefix.push_back(record2);
				goto while_end;
			}
		}
		prefix.pop_back();
		slot[record1] = false;
		remained_max = std::max(record1, remained_max);
		if (prefix.size()) {
			hint = record1 - 1;
		}
		else {
			if (record1 == 1) {
				return vector<int>();
			}
			prefix.push_back(record1 - 1);
		}
	while_end:
		;
	}
	return prefix;
}

bool is_square(int n) {
	int rt = get_square_rt(n);
	return rt * rt == n;
}


std::default_random_engine e;


class SelectEngine {
public:
	SelectEngine() : total_(0) {}
	template<typename T>
	void random_update(T& raw, const T& candidate, int freq = 1) {
		total_ = freq + total_;
		if (e() % total_ < freq) {
			// update		
			raw = candidate;
		}
	}
private:
	int total_;
};

bool check_arr(vector<int>& wtf) {
	for (int i = 0; i < wtf.size() - 1; ++i) {
		bool hh = is_square(wtf[i] + wtf[i + 1]);
		if (!hh) {
			return false;
		}
	}
	return true;
}


enum class Status {
	PerfectMatch,
	GoodMatch,
	TabuMatch,
	NoMatch
};


constexpr int tabu_N = 3000;
bool is_tabu(int step, vector<int>& tabu_table, int ep1, int ep2) {
	--ep1;
	--ep2;
	return step <= tabu_table[ep1 * tabu_N + ep2];
}

void update_tabu(int step, vector<int>& tabu_table, int ep1, int ep2) {
	--ep1;
	--ep2;
	tabu_table[ep1* tabu_N + ep2] = step;
	tabu_table[ep2* tabu_N + ep1] = step;
}

std::tuple<Status, int> find_best(vector<int>& left, int ep1, int ep2) {
	// the only permitted operation: 
	// spilt record1 into [record1_beg x] [y record1_end] || [ep1, ep2]
	// get [record1_beg x, ep1 ep2] 
	//     [y record1_end]
	using std::make_tuple;
	std::pair<int, size_t> normal_best;
	normal_best.first = tabu_N;
	normal_best.second = -1;
	SelectEngine normSelect;


	for (size_t index = 0; index < left.size() - 1; ++index) {
		auto x = left[index];
		auto y = left[index + 1];
		if (is_square(x + ep1)) {
			if (is_square(y + ep2)) {
				return make_tuple(Status::PerfectMatch, index);
			}
			auto new_pair = std::make_pair(y, index);
			normSelect.random_update(normal_best, new_pair);
		}
	}

	if (normal_best.first == tabu_N) {
		return make_tuple(Status::NoMatch, -1);
	}
	else return make_tuple(Status::GoodMatch, normal_best.second);
}


vector<int> tabu_insert(vector<int> raw, int N, int max) {
	assert(N == max);
	auto record1 = raw;
	auto record2 = vector<int>(1, N);
	int tabu_step = N / 2;
	//vector<int> tabu_table(max * tabu_N, -tabu_step * 2);
	int step = 0;
	constexpr int max_step = 100000;
	while (step < max_step) {

		step += 1;
		auto record1_rev = record1;
		auto record2_rev = record2;
		std::reverse(record1_rev.begin(), record1_rev.end());
		std::reverse(record2_rev.begin(), record2_rev.end());
		auto extract = [&](int exec_id) {
			assert(exec_id < 8 && exec_id >= 0);
			switch (exec_id) {
			case 0: return std::make_pair(&record1, &record2);
			case 1: return std::make_pair(&record1_rev, &record2);
			case 2: return std::make_pair(&record1, &record2_rev);
			case 3: return std::make_pair(&record1_rev, &record2_rev);
			case 4: return std::make_pair(&record2, &record1);
			case 5: return std::make_pair(&record2_rev, &record1);
			case 6: return std::make_pair(&record2, &record1_rev);
			case 7: return std::make_pair(&record2_rev, &record1_rev);
			default: return std::make_pair((vector<int>*)nullptr, (vector<int>*)nullptr);
			}
		};
		for (int id = 0; id < 4; ++id) {
			auto[left_, right_] = extract(id);
			auto& left = *left_;
			auto& right = *right_;
			if (is_square(left.back() + right.front())) {
				// perfect match
				auto res = left;
				res.insert(res.end(), right.begin(), right.end());
				return res;
			}
		}
		int exec_id = -1;
		auto exec_status = Status::NoMatch;
		int exec_penalty = 0;
		for (int id = 0; id < 8; ++id) {
			auto[left_, right_] = extract(id);
			auto& left = *left_;
			auto& right = *right_;
			auto[status, iter] = find_best(left, right.front(), right.back());
			if (status == Status::NoMatch || status > exec_status) {
				continue;
			}
			assert(iter >= 0 && iter < left.size() - 1);
			auto penalty = left[iter + 1] - right.front();
			if (status < exec_status) {
				exec_status = status;
				exec_id = id;
				exec_penalty = penalty;
			}
			else {
				assert(status == exec_status);
				if (penalty < exec_penalty) {
					exec_status = status;
					exec_id = id;
					exec_penalty = penalty;
				}
			}
		}
		assert(exec_id != -1);
		{
			auto[left_, right_] = extract(exec_id);
			auto left = *left_;
			auto right = *right_;
			auto[status, iter] = find_best(left, right.front(), right.back());
			assert(is_square(left[iter] + right.front()));
			assert(iter >= 0 && iter < left.size() - 1);
			record1 = vector<int>(left.begin() + iter + 1, left.end());
			record2 = vector<int>(left.begin(), left.begin() + iter + 1);
			record2.insert(record2.end(), right.begin(), right.end());
		}
	}
	return vector<int>();
}

int main() {
	// generate seed using brute force
	auto vec = search(40);

	cout << 40 << ":";
	for (auto x : vec) {
		cout << x << " ";
	}
	assert(check_arr(vec));

	for (int i = 41; i < 2001; ++i) {
		vec = tabu_insert(vec, i, i);
		if (vec.size() != i) {
			cout << "failed" << std::endl;
			break;
		}
		assert(check_arr(vec));
		if (i % 500 == 0) {
			cout << std::endl;
			cout << i << ":";
			for (auto x : vec) {
				cout << x << " ";
			}
			cout << std::endl;
		}
	}
	return 0;
}
⚠️ **GitHub.com Fallback** ⚠️