|
| 1 | +/* |
| 2 | + * Solution to Project Euler problem 165 |
| 3 | + * Copyright (c) Project Nayuki. All rights reserved. |
| 4 | + * |
| 5 | + * https://www.nayuki.io/page/project-euler-solutions |
| 6 | + * https://github.com/nayuki/Project-Euler-solutions |
| 7 | + */ |
| 8 | + |
| 9 | +import java.math.BigInteger; |
| 10 | +import java.util.ArrayList; |
| 11 | +import java.util.HashSet; |
| 12 | +import java.util.List; |
| 13 | +import java.util.Objects; |
| 14 | +import java.util.Set; |
| 15 | + |
| 16 | + |
| 17 | +public final class p165 implements EulerSolution { |
| 18 | + |
| 19 | + public static void main(String[] args) { |
| 20 | + System.out.println(new p165().run()); |
| 21 | + } |
| 22 | + |
| 23 | + |
| 24 | + private static final int NUM_LINE_SEGMENTS = 5000; |
| 25 | + |
| 26 | + |
| 27 | + public String run() { |
| 28 | + BbsRandom rand = new BbsRandom(); |
| 29 | + List<LineSegment> lines = new ArrayList<>(); |
| 30 | + for (int i = 0; i < NUM_LINE_SEGMENTS; i++) |
| 31 | + lines.add(new LineSegment(rand)); |
| 32 | + |
| 33 | + Set<Point> trueIntersections = new HashSet<>(); |
| 34 | + Fraction FRAC_ONE = new Fraction(bi(1)); |
| 35 | + for (int i = 0; i < lines.size(); i++) { |
| 36 | + LineSegment seg0 = lines.get(i); |
| 37 | + for (int j = i + 1; j < lines.size(); j++) { |
| 38 | + LineSegment seg1 = lines.get(j); |
| 39 | + |
| 40 | + int x0 = seg0.x0, y0 = seg0.y0; |
| 41 | + int x1 = seg0.x1, y1 = seg0.y1; |
| 42 | + int x2 = seg1.x0, y2 = seg1.y0; |
| 43 | + int x3 = seg1.x1, y3 = seg1.y1; |
| 44 | + |
| 45 | + // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line_segment |
| 46 | + int denom = (x0 - x1) * (y2 - y3) - (x2 - x3) * (y0 - y1); |
| 47 | + if (denom == 0) { |
| 48 | + // There is no unique intersection point between the two infinite lines. This is equivalent to |
| 49 | + // {one or both line segments being just a point, or both line segments being parallel |
| 50 | + // (regardless of whether or not they lie on the same infinite line)}. |
| 51 | + continue; |
| 52 | + } |
| 53 | + int numer0 = (x0 - x2) * (y2 - y3) - (x2 - x3) * (y0 - y2); |
| 54 | + int numer1 = (x1 - x0) * (y0 - y2) - (x0 - x2) * (y1 - y0); |
| 55 | + |
| 56 | + Fraction t0 = new Fraction(bi(numer0), bi(denom)); |
| 57 | + Fraction t1 = new Fraction(bi(numer1), bi(denom)); |
| 58 | + if (Fraction.ZERO.compareTo(t0) < 0 && t0.compareTo(FRAC_ONE) < 0 && |
| 59 | + Fraction.ZERO.compareTo(t1) < 0 && t1.compareTo(FRAC_ONE) < 0) { |
| 60 | + Point p = new Point( |
| 61 | + frac(x0).add(t0.multiply(frac(x1 - x0))), |
| 62 | + frac(y0).add(t0.multiply(frac(y1 - y0)))); |
| 63 | + trueIntersections.add(p); |
| 64 | + } |
| 65 | + } |
| 66 | + } |
| 67 | + return Integer.toString(trueIntersections.size()); |
| 68 | + } |
| 69 | + |
| 70 | + |
| 71 | + private static Fraction frac(int x) { |
| 72 | + return new Fraction(bi(x)); |
| 73 | + } |
| 74 | + |
| 75 | + |
| 76 | + private static BigInteger bi(int x) { |
| 77 | + return BigInteger.valueOf(x); |
| 78 | + } |
| 79 | + |
| 80 | + |
| 81 | + |
| 82 | + // Blum Blum Shub generator |
| 83 | + private static final class BbsRandom { |
| 84 | + |
| 85 | + private int state = 290797; |
| 86 | + |
| 87 | + |
| 88 | + public int next() { |
| 89 | + state = (int)((long)state * state % 50515093); |
| 90 | + return state % 500; |
| 91 | + } |
| 92 | + |
| 93 | + } |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | + private static final class LineSegment { |
| 98 | + |
| 99 | + public final int x0, y0, x1, y1; |
| 100 | + |
| 101 | + |
| 102 | + public LineSegment(BbsRandom r) { |
| 103 | + x0 = r.next(); |
| 104 | + y0 = r.next(); |
| 105 | + x1 = r.next(); |
| 106 | + y1 = r.next(); |
| 107 | + } |
| 108 | + |
| 109 | + } |
| 110 | + |
| 111 | + |
| 112 | + |
| 113 | + private static final class Point { |
| 114 | + |
| 115 | + private final int xNumer, xDenom, yNumer, yDenom; |
| 116 | + |
| 117 | + |
| 118 | + public Point(Fraction x, Fraction y) { |
| 119 | + xNumer = x.numerator .intValueExact(); |
| 120 | + xDenom = x.denominator.intValueExact(); |
| 121 | + yNumer = y.numerator .intValueExact(); |
| 122 | + yDenom = y.denominator.intValueExact(); |
| 123 | + } |
| 124 | + |
| 125 | + |
| 126 | + public boolean equals(Object obj) { |
| 127 | + if (!(obj instanceof Point)) |
| 128 | + return false; |
| 129 | + Point other = (Point)obj; |
| 130 | + return xNumer == other.xNumer |
| 131 | + && xDenom == other.xDenom |
| 132 | + && yNumer == other.yNumer |
| 133 | + && yDenom == other.yDenom; |
| 134 | + } |
| 135 | + |
| 136 | + |
| 137 | + public int hashCode() { |
| 138 | + return Objects.hash(xNumer, xDenom, yNumer, yDenom); |
| 139 | + } |
| 140 | + |
| 141 | + } |
| 142 | + |
| 143 | +} |
0 commit comments