<< problem 164 - Numbers for which no three consecutive digits ... | Criss Cross - problem 166 >> |

# Problem 165: Intersections

(see projecteuler.net/problem=165)

A segment is uniquely defined by its two endpoints.

By considering two line segments in plane geometry there are three possibilities:

the segments have zero points, one point, or infinitely many points in common.

Moreover when two segments have exactly one point in common it might be the case that that common point is an endpoint of either one of the segments or of both.

If a common point of two segments is not an endpoint of either of the segments it is an interior point of both segments.

We will call a common point T of two segments L_1 and L_2 a true intersection point of L_1 and L_2 if T is the only common point of L_1 and L_2 and T is an interior point of both segments.

Consider the three segments L_1, L_2, and L_3:

L_1: (27, 44) to (12, 32)

L_2: (46, 53) to (17, 62)

L_3: (46, 70) to (22, 40)

It can be verified that line segments L_2 and L_3 have a true intersection point.

We note that as the one of the end points of L_3: (22,40) lies on L_1 this is not considered to be a true point of intersection.

L_1 and L_2 have no common point. So among the three line segments, we find one true intersection point.

Now let us do the same for 5000 line segments. To this end, we generate 20000 numbers using the so-called "Blum Blum Shub" pseudo-random number generator.

s_0 = 290797

s_{n+1} = s_n * s_n (modulo 50515093)

t_n = s_n (modulo 500)

To create each line segment, we use four consecutive numbers tn. That is, the first line segment is given by:

(t_1, t_2) to (t_3, t_4)

The first four numbers computed according to the above generator should be: 27, 144, 12 and 232. The first segment would thus be (27,144) to (12,232).

How many distinct true intersection points are found among the 5000 line segments?

# My Algorithm

I studied Computer Graphics at university and had no problem coming up with an intersection algorithm.

Wikipedia's explanations of the use of the determinant are somehow weird and I would have trouble unterstanding it - but I already knew that from classes at university.

The main issue inside my `intersect`

code is numerical stability.

These three lines reduce the number of digits so that the correct result is found:

`const auto Precision = 0.00000001;`

`where.x = round(where.x / Precision) * Precision;`

`where.y = round(where.y / Precision) * Precision;`

The constant `Precision`

was found by trial-and-error.

The pseudo-random Blum Blum shub algorithm can be converted to a few lines of code (see `next`

).

My code provides two simple classes `Point`

and `Segment`

to keep things organized.

`Point`

needs to support comparisons operators for `std::sort`

and `std::unique`

(weeding out duplicate intersections).

`main`

finds all (!) intersections and defers checking for duplicates until the end.

Duplicates are eliminated as follows:

- sort all intersections → identical intersections will be neighboring elements in the sorted set

- call `std::unique`

to move all duplicates to the end of the data container ("garbage")

- remove all garbage points

# My code

… was written in C++11 and can be compiled with G++, Clang++, Visual C++. You can download it, too.

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
// generate pseudo-random numbers (Blum Blum Shub algorithm)

unsigned int next()
{
static unsigned long long seed = 290797;
seed *= seed;
seed %= 50515093;
return seed % 500;
}
// a 2D point

struct Point
{
double x, y;
// needed for std::unique
bool operator==(const Point& other) const
{
return x == other.x && y == other.y;
}
// needed for std::sort
bool operator< (const Point& other) const
{
if (x != other.x)
return x < other.x;
else
return y < other.y;
}
};
// define a segment

struct Segment
{
Point from, to;
};
// find intersection of two segments, out parameter "where" is only valid if function returns true

bool intersect(const Segment& segment1, const Segment& segment2, Point& where)
{
// shorter names for the four endpoints
auto a = segment1.from;
auto b = segment1.to;
auto c = segment2.from;
auto d = segment2.to;
// store slope in a Point (just because I'm lazy and don't want to introduce another data type)
Point slope1, slope2;
slope1.x = b.x - a.x;
slope1.y = b.y - a.y;
slope2.x = d.x - c.x;
slope2.y = d.y - c.y;
auto determinant = slope1.x * slope2.y - slope2.x * slope1.y;
// parallel ?
if (determinant == 0)
return false;
// now the lines intersect, but not necessarily the segments
auto s = (slope1.x * (a.y - c.y) - slope1.y * (a.x - c.x)) / determinant;
auto t = (slope2.x * (a.y - c.y) - slope2.y * (a.x - c.x)) / determinant;
// parameters s and t must be in (0 ... 1)
// borders (=endpoints) are not true intersections according to problem statement
if (s <= 0 || s >= 1 || t <= 0 || t >= 1)
return false;
// yes, intersection found (might be a duplicate, though !)
where.x = a.x + t * slope1.x;
where.y = a.y + t * slope1.y;
// cut off a few digits to avoid rounding issues
const auto Precision = 0.00000001;
where.x = round(where.x / Precision) * Precision;
where.y = round(where.y / Precision) * Precision;
return true;
}
int main()
{
std::vector<Segment> segments;
std::vector<Point> intersections;
unsigned int limit = 5000;
std::cin >> limit;
for (unsigned int i = 0; i < limit; i++)
{
// create "random" segment
Segment current;
current.from.x = next();
current.from.y = next();
current.to .x = next();
current.to .y = next();
// try to intersect with all other segments
Point where;
for (auto compare : segments)
if (intersect(current, compare, where))
intersections.push_back(where);
// add current segment to list of segments
segments.push_back(current);
}
// eliminate duplicate intersection points
std::sort(intersections.begin(), intersections.end());
auto garbage = std::unique(intersections.begin(), intersections.end());
intersections.erase(garbage, intersections.end());
// display result
std::cout << intersections.size() << std::endl;
return 0;
}

This solution contains 19 empty lines, 19 comments and 4 preprocessor commands.

# Interactive test

You can submit your own input to my program and it will be instantly processed at my server:

This is equivalent to`echo 10 | ./165`

Output:

*Note:* the original problem's input `5000`

__cannot__ be entered

because just copying results is a soft skill reserved for idiots.

*(this interactive test is still under development, computations will be aborted after one second)*

# Benchmark

The correct solution to the original Project Euler problem was found in 0.6 seconds on a Intel® Core™ i7-2600K CPU @ 3.40GHz.

Peak memory usage was about 69 MByte.

(compiled for x86_64 / Linux, GCC flags: `-O3 -march=native -fno-exceptions -fno-rtti -std=c++11 -DORIGINAL`

)

See here for a comparison of all solutions.

**Note:** interactive tests run on a weaker (=slower) computer. Some interactive tests are compiled without `-DORIGINAL`

.

# Changelog

June 20, 2017 submitted solution

June 20, 2017 added comments

# Hackerrank

see https://www.hackerrank.com/contests/projecteuler/challenges/euler165

My code solves **11** out of **11** test cases (score: **100%**)

# Difficulty

Project Euler ranks this problem at **65%** (out of 100%).

Hackerrank describes this problem as **easy**.

*Note:*

Hackerrank has strict execution time limits (typically 2 seconds for C++ code) and often a much wider input range than the original problem.

In my opinion, Hackerrank's modified problems are usually a lot harder to solve. As a rule thumb: brute-force is rarely an option.

# Links

projecteuler.net/thread=165 - **the** best forum on the subject (*note:* you have to submit the correct solution first)

# Heatmap

green problems solve the original Project Euler problem and have a perfect score of 100% at Hackerrank, too.

yellow problems score less than 100% at Hackerrank (but still solve the original problem).

gray problems are already solved but I haven't published my solution yet.

blue problems are solved and there wasn't a Hackerrank version of it at the time I solved it or I didn't care about it because it differed too much.

red problems are solved but exceed the time limit of one minute or the memory limit of 256 MByte.

*Please click on a problem's number to open my solution to that problem:*

1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |

26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |

51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |

76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |

126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 |

151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 |

176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 |

201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 |

226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 |

251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 |

276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 |

301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 |

326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 |

351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 |

376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 |

401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 |

426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 |

451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 |

476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 |

501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 |

526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 |

I scored 13,183 points (out of 15300 possible points, top rank was 17 out of ≈60000 in August 2017) at Hackerrank's Project Euler+.

Look at my progress and performance pages to get more details.

My username at Project Euler is

**stephanbrumme**while it's stbrumme at Hackerrank.

# Copyright

I hope you enjoy my code and learn something - or give me feedback how I can improve my solutions.

All of my solutions can be used for any purpose and I am in no way liable for any damages caused.

You can even remove my name and claim it's yours. But then you shall burn in hell.

The problems and most of the problems' images were created by Project Euler.

Thanks for all their endless effort.

<< problem 164 - Numbers for which no three consecutive digits ... | Criss Cross - problem 166 >> |