1 # [Project Euler, first problem: "Multiples of 3 and 5"](https://projecteuler.net/problem=1)
3 If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
5 Find the sum of all the multiples of 3 or 5 below 1000.
9 from notebook_preamble import J, V, define
12 Let's create a predicate that returns `True` if a number is a multiple of 3 or 5 and `False` otherwise.
16 define('P [3 % not] dupdip 5 % not or')
26 80 • [3 % not] dupdip 5 % not or
27 80 [3 % not] • dupdip 5 % not or
28 80 • 3 % not 80 5 % not or
29 80 3 • % not 80 5 % not or
39 Given the predicate function `P` a suitable program is:
41 PE1 == 1000 range [P] filter sum
43 This function generates a list of the integers from 0 to 999, filters
44 that list by `P`, and then sums the result.
46 Logically this is fine, but pragmatically we are doing more work than we
47 should be; we generate one thousand integers but actually use less than
48 half of them. A better solution would be to generate just the multiples
49 we want to sum, and to add them as we go rather than storing them and
50 adding summing them at the end.
52 At first I had the idea to use two counters and increase them by three
53 and five, respectively. This way we only generate the terms that we
54 actually want to sum. We have to proceed by incrementing the counter
55 that is lower, or if they are equal, the three counter, and we have to
56 take care not to double add numbers like 15 that are multiples of both
59 This seemed a little clunky, so I tried a different approach.
61 Consider the first few terms in the series:
63 3 5 6 9 10 12 15 18 20 21 ...
65 Subtract each number from the one after it (subtracting 0 from 3):
67 3 5 6 9 10 12 15 18 20 21 24 25 27 30 ...
68 0 3 5 6 9 10 12 15 18 20 21 24 25 27 ...
69 -------------------------------------------
70 3 2 1 3 1 2 3 3 2 1 3 1 2 3 ...
72 You get this lovely repeating palindromic sequence:
76 To make a counter that increments by factors of 3 and 5 you just add
77 these differences to the counter one-by-one in a loop.
80 To make use of this sequence to increment a counter and sum terms as we
81 go we need a function that will accept the sum, the counter, and the next
82 term to add, and that adds the term to the counter and a copy of the
83 counter to the running sum. This function will do that:
89 define('PE1.1 + [+] dupdip')
111 V('0 0 [3 2 1 3 1 2 3] [PE1.1] step')
114 • 0 0 [3 2 1 3 1 2 3] [PE1.1] step
115 0 • 0 [3 2 1 3 1 2 3] [PE1.1] step
116 0 0 • [3 2 1 3 1 2 3] [PE1.1] step
117 0 0 [3 2 1 3 1 2 3] • [PE1.1] step
118 0 0 [3 2 1 3 1 2 3] [PE1.1] • step
119 0 0 3 [PE1.1] • i [2 1 3 1 2 3] [PE1.1] step
120 0 0 3 • PE1.1 [2 1 3 1 2 3] [PE1.1] step
121 0 0 3 • + [+] dupdip [2 1 3 1 2 3] [PE1.1] step
122 0 3 • [+] dupdip [2 1 3 1 2 3] [PE1.1] step
123 0 3 [+] • dupdip [2 1 3 1 2 3] [PE1.1] step
124 0 3 • + 3 [2 1 3 1 2 3] [PE1.1] step
125 3 • 3 [2 1 3 1 2 3] [PE1.1] step
126 3 3 • [2 1 3 1 2 3] [PE1.1] step
127 3 3 [2 1 3 1 2 3] • [PE1.1] step
128 3 3 [2 1 3 1 2 3] [PE1.1] • step
129 3 3 2 [PE1.1] • i [1 3 1 2 3] [PE1.1] step
130 3 3 2 • PE1.1 [1 3 1 2 3] [PE1.1] step
131 3 3 2 • + [+] dupdip [1 3 1 2 3] [PE1.1] step
132 3 5 • [+] dupdip [1 3 1 2 3] [PE1.1] step
133 3 5 [+] • dupdip [1 3 1 2 3] [PE1.1] step
134 3 5 • + 5 [1 3 1 2 3] [PE1.1] step
135 8 • 5 [1 3 1 2 3] [PE1.1] step
136 8 5 • [1 3 1 2 3] [PE1.1] step
137 8 5 [1 3 1 2 3] • [PE1.1] step
138 8 5 [1 3 1 2 3] [PE1.1] • step
139 8 5 1 [PE1.1] • i [3 1 2 3] [PE1.1] step
140 8 5 1 • PE1.1 [3 1 2 3] [PE1.1] step
141 8 5 1 • + [+] dupdip [3 1 2 3] [PE1.1] step
142 8 6 • [+] dupdip [3 1 2 3] [PE1.1] step
143 8 6 [+] • dupdip [3 1 2 3] [PE1.1] step
144 8 6 • + 6 [3 1 2 3] [PE1.1] step
145 14 • 6 [3 1 2 3] [PE1.1] step
146 14 6 • [3 1 2 3] [PE1.1] step
147 14 6 [3 1 2 3] • [PE1.1] step
148 14 6 [3 1 2 3] [PE1.1] • step
149 14 6 3 [PE1.1] • i [1 2 3] [PE1.1] step
150 14 6 3 • PE1.1 [1 2 3] [PE1.1] step
151 14 6 3 • + [+] dupdip [1 2 3] [PE1.1] step
152 14 9 • [+] dupdip [1 2 3] [PE1.1] step
153 14 9 [+] • dupdip [1 2 3] [PE1.1] step
154 14 9 • + 9 [1 2 3] [PE1.1] step
155 23 • 9 [1 2 3] [PE1.1] step
156 23 9 • [1 2 3] [PE1.1] step
157 23 9 [1 2 3] • [PE1.1] step
158 23 9 [1 2 3] [PE1.1] • step
159 23 9 1 [PE1.1] • i [2 3] [PE1.1] step
160 23 9 1 • PE1.1 [2 3] [PE1.1] step
161 23 9 1 • + [+] dupdip [2 3] [PE1.1] step
162 23 10 • [+] dupdip [2 3] [PE1.1] step
163 23 10 [+] • dupdip [2 3] [PE1.1] step
164 23 10 • + 10 [2 3] [PE1.1] step
165 33 • 10 [2 3] [PE1.1] step
166 33 10 • [2 3] [PE1.1] step
167 33 10 [2 3] • [PE1.1] step
168 33 10 [2 3] [PE1.1] • step
169 33 10 2 [PE1.1] • i [3] [PE1.1] step
170 33 10 2 • PE1.1 [3] [PE1.1] step
171 33 10 2 • + [+] dupdip [3] [PE1.1] step
172 33 12 • [+] dupdip [3] [PE1.1] step
173 33 12 [+] • dupdip [3] [PE1.1] step
174 33 12 • + 12 [3] [PE1.1] step
175 45 • 12 [3] [PE1.1] step
176 45 12 • [3] [PE1.1] step
177 45 12 [3] • [PE1.1] step
178 45 12 [3] [PE1.1] • step
181 45 12 3 • + [+] dupdip
189 So one `step` through all seven terms brings the counter to 15 and the total to 60.
227 We only want the terms *less than* 1000.
241 That means we want to run the full list of numbers sixty-six times to get to 990 and then the first four numbers 3 2 1 3 to get to 999.
245 define('PE1 0 0 66 [[3 2 1 3 1 2 3] [PE1.1] step] times [3 2 1 3] [PE1.1] step pop')
256 This form uses no extra storage and produces no unused summands. It's
257 good but there's one more trick we can apply. The list of seven terms
258 takes up at least seven bytes. But notice that all of the terms are less
259 than four, and so each can fit in just two bits. We could store all
260 seven terms in just fourteen bits and use masking and shifts to pick out
261 each term as we go. This will use less space and save time loading whole
262 integer terms from the list.
265 0b 11 10 01 11 01 10 11 == 14811
281 define('PE1.2 [3 & PE1.1] dupdip 2 >>')
293 0 0 14811 • [3 & PE1.1] dupdip 2 >>
294 0 0 14811 [3 & PE1.1] • dupdip 2 >>
295 0 0 14811 • 3 & PE1.1 14811 2 >>
296 0 0 14811 3 • & PE1.1 14811 2 >>
297 0 0 3 • PE1.1 14811 2 >>
298 0 0 3 • + [+] dupdip 14811 2 >>
299 0 3 • [+] dupdip 14811 2 >>
300 0 3 [+] • dupdip 14811 2 >>
318 3 3 3702 • [3 & PE1.1] dupdip 2 >>
319 3 3 3702 [3 & PE1.1] • dupdip 2 >>
320 3 3 3702 • 3 & PE1.1 3702 2 >>
321 3 3 3702 3 • & PE1.1 3702 2 >>
322 3 3 2 • PE1.1 3702 2 >>
323 3 3 2 • + [+] dupdip 3702 2 >>
324 3 5 • [+] dupdip 3702 2 >>
325 3 5 [+] • dupdip 3702 2 >>
336 V('0 0 14811 7 [PE1.2] times pop')
339 • 0 0 14811 7 [PE1.2] times pop
340 0 • 0 14811 7 [PE1.2] times pop
341 0 0 • 14811 7 [PE1.2] times pop
342 0 0 14811 • 7 [PE1.2] times pop
343 0 0 14811 7 • [PE1.2] times pop
344 0 0 14811 7 [PE1.2] • times pop
345 0 0 14811 • PE1.2 6 [PE1.2] times pop
346 0 0 14811 • [3 & PE1.1] dupdip 2 >> 6 [PE1.2] times pop
347 0 0 14811 [3 & PE1.1] • dupdip 2 >> 6 [PE1.2] times pop
348 0 0 14811 • 3 & PE1.1 14811 2 >> 6 [PE1.2] times pop
349 0 0 14811 3 • & PE1.1 14811 2 >> 6 [PE1.2] times pop
350 0 0 3 • PE1.1 14811 2 >> 6 [PE1.2] times pop
351 0 0 3 • + [+] dupdip 14811 2 >> 6 [PE1.2] times pop
352 0 3 • [+] dupdip 14811 2 >> 6 [PE1.2] times pop
353 0 3 [+] • dupdip 14811 2 >> 6 [PE1.2] times pop
354 0 3 • + 3 14811 2 >> 6 [PE1.2] times pop
355 3 • 3 14811 2 >> 6 [PE1.2] times pop
356 3 3 • 14811 2 >> 6 [PE1.2] times pop
357 3 3 14811 • 2 >> 6 [PE1.2] times pop
358 3 3 14811 2 • >> 6 [PE1.2] times pop
359 3 3 3702 • 6 [PE1.2] times pop
360 3 3 3702 6 • [PE1.2] times pop
361 3 3 3702 6 [PE1.2] • times pop
362 3 3 3702 • PE1.2 5 [PE1.2] times pop
363 3 3 3702 • [3 & PE1.1] dupdip 2 >> 5 [PE1.2] times pop
364 3 3 3702 [3 & PE1.1] • dupdip 2 >> 5 [PE1.2] times pop
365 3 3 3702 • 3 & PE1.1 3702 2 >> 5 [PE1.2] times pop
366 3 3 3702 3 • & PE1.1 3702 2 >> 5 [PE1.2] times pop
367 3 3 2 • PE1.1 3702 2 >> 5 [PE1.2] times pop
368 3 3 2 • + [+] dupdip 3702 2 >> 5 [PE1.2] times pop
369 3 5 • [+] dupdip 3702 2 >> 5 [PE1.2] times pop
370 3 5 [+] • dupdip 3702 2 >> 5 [PE1.2] times pop
371 3 5 • + 5 3702 2 >> 5 [PE1.2] times pop
372 8 • 5 3702 2 >> 5 [PE1.2] times pop
373 8 5 • 3702 2 >> 5 [PE1.2] times pop
374 8 5 3702 • 2 >> 5 [PE1.2] times pop
375 8 5 3702 2 • >> 5 [PE1.2] times pop
376 8 5 925 • 5 [PE1.2] times pop
377 8 5 925 5 • [PE1.2] times pop
378 8 5 925 5 [PE1.2] • times pop
379 8 5 925 • PE1.2 4 [PE1.2] times pop
380 8 5 925 • [3 & PE1.1] dupdip 2 >> 4 [PE1.2] times pop
381 8 5 925 [3 & PE1.1] • dupdip 2 >> 4 [PE1.2] times pop
382 8 5 925 • 3 & PE1.1 925 2 >> 4 [PE1.2] times pop
383 8 5 925 3 • & PE1.1 925 2 >> 4 [PE1.2] times pop
384 8 5 1 • PE1.1 925 2 >> 4 [PE1.2] times pop
385 8 5 1 • + [+] dupdip 925 2 >> 4 [PE1.2] times pop
386 8 6 • [+] dupdip 925 2 >> 4 [PE1.2] times pop
387 8 6 [+] • dupdip 925 2 >> 4 [PE1.2] times pop
388 8 6 • + 6 925 2 >> 4 [PE1.2] times pop
389 14 • 6 925 2 >> 4 [PE1.2] times pop
390 14 6 • 925 2 >> 4 [PE1.2] times pop
391 14 6 925 • 2 >> 4 [PE1.2] times pop
392 14 6 925 2 • >> 4 [PE1.2] times pop
393 14 6 231 • 4 [PE1.2] times pop
394 14 6 231 4 • [PE1.2] times pop
395 14 6 231 4 [PE1.2] • times pop
396 14 6 231 • PE1.2 3 [PE1.2] times pop
397 14 6 231 • [3 & PE1.1] dupdip 2 >> 3 [PE1.2] times pop
398 14 6 231 [3 & PE1.1] • dupdip 2 >> 3 [PE1.2] times pop
399 14 6 231 • 3 & PE1.1 231 2 >> 3 [PE1.2] times pop
400 14 6 231 3 • & PE1.1 231 2 >> 3 [PE1.2] times pop
401 14 6 3 • PE1.1 231 2 >> 3 [PE1.2] times pop
402 14 6 3 • + [+] dupdip 231 2 >> 3 [PE1.2] times pop
403 14 9 • [+] dupdip 231 2 >> 3 [PE1.2] times pop
404 14 9 [+] • dupdip 231 2 >> 3 [PE1.2] times pop
405 14 9 • + 9 231 2 >> 3 [PE1.2] times pop
406 23 • 9 231 2 >> 3 [PE1.2] times pop
407 23 9 • 231 2 >> 3 [PE1.2] times pop
408 23 9 231 • 2 >> 3 [PE1.2] times pop
409 23 9 231 2 • >> 3 [PE1.2] times pop
410 23 9 57 • 3 [PE1.2] times pop
411 23 9 57 3 • [PE1.2] times pop
412 23 9 57 3 [PE1.2] • times pop
413 23 9 57 • PE1.2 2 [PE1.2] times pop
414 23 9 57 • [3 & PE1.1] dupdip 2 >> 2 [PE1.2] times pop
415 23 9 57 [3 & PE1.1] • dupdip 2 >> 2 [PE1.2] times pop
416 23 9 57 • 3 & PE1.1 57 2 >> 2 [PE1.2] times pop
417 23 9 57 3 • & PE1.1 57 2 >> 2 [PE1.2] times pop
418 23 9 1 • PE1.1 57 2 >> 2 [PE1.2] times pop
419 23 9 1 • + [+] dupdip 57 2 >> 2 [PE1.2] times pop
420 23 10 • [+] dupdip 57 2 >> 2 [PE1.2] times pop
421 23 10 [+] • dupdip 57 2 >> 2 [PE1.2] times pop
422 23 10 • + 10 57 2 >> 2 [PE1.2] times pop
423 33 • 10 57 2 >> 2 [PE1.2] times pop
424 33 10 • 57 2 >> 2 [PE1.2] times pop
425 33 10 57 • 2 >> 2 [PE1.2] times pop
426 33 10 57 2 • >> 2 [PE1.2] times pop
427 33 10 14 • 2 [PE1.2] times pop
428 33 10 14 2 • [PE1.2] times pop
429 33 10 14 2 [PE1.2] • times pop
430 33 10 14 • PE1.2 1 [PE1.2] times pop
431 33 10 14 • [3 & PE1.1] dupdip 2 >> 1 [PE1.2] times pop
432 33 10 14 [3 & PE1.1] • dupdip 2 >> 1 [PE1.2] times pop
433 33 10 14 • 3 & PE1.1 14 2 >> 1 [PE1.2] times pop
434 33 10 14 3 • & PE1.1 14 2 >> 1 [PE1.2] times pop
435 33 10 2 • PE1.1 14 2 >> 1 [PE1.2] times pop
436 33 10 2 • + [+] dupdip 14 2 >> 1 [PE1.2] times pop
437 33 12 • [+] dupdip 14 2 >> 1 [PE1.2] times pop
438 33 12 [+] • dupdip 14 2 >> 1 [PE1.2] times pop
439 33 12 • + 12 14 2 >> 1 [PE1.2] times pop
440 45 • 12 14 2 >> 1 [PE1.2] times pop
441 45 12 • 14 2 >> 1 [PE1.2] times pop
442 45 12 14 • 2 >> 1 [PE1.2] times pop
443 45 12 14 2 • >> 1 [PE1.2] times pop
444 45 12 3 • 1 [PE1.2] times pop
445 45 12 3 1 • [PE1.2] times pop
446 45 12 3 1 [PE1.2] • times pop
448 45 12 3 • [3 & PE1.1] dupdip 2 >> pop
449 45 12 3 [3 & PE1.1] • dupdip 2 >> pop
450 45 12 3 • 3 & PE1.1 3 2 >> pop
451 45 12 3 3 • & PE1.1 3 2 >> pop
452 45 12 3 • PE1.1 3 2 >> pop
453 45 12 3 • + [+] dupdip 3 2 >> pop
454 45 15 • [+] dupdip 3 2 >> pop
455 45 15 [+] • dupdip 3 2 >> pop
456 45 15 • + 15 3 2 >> pop
465 And so we have at last:
469 define('PE1 0 0 66 [14811 7 [PE1.2] times pop] times 14811 4 [PE1.2] times popop')
482 14811 7 [PE1.2] times pop
483 14811 4 [PE1.2] times pop
484 14811 n [PE1.2] times pop
485 n 14811 swap [PE1.2] times pop
489 define('PE1.3 14811 swap [PE1.2] times pop')
492 Now we can simplify the definition above:
496 define('PE1 0 0 66 [7 PE1.3] times 4 PE1.3 pop')
507 Here's our joy program all in one place. It doesn't make so much sense, but if you have read through the above description of how it was derived I hope it's clear.
509 PE1.1 == + [+] dupdip
510 PE1.2 == [3 & PE1.1] dupdip 2 >>
511 PE1.3 == 14811 swap [PE1.2] times pop
512 PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop
515 It's a little clunky iterating sixty-six times though the seven numbers then four more. In the _Generator Programs_ notebook we derive a generator that can be repeatedly driven by the `x` combinator to produce a stream of the seven numbers repeating over and over again.
519 define('PE1.terms [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]')
524 J('PE1.terms 21 [x] times')
527 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]
530 We know from above that we need sixty-six times seven then four more terms to reach up to but not over one thousand.
544 J('PE1.terms 466 [x] times pop')
547 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3
550 ### ...and they do sum to 999.
554 J('[PE1.terms 466 [x] times pop] run sum')
560 Now we can use `PE1.1` to accumulate the terms as we go, and then `pop` the generator and the counter from the stack when we're done, leaving just the sum.
564 J('0 0 PE1.terms 466 [x [PE1.1] dip] times popop')
570 # A little further analysis renders iteration unnecessary.
571 Consider finding the sum of the positive integers less than or equal to ten.
575 J('[10 9 8 7 6 5 4 3 2 1] sum')
581 Instead of summing them, [observe](https://en.wikipedia.org/wiki/File:Animated_proof_for_the_formula_giving_the_sum_of_the_first_integers_1%2B2%2B...%2Bn.gif):
590 From the above example we can deduce that the sum of the first N positive integers is:
594 (The formula also works for odd values of N, I'll leave that to you if you want to work it out or you can take my word for it.)
598 define('F dup ++ * 2 floordiv')
608 10 • dup ++ * 2 floordiv
609 10 10 • ++ * 2 floordiv
616 ## Generalizing to Blocks of Terms
617 We can apply the same reasoning to the PE1 problem.
619 Between 0 and 990 inclusive there are sixty-six "blocks" of seven terms each, starting with:
625 [978 980 981 984 985 987 990]
627 If we reverse one of these two blocks and sum pairs...
631 J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip')
634 [[978 15] [980 12] [981 10] [984 9] [985 6] [987 5] [990 3]]
639 J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map')
642 [993 992 991 993 991 992 993]
645 (Interesting that the sequence of seven numbers appears again in the rightmost digit of each term.)
649 J('[ 3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map sum')
655 Since there are sixty-six blocks and we are pairing them up, there must be thirty-three pairs, each of which sums to 6945. We also have these additional unpaired terms between 990 and 1000:
659 So we can give the "sum of all the multiples of 3 or 5 below 1000" like so:
663 J('6945 33 * [993 995 996 999] cons sum')
669 It's worth noting, I think, that this same reasoning holds for any two numbers $n$ and $m$ the multiples of which we hope to sum. The multiples would have a cycle of differences of length $k$ and so we could compute the sum of $Nk$ multiples as above.
671 The sequence of differences will always be a palidrome. Consider an interval spanning the least common multiple of $n$ and $m$:
676 Here we have 4 and 7, and you can read off the sequence of differences directly from the diagram: 4 3 1 4 2 2 4 1 3 4.
678 Geometrically, the actual values of $n$ and $m$ and their *lcm* don't matter, the pattern they make will always be symmetrical around its midpoint. The same reasoning holds for multiples of more than two numbers.
680 # The Simplest Program
682 Of course, the simplest joy program for the first Project Euler problem is just: