281. Java 5, 11628 bytes, 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 debug)
- อย่า เคย ใช้. ชวา 1. เป็น verbose มากกว่า Java โดยทั่วไป
สิ่งนี้อาจทำลายโซ่
- ช่องว่าง (7 วันและ 48 นาที) คือไม่เกินช่องว่างที่สร้างขึ้นโดยคำตอบนี้ซึ่งเป็น 7 วัน 1 ชั่วโมง 25 นาทีต่อมากว่าหนึ่งก่อนหน้านี้
สถิติใหม่ในจำนวนมหาศาล! เพราะฉัน (ผิดพลาด?) ใช้ช่องว่างแทนแท็บจำนวนไบต์จึงมีขนาดใหญ่เกินความจำเป็น บนเครื่องของฉันมันคือ9550ไบต์ (ในขณะที่เขียนการแก้ไขนี้)
- ลำดับถัดไป
- รหัสในรูปแบบปัจจุบันพิมพ์เฉพาะ 20 คำแรกของลำดับ แต่มันเป็นเรื่องง่ายที่จะเปลี่ยนเพื่อที่จะพิมพ์ 1000 รายการแรก (จากการเปลี่ยนแปลง
20
ในfor (int i = 0; i < 20; ++i)
การ1000
)
เย้! สิ่งนี้สามารถคำนวณคำศัพท์ได้มากกว่าที่ระบุไว้ในหน้า OEIS! (เป็นครั้งแรกสำหรับความท้าทายที่ฉันต้องใช้ Java) เว้นแต่ OEIS จะมีเงื่อนไขเพิ่มเติมที่ไหนซักแห่ง ...
คำอธิบายด่วน
คำอธิบายของคำอธิบายลำดับ
ลำดับถามถึงจำนวนโพลินอยด์ nonplanar ฟรีที่มีกลุ่มสมมาตร C 2vโดยที่:
- polyenoid: (แบบจำลองทางคณิตศาสตร์ของ polyene ไฮโดรคาร์บอน) ต้นไม้ (หรือในกรณีเลว, จุดสุดยอดเดียว) ด้วยสามารถฝังในตาข่ายหกเหลี่ยม
ตัวอย่างเช่นพิจารณาต้นไม้
O O O O (3)
| \ / \
| \ / \
O --- O --- O O --- O O --- O
| \
| (2) \
(1) O O
อันแรกไม่สามารถฝังในตาข่ายหกเหลี่ยมได้ในขณะที่อันที่สองสามารถทำได้ การฝังนั้นจะถือว่าแตกต่างจากทรีที่สาม
- nonplanar polyenoid: การฝังต้นไม้ดังกล่าวซึ่งมีจุดยอดสองจุดซ้อนทับกันอยู่
(2)
และ(3)
ต้นไม้ด้านบนเป็นภาพถ่าย อย่างไรก็ตามอันนี้ไม่ใช่ nonplanar:
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) เพื่อรับสิทธิ์ ฉันไม่ได้อ่านคำอธิบายในกระดาษ
[สิ่งที่ต้องแก้ไขส่วนนี้]
แน่นอนการนับภาพถ่ายเป็นเรื่องง่ายกว่าการนับ nonplanar นั่นคือสิ่งที่กระดาษทำเช่นกัน
(ไม่รวมจุดยอดที่ซ้อนทับกัน) จะถูกระบุด้วยการเขียนโปรแกรมคอมพิวเตอร์ ดังนั้นตัวเลขของโพลีโนไนด์ที่ไม่ใช่เชิงเรขาคณิตจึงสามารถเข้าถึงได้
ดังนั้น ... โปรแกรมนับจำนวนของโพลารอยด์ตามระนาบและลบออกจากยอดรวม
เพราะต้นไม้นั้นเป็นภาพถ่ายแนวราบมันจึงมี■
ระนาบการสะท้อน ดังนั้นเงื่อนไขจึงลดลงเหลือ "นับจำนวนต้นไม้ด้วยแกนสะท้อนในการแสดงแบบ 2 มิติ"
วิธีที่ไร้เดียงสาจะสร้างต้นไม้ทั้งหมดที่มี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
และจากนั้นขยายโหนดดังกล่าวว่า สิ่งนี้ทำโดยส่วน "Reflection Type 1" ของโปรแกรมและจากนั้นสำหรับกราฟที่สร้างขึ้นแต่ละอันสะท้อน (mirror) ผ่านแกน 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 เช่นกัน