ライブラリ - (2006/05/23 (火) 02:35:14) の編集履歴(バックアップ)
平面幾何計算
class Double2D{ double x; double y; static final double EPS = 1.0E-9; Double2D(double x,double y){ this.x = x; this.y = y; } @Override public boolean equals(Object o){ Double2D p = (Double2D)o; return x==p.x&&y==p.y; } public double squareDist(Double2D p){ return (x-p.x)*(x-p.x) + (y-p.y)*(y-p.y); } public double dist(Double2D p){ return Math.sqrt(this.squareDist(p)); } // thisとpを通る半径rの円の中心 public Double2D[] centerOfPassingCircle1(Double2D p,double r){ if(this.dist(p)>r) return null; // 境界条件はテキトー double a = p.x - x; double b = p.y - y; double d = this.dist(p); double qx0 = x / 2 + p.x / 2 + Math.sqrt(r - d*d/4)*(-b)/d; double qy0 = y / 2 + p.y / 2 + Math.sqrt(r - d*d/4)*a/d; double qx1 = x / 2 + p.x / 2 - Math.sqrt(r - d*d/4)*(-b)/d; double qy1 = y / 2 + p.y / 2 - Math.sqrt(r - d*d/4)*a/d; Double2D[] ret = new Double2D[2]; ret[0] = new Double2D(qx0,qy0); ret[1] = new Double2D(qx1,qy1); return ret; } // 三点の作る三角形の面積 public static double squareOfTriangle(Double2D p1,Double2D p2,Double2D p3){ double x1 = p1.x-p2.x; double x2 = p3.x-p2.x; double y1 = p1.y-p2.y; double y2 = p3.y-p2.y; double s = (x1*y2-x2*y1)/2; return Math.abs(s); } // 多角形の内部にあるかどうか 自己ループはないものとする public static boolean inPolygon(ArrayList<Double2D> poly){ double acc = 0.0; int n = poly.size(); for(int i=0;i<n-1;i++){ acc += arg(poly.get(i),poly.get(i+1)); } acc += arg(poly.get(n-1),poly.get(0)); if(Math.abs(acc)<EPS) return false; else return true; } // -Pi < theta < Pi の範囲で偏角を返す 複素数でいう Arg(p2/p1) public static double arg(Double2D p1,Double2D p2){ double ax = p1.x; double ay = p1.y; double bx = p2.x; double by = p2.y; double theta = Math.atan2((ax*by-ay*bx)/(ax*ax+ay*ay),(ax*bx+ay*by)/(ax*ax+ay*ay)); return theta; } // 外接円の中心 Online Judge 検証なし 簡易検証あり public static Double2D centerOfCircumscribingCircle(Double2D p1, Double2D p2, Double2D p3){ double x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y, x3 = p3.x, y3 = p3.y; double det = (x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2)); if(Math.abs(det)<EPS) return null; double sx = -y1*(x2*x2+y2*y2-x3*x3-y3*y3) -y2*(x3*x3+y3*y3-x1*x1-y1*y1) -y3*(x1*x1+y1*y1-x2*x2-y2*y2); double sy = x1*(x2*x2+y2*y2-x3*x3-y3*y3) +x2*(x3*x3+y3*y3-x1*x1-y1*y1) +x3*(x1*x1+y1*y1-x2*x2-y2*y2); return new Double2D(0.5*sx/det, 0.5*sy/det); } } class Line{ double a; double b; double c; static final double EPS = 1.0E-9; Line(double a, double b, double c) { this.a = a; this.b = b; this.c = c; } Line(Double2D p1,Double2D p2){ this.a = p1.y-p2.y; this.b = -p1.x+p2.x; this.c = -p1.x*(p1.y-p2.y)+p1.y*(p1.x-p2.x); } // 線分と線分の交点 ない場合は null // 2線分が重なりをもつときは考えていない public Double2D crossPoint(Line l){ // 平行 if(Math.abs(this.a*l.b-this.b*l.a)<EPS){ return null; } else{ double x = (-l.b*this.c+this.b*l.c)/(this.a*l.b-this.b*l.a); double y = (l.a*this.c-this.a*l.c)/(this.a*l.b-this.b*l.a); return new Double2D(x,y); } } } class Circle{ double x; double y; double r; static final double EPS = 1.0E-9; Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r; } public double centerDist(Circle c){ return Math.sqrt((x-c.x)*(x-c.x)+(y-c.y)*(y-c.y)); } // 2円の交点 public Double2D[] crossPoint1(Circle c){ double d = centerDist(c); if(r+c.r > d) return null; // 境界条件はテキトー double d1 = (d*d + r*r - c.r*c.r)/(2*d); double ex = x + (c.x-x)*d1/d; double ey = y + (c.y-y)*d1/d; double l = Math.sqrt(r*r-d1*d1); double lx = -(c.y-y)*l/d; double ly = (c.x-x)*l/d; Double2D[] ret = new Double2D[2]; ret[0] = new Double2D(ex+lx, ey+ly); ret[1] = new Double2D(ex-lx, ey-ly); return ret; } // 1点を通る接線 public Line[] tangentLine(Double2D p){ Line[] ret = new Line[2]; if(Math.abs((x-p.x)*(x-p.x)-r*r)<EPS){ ret[0] = new Line(1, 0, -p.x); double m = ((y-p.y)*(y-p.y)-r*r)/(2*(x-p.x)*(y-p.y)); ret[1] = new Line(m, -1, -m*p.x+p.y); } else{ double m1 = ((x-p.x)*(y-p.y)+r*Math.sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)-r*r))/((x-p.x)*(x-p.x)-r*r); double m2 = ((x-p.x)*(y-p.y)-r*Math.sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)-r*r))/((x-p.x)*(x-p.x)-r*r); ret[0] = new Line(m1, -1, -m1*p.x+p.y); ret[1] = new Line(m2, -1, -m2*p.x+p.y); } return ret; } } class LineSegment{ Double2D p1; Double2D p2; static final double EPS = 1.0E-9; LineSegment(double x1, double y1, double x2, double y2){ p1 = new Double2D(x1, y1); p2 = new Double2D(x2, y2); } // 2線分が重なりをもつときは考えていない public Double2D crossPoint(LineSegment ls){ Double2D p3 = ls.p1; Double2D p4 = ls.p2; if(fcompare(Math.max(p1.x, p2.x), Math.min(p3.x, p4.x))<0 || fcompare(Math.max(p3.x, p4.x), Math.min(p1.x, p2.x))<0 || fcompare(Math.max(p1.y, p2.y), Math.min(p3.y, p4.y))<0 || fcompare(Math.max(p3.y, p4.y), Math.min(p1.y, p2.y))<0) return null; double dx00 = p2.x - p1.x; double dy00 = p2.y - p1.y; double dx01 = p3.x - p1.x; double dy01 = p3.y - p1.y; double dx02 = p4.x - p1.x; double dy02 = p4.y - p1.y; double dx10 = p4.x - p3.x; double dy10 = p4.y - p3.y; double dx11 = p1.x - p3.x; double dy11 = p1.y - p3.y; double dx12 = p2.x - p3.x; double dy12 = p2.y - p3.y; // 平行 if(Math.abs(dx00*dy10-dy00*dx10)<EPS){ if(p1.x==p3.x&&p1.y==p3.y) return p1; if(p1.x==p4.x&&p1.y==p4.y) return p1; if(p2.x==p3.x&&p2.y==p3.y) return p2; if(p2.x==p4.x&&p2.y==p4.y) return p2; } if(fcompare((dx00*dy01-dx01*dy00)*(dx00*dy02-dx02*dy00), 0)<=0 && fcompare((dx10*dy11-dx11*dy10)*(dx10*dy12-dx12*dy10), 0)<=0){ Line l1 = new Line(p1, p2); Line l2 = new Line(p3, p4); return l1.crossPoint(l2); } else return null; } private int fcompare(double a, double b){ if(Math.abs(a-b)<EPS) return 0; if(a>b) return 1; else return -1; }
}
高速Kruskal with 高速MFSet
O(NlogN)のMFsetを用いたKruskalのアルゴリズム.
class KruskalSolver{ int n; double[][] dist; double len; private PriorityQueue<Edge> q; KruskalSolver(int n,double dist[][]){ this.n = n; this.dist = dist; this.q = new PriorityQueue<Edge>(); this.len = 0.0; for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ if(dist[i][j]<Double.MAX_VALUE){ q.offer(new Edge(i, j, dist[i][j])); } } } //Kruskal's algorithm MFSet mfs = new MFSet(n); while(!q.isEmpty()){ Edge e = q.poll(); int i = e.p1; int j = e.p2; if(mfs.find(i)!=mfs.find(j)){ mfs.merge(mfs.find(i),mfs.find(j)); len += e.length; } } } } class Edge implements Comparable<Edge>{ int p1; int p2; double length; Edge(int i,int j,double l){ p1 = i; p2 = j; length = l; } public int compareTo(Edge e){ if(length>e.length) return 1; else if(length==e.length) return 0; else return -1; } } class MFSet{ int n; private int[] setsize; private int[] firstelem; private int[] set; private int[] next; MFSet(int n){ this.n = n; setsize = new int[n]; Arrays.fill(setsize, 1); firstelem = new int[n]; for(int i=0;i<n;i++) firstelem[i] = i; set = new int[n]; for(int i=0;i<n;i++) set[i] = i; next = new int[n]; for(int i=0;i<n;i++) next[i] = -1; } int find(int x){ return set[x]; } void merge(int a, int b){ if(setsize[a]<setsize[b]){ merge(b, a); return; } int i = firstelem[b]; while(next[i]>=0){ set[i] = a; i = next[i]; } set[i] = a; next[i] = firstelem[a]; firstelem[a] = firstelem[b]; setsize[a] = setsize[a] + setsize[b]; setsize[b] = 0; firstelem[b] = -1; } }
Kruskal
O(N^2)のMFSetを用いたKruskalのアルゴリズム.
class KruskalSolver{ int n; double[][] dist; double len; private PriorityQueue<Edge> q; int[] set; KruskalSolver(int n,double dist[][]){ this.n = n; this.dist = dist; this.len = 0.0; this.q = new PriorityQueue<Edge>(); this.set = new int[n]; for(int i=0;i<n;i++) set[i] = i; for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ if(dist[i][j]<Double.MAX_VALUE){ q.offer(new Edge(i, j, dist[i][j])); } } } //Kruskal's algorithm while(!q.isEmpty()){ Edge e = q.poll(); int i = e.p1; int j = e.p2; int si = set[i]; int sj = set[j]; if(si!=sj){ len += e.length; for(int k=0;k<n;k++){ if(set[k]==sj) set[k] = si; } } } } } class Edge implements Comparable<Edge>{ int p1; int p2; double length; Edge(int i,int j,double l){ p1 = i; p2 = j; length = l; } public int compareTo(Edge e){ if(length>e.length) return 1; else if(length==e.length) return 0; else return -1; } }