281. Java 5, 11628 байт, A000947
// package oeis_challenge;
import java.util.*;
import java.lang.*;
class Main {
// static void assert(boolean cond) {
// if (!cond)
// throw new Error("Assertion failed!");
// }
/* Use the formula a(n) = A000063(n + 2) - A000936(n).
It's unfair that I use the formula of "number of free polyenoid with n
nodes and symmetry point group C_{2v}" (formula listed in A000063)
without understanding why it's true...
*/
static int catalan(int x) {
int ans = 1;
for (int i = 1; i <= x; ++i)
ans = ans * (2*x+1-i) / i;
return ans / -~x;
}
static int A63(int n) {
int ans = catalan(n/2 - 1);
if (n%4 == 0) ans -= catalan(n/4 - 1);
if (n%6 == 0) ans -= catalan(n/6 - 1);
return ans;
}
static class Point implements Comparable<Point> {
final int x, y;
Point(int _x, int _y) {
x = _x; y = _y;
}
/// @return true if this is a point, false otherwise (this is a vector)
public boolean isPoint() {
return (x + y) % 3 != 0;
}
/// Translate this point by a vector.
public Point add(Point p) {
assert(this.isPoint() && ! p.isPoint());
return new Point(x + p.x, y + p.y);
}
/// Reflect this point along x-axis.
public Point reflectX() {
return new Point(x - y, -y);
}
/// Rotate this point 60 degrees counter-clockwise.
public Point rot60() {
return new Point(x - y, x);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point p = (Point) o;
return x == p.x && y == p.y;
}
@Override
public int hashCode() {
return 21521 * (3491 + x) + y;
}
public String toString() {
// return String.format("(%d, %d)", x, y);
return String.format("setxy %d %d", x * 50 - y * 25, y * 40);
}
public int compareTo(Point p) {
int a = Integer.valueOf(x).compareTo(p.x);
if (a != 0) return a;
return Integer.valueOf(y).compareTo(p.y);
}
/// Helper class.
static interface Predicate {
abstract boolean test(Point p);
}
static abstract class UnaryFunction {
abstract Point apply(Point p);
}
}
static class Edge implements Comparable<Edge> {
final Point a, b; // guarantee a < b
Edge(Point x, Point y) {
assert x != y;
if (x.compareTo(y) > 0) { // y < x
a = y; b = x;
} else {
a = x; b = y;
}
}
public int compareTo(Edge e) {
int x = a.compareTo(e.a);
if (x != 0) return x;
return b.compareTo(e.b);
}
}
/// A graph consists of multiple {@code Point}s.
static class Graph {
private HashMap<Point, Point> points;
public Graph() {
points = new HashMap<Point, Point>();
}
public Graph(Graph g) {
points = new HashMap<Point, Point>(g.points);
}
public void add(Point p, Point root) {
assert(p.isPoint());
assert(root.isPoint());
assert(p == root || points.containsKey(root));
points.put(p, root);
}
public Graph map(Point.UnaryFunction fn) {
Graph result = new Graph();
for (Map.Entry<Point, Point> pq : points.entrySet()) {
Point p = pq.getKey(), q = pq.getValue();
assert(p.isPoint()) : p;
assert(q.isPoint()) : q;
p = fn.apply(p); assert(p.isPoint()) : p;
q = fn.apply(q); assert(q.isPoint()) : q;
result.points.put(p, q);
}
return result;
}
public Graph reflectX() {
return this.map(new Point.UnaryFunction() {
public Point apply(Point p) {
return p.reflectX();
}
});
}
public Graph rot60() {
return this.map(new Point.UnaryFunction() {
public Point apply(Point p) {
return p.rot60();
}
});
}
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (o.getClass() != getClass()) return false;
Graph g = (Graph) o;
return points.equals(g.points);
}
@Override
public int hashCode() {
return points.hashCode();
}
Graph[] expand(Point.Predicate fn) {
List<Graph> result = new ArrayList<Graph>();
for (Point p : points.keySet()) {
int[] deltaX = new int[] { -1, 0, 1, 1, 0, -1};
int[] deltaY = new int[] { 0, 1, 1, 0, -1, -1};
for (int i = 6; i --> 0;) {
Point p1 = new Point(p.x + deltaX[i], p.y + deltaY[i]);
if (points.containsKey(p1) || !fn.test(p1)
|| !p1.isPoint()) continue;
Graph g = new Graph(this);
g.add(p1, p);
result.add(g);
}
}
return result.toArray(new Graph[0]);
}
public static Graph[] expand(Graph[] graphs, Point.Predicate fn) {
Set<Graph> result = new HashSet<Graph>();
for (Graph g0 : graphs) {
Graph[] g = g0.expand(fn);
for (Graph g1 : g) {
if (result.contains(g1)) continue;
result.add(g1);
}
}
return result.toArray(new Graph[0]);
}
private Edge[] edges() {
List<Edge> result = new ArrayList<Edge>();
for (Map.Entry<Point, Point> pq : points.entrySet()) {
Point p = pq.getKey(), q = pq.getValue();
if (p.equals(q)) continue;
result.add(new Edge(p, q));
}
return result.toArray(new Edge[0]);
}
/**
* Check if two graphs are isomorphic... under translation.
* @return {@code true} if {@code this} is isomorphic
* under translation, {@code false} otherwise.
*/
public boolean isomorphic(Graph g) {
if (points.size() != g.points.size()) return false;
Edge[] a = this.edges();
Edge[] b = g.edges();
Arrays.sort(a);
Arrays.sort(b);
// for (Edge e : b)
// System.err.println(e.a + " - " + e.b);
// System.err.println("------- >><< ");
assert (a.length > 0);
assert (a.length == b.length);
int a_bx = a[0].a.x - b[0].a.x, a_by = a[0].a.y - b[0].a.y;
for (int i = 0; i < a.length; ++i) {
if (a_bx != a[i].a.x - b[i].a.x ||
a_by != a[i].a.y - b[i].a.y) return false;
if (a_bx != a[i].b.x - b[i].b.x ||
a_by != a[i].b.y - b[i].b.y) return false;
}
return true;
}
// C_{2v}.
public boolean correctSymmetry() {
Graph[] graphs = new Graph[6];
graphs[0] = this.reflectX();
for (int i = 1; i < 6; ++i) graphs[i] = graphs[i-1].rot60();
assert(graphs[5].rot60().isomorphic(graphs[0]));
int count = 0;
for (Graph g : graphs) {
if (this.isomorphic(g)) ++count;
// if (count >= 2) {
// return false;
// }
}
// if (count > 1) System.err.format("too much: %d%n", count);
assert(count > 0);
return count == 1; // which is, basically, true
}
public void reflectSelfType2() {
Graph g = this.map(new Point.UnaryFunction() {
public Point apply(Point p) {
return new Point(p.y - p.x, p.y);
}
});
Point p = new Point(1, 1);
assert (p.equals(points.get(p)));
points.putAll(g.points);
assert (p.equals(points.get(p)));
Point q = new Point(0, 1);
assert (q.equals(points.get(q)));
points.put(p, q);
}
public void reflectSelfX() {
Graph g = this.reflectX();
points.putAll(g.points); // duplicates doesn't matter
}
}
static int A936(int n) {
// if (true) return (new int[]{0, 0, 0, 1, 1, 2, 4, 4, 12, 10, 29, 27, 88, 76, 247, 217, 722, 638, 2134, 1901, 6413})[n];
// some unreachable codes here for testing.
int ans = 0;
if (n % 2 == 0) { // reflection type 2. (through line 2x == y)
Graph[] graphs = new Graph[1];
graphs[0] = new Graph();
Point p = new Point(1, 1);
graphs[0].add(p, p);
for (int i = n / 2 - 1; i --> 0;)
graphs = Graph.expand(graphs, new Point.Predicate() {
public boolean test(Point p) {
return 2*p.x > p.y;
}
});
int count = 0;
for (Graph g : graphs) {
g.reflectSelfType2();
if (g.correctSymmetry()) {
++count;
// for (Edge e : g.edges())
// System.err.println(e.a + " - " + e.b);
// System.err.println("------*");
}
// else System.err.println("Failed");
}
assert (count%2 == 0);
// System.err.println("A936(" + n + ") count = " + count + " -> " + (count/2));
ans += count / 2;
}
// Reflection type 1. (reflectX)
Graph[] graphs = new Graph[1];
graphs[0] = new Graph();
Point p = new Point(1, 0);
graphs[0].add(p, p);
if (n % 2 == 0) graphs[0].add(new Point(2, 0), p);
for (int i = (n-1) / 2; i --> 0;)
graphs = Graph.expand(graphs, new Point.Predicate() {
public boolean test(Point p) {
return p.y > 0;
}
});
int count = 0;
for (Graph g : graphs) {
g.reflectSelfX();
if (g.correctSymmetry()) {
++count;
// for (Edge e : g.edges())
// System.err.printf(
// "pu %s pd %s\n"
// // "%s - %s%n"
// , e.a, e.b);
// System.err.println("-------/");
}
// else System.err.println("Failed");
}
if(n % 2 == 0) {
assert(count % 2 == 0);
count /= 2;
}
ans += count;
// System.err.println("A936(" + n + ") = " + ans);
return ans;
}
public static void main(String[] args) {
// Probably
if (! "1.5.0_22".equals(System.getProperty("java.version"))) {
System.err.println("Warning: Java version is not 1.5.0_22");
}
// A936(6);
for (int i = 0; i < 20; ++i)
System.out.println(i + " | " + (A63(i+9) - A936(i+7)));
//A936(i+2);
}
}
Попробуйте онлайн!
Примечание:
- Протестировано локально с Java 5. (чтобы предупреждение не выводилось - см. Вкладку отладки TIO)
- Не. Когда-либо. Используйте. Ява. 1. Это более многословно, чем Java в целом.
Это может разорвать цепь.
- Разрыв (7 дней и 48 минут) не больше, чем разрыв, созданный этим ответом , который на 7 дней и 1 час 25 минут позже предыдущего .
Новый рекорд на большой счет! Поскольку я (ошибочно?) Использую пробелы вместо вкладок, количество байтов больше, чем необходимо. На моей машине это 9550 байт. (на момент написания этой редакции)
- Следующая последовательность .
- Код в его текущей форме печатает только первые 20 членов последовательности. Однако это легко изменить , чтобы он печатает первые 1000 пунктов ( в результате изменения в
20ин for (int i = 0; i < 20; ++i)к 1000)
Ура! Это может вычислить больше терминов, чем указано на странице OEIS! (впервые, для испытания мне нужно использовать Java), если у OEIS где-то больше терминов ...
Быстрое объяснение
Объяснение описания последовательности.
В последовательности задают номер свободного непланарного полиеноида с группой симметрии C 2v , где:
- Полиеноид: (математическая модель полиеновых углеводородов) деревьев (или в вырожденном случае, одной вершины) с может быть встроен в гексагональную решетку.
Например, рассмотрим деревья
O O O O (3)
| \ / \
| \ / \
O --- O --- O O --- O O --- O
| \
| (2) \
(1) O O
Первый не может быть встроен в гексагональную решетку, а второй может. Это конкретное вложение считается отличным от третьего дерева.
- неплоский полиеноид: вложение деревьев так, что существует две перекрывающиеся вершины.
(2)а (3)дерево сверху плоское. Этот, однако, является неплоским:
O---O O
/ \
/ \
O O
\ /
\ /
O --- O
(есть 7 вершин и 6 ребер)
- свободный полиеноид: Варианты одного полиеноида, которые могут быть получены вращением и отражением, считаются единым целым.

- Группа C 2v : Полиеноиды учитываются, только если они имеют 2 перпендикулярные плоскости отражения и не более.
Например, единственный полиеноид с 2 вершинами
O --- O
имеет 3 плоскости отражения: горизонтальную -, вертикальную |и параллельную экрану компьютера ■. Это слишком много.
С другой стороны, этот
O --- O
\
\
O
имеет 2 плоскости отражения: /а ■.
Объяснение метода
А теперь подход о том, как на самом деле считать число.
Во-первых, я принимаю формулу a(n) = A000063(n + 2) - A000936(n)(приведенную на странице OEIS) как должное. Я не читал объяснение в газете.
[TODO исправить эту часть]
Конечно, считать планар проще, чем считать непланарно. Это то, что делает бумага тоже.
Геометрически плоские полиеноиды (без перекрывающихся вершин) подсчитываются с помощью компьютерного программирования. Таким образом, число геометрически неплоских полиеноидов становится доступным.
Итак ... программа подсчитывает количество плоских полиеноидов и вычитает их из общего числа.
Поскольку дерево в любом случае является плоским, оно, очевидно, имеет ■плоскость отражения. Таким образом, условие сводится к тому, чтобы «посчитать количество деревьев с осью отражения в его 2D представлении».
Наивным способом было бы сгенерировать все деревья с nузлами и проверить правильность симметрии. Однако, поскольку нам нужно только найти количество деревьев с осью отражения, мы можем просто сгенерировать все возможные полудерева на одной половине, отразить их через ось и затем проверить правильность симметрии. Более того, поскольку сгенерированные полиеноиды являются (плоскими) деревьями, он должен касаться оси отражения ровно один раз.
Функция public static Graph[] expand(Graph[] graphs, Point.Predicate fn)принимает массив графиков, каждый из которых имеет nузлы, и выводит массив графиков, каждый из которых имеет n+1узлы, не равные друг другу (при переводе), так что добавленный узел должен удовлетворять предикату fn.
Рассмотрим 2 возможных оси отражения: одна проходит через вершину и совпадает с ребрами ( x = 0), а другая - перпендикулярная биссектриса ребра ( 2x = y). Мы можем взять только один из них, потому что сгенерированные графы, в любом случае, изоморфны.

Итак, для первой оси x = 0мы начинаем с базового графа, состоящего из одного узла (1, 0)(в случае nнечетного) или двух узлов с ребром между (1, 0) - (2, 0)(в случае nчетного), а затем расширяем узлы так, чтобы y > 0. Это делается в разделе «Тип отражения 1» программы, а затем для каждого сгенерированного графа отражается (отражается) через ось X x = 0( g.reflectSelfX()), а затем проверяется, имеет ли он правильную симметрию.
Однако обратите внимание, что если nделится на 2, таким образом мы посчитали каждый граф дважды, потому что мы также генерируем его зеркальное отображение по оси 2x = y + 3.

(обратите внимание на 2 оранжевых)
Аналогично для оси 2x = y, если (и только если) nявляется четным, мы начинаем с точки (1, 1), генерируем графики таким образом, чтобы 2*x > yи отражали каждый из них по 2x = yоси ( g.reflectSelfType2()), соединялись (1, 0)с ними (1, 1)и проверяли, имеют ли они правильную симметрию. Не забудьте разделить на 2 тоже.