281. Java 5, 11628 byte, 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);
}
}
Cobalah online!
Catatan:
- Diuji secara lokal dengan Java 5. (sedemikian sehingga peringatan tidak dicetak - lihat tab debug TIO)
- Jangan. Pernah. Menggunakan. Jawa. 1. Ini lebih bertele-tele daripada Jawa secara umum.
Ini dapat memutus rantai.
- Kesenjangan (7 hari dan 48 menit) tidak lebih dari jarak yang dibuat oleh jawaban ini , yaitu 7 hari dan 1 jam 25 menit kemudian dari yang sebelumnya .
Rekor baru pada bytecount besar! Karena saya (secara keliru?) Menggunakan spasi alih-alih tab, bytecount lebih besar dari yang diperlukan. Di komputer saya ini adalah 9.550 byte. (pada saat penulisan revisi ini)
- Urutan berikutnya .
- Kode, dalam bentuk saat ini, hanya mencetak 20 syarat pertama dari urutan. Namun itu mudah untuk mengubah sehingga akan mencetak pertama 1000 item (oleh perubahan
20
dalam for (int i = 0; i < 20; ++i)
untuk 1000
)
Yay! Ini dapat menghitung lebih banyak istilah daripada yang tercantum pada halaman OEIS! (untuk pertama kalinya, untuk tantangan saya perlu menggunakan Java) kecuali OEIS memiliki lebih banyak istilah di suatu tempat ...
Penjelasan cepat
Penjelasan deskripsi urutan.
Urutan meminta jumlah polyenoid nonplanar gratis dengan kelompok simetri C 2v , di mana:
- polyenoid: (model matematika polyene hydrocarbon) pohon (atau dalam kasus degenerate, satu vertex) dengan dapat tertanam dalam kisi heksagonal.
Sebagai contoh, perhatikan pohon-pohonnya
O O O O (3)
| \ / \
| \ / \
O --- O --- O O --- O O --- O
| \
| (2) \
(1) O O
Yang pertama tidak bisa tertanam dalam kisi heksagonal, sedangkan yang kedua bisa. Penanaman khusus itu dianggap berbeda dari pohon ketiga.
- nonplanar polyenoid: penanaman pohon sedemikian rupa sehingga ada dua simpul yang tumpang tindih.
(2)
dan (3)
pohon di atas adalah planar. Yang ini, bagaimanapun, adalah nonplanar:
O---O O
/ \
/ \
O O
\ /
\ /
O --- O
(ada 7 simpul dan 6 tepi)
- polyenoid bebas: Varian dari satu polyenoid, yang dapat diperoleh dengan rotasi dan refleksi, dihitung sebagai satu.
- Kelompok C 2v : Polyenoid hanya dihitung jika mereka memiliki 2 bidang pantulan tegak lurus, dan tidak lebih.
Misalnya, satu-satunya polyenoid dengan 2 simpul
O --- O
memiliki 3 bidang refleksi: Yang horizontal -
, yang vertikal |
, dan yang sejajar dengan layar komputer ■
. Itu terlalu banyak.
Di sisi lain, yang ini
O --- O
\
\
O
memiliki 2 bidang refleksi: /
dan ■
.
Penjelasan metode
Dan sekarang, pendekatan tentang bagaimana sebenarnya menghitung angka.
Pertama, saya menerima formula a(n) = A000063(n + 2) - A000936(n)
(terdaftar pada halaman OEIS) begitu saja. Saya tidak membaca penjelasan di koran.
[TODO memperbaiki bagian ini]
Tentu saja, menghitung planar lebih mudah daripada menghitung nonplanar. Itu yang dilakukan koran juga.
Polenoid planar secara geometris (tanpa simpul yang tumpang tindih) dihitung dengan pemrograman komputer. Dengan demikian jumlah polenoid nonplanar yang geometris dapat diakses.
Jadi ... program menghitung jumlah polenoid planar, dan kurangi dari total.
Karena pohon itu planar, jelas memiliki ■
bidang pantulan. Jadi kondisinya bermuara pada "hitung jumlah pohon dengan sumbu refleksi dalam representasi 2D-nya".
Cara naif akan menghasilkan semua pohon dengan n
node, dan periksa simetri yang benar. Namun, karena kami hanya ingin menemukan jumlah pohon dengan sumbu refleksi, kami hanya dapat menghasilkan semua setengah pohon yang mungkin pada satu setengah, mirror mereka melalui sumbu, dan kemudian periksa simetri yang benar. Selain itu, karena polenoid yang dihasilkan adalah pohon (planar), ia harus menyentuh sumbu refleksi tepat sekali.
Fungsi ini public static Graph[] expand(Graph[] graphs, Point.Predicate fn)
mengambil array grafik, masing-masing memiliki n
node, dan output array grafik, masing-masing memiliki n+1
node, tidak sama satu sama lain (dalam terjemahan) - sehingga simpul yang ditambahkan harus memenuhi predikat fn
.
Pertimbangkan 2 sumbu refleksi yang mungkin: Yang melewati titik dan bertepatan dengan tepi ( x = 0
), dan yang merupakan garis-bagi tegak lurus dari tepi ( 2x = y
). Kita hanya dapat mengambil salah satunya saja karena grafik yang dihasilkan adalah isomorfik.
Jadi, untuk sumbu pertama x = 0
, kita mulai dari grafik dasar yang terdiri dari satu simpul (1, 0)
(dalam kasus n
ganjil) atau dua node dengan tepi antara (1, 0) - (2, 0)
(dalam kasus n
genap), dan kemudian perluas simpul sedemikian rupa y > 0
. Itu dilakukan oleh bagian "Refleksi tipe 1" dari program, dan kemudian untuk setiap grafik yang dihasilkan, refleksikan (mirror) itu sendiri melalui sumbu X x = 0
( g.reflectSelfX()
), dan kemudian periksa apakah ia memiliki simetri yang benar.
Namun, perhatikan bahwa jika n
habis dibagi 2, dengan cara ini kami menghitung setiap grafik dua kali, karena kami juga menghasilkan gambar cermin oleh sumbu 2x = y + 3
.
(perhatikan 2 yang oranye)
Mirip dengan sumbu 2x = y
, jika (dan hanya jika) n
adalah genap, kita mulai dari titik (1, 1)
, menghasilkan grafik sedemikian rupa 2*x > y
, dan merefleksikan masing-masing dari mereka di atas 2x = y
sumbu ( g.reflectSelfType2()
), terhubung (1, 0)
dengan (1, 1)
, dan periksa apakah mereka memiliki simetri yang benar. Ingatlah untuk membagi dengan 2 juga.