1 ! Copyright (c) 2008 Aaron Schaefer.
2 ! See https://factorcode.org/license.txt for BSD license.
3 USING: combinators.short-circuit kernel math math.functions
4 math.combinatorics ranges project-euler.common sequences sets
8 ! https://projecteuler.net/problem=43
13 ! The number, 1406357289, is a 0 to 9 pandigital number because
14 ! it is made up of each of the digits 0 to 9 in some order, but
15 ! it also has a rather interesting sub-string divisibility
18 ! Let d1 be the 1st digit, d2 be the 2nd digit, and so on. In
19 ! this way, we note the following:
21 ! * d2d3d4 = 406 is divisible by 2
22 ! * d3d4d5 = 063 is divisible by 3
23 ! * d4d5d6 = 635 is divisible by 5
24 ! * d5d6d7 = 357 is divisible by 7
25 ! * d6d7d8 = 572 is divisible by 11
26 ! * d7d8d9 = 728 is divisible by 13
27 ! * d8d9d10 = 289 is divisible by 17
29 ! Find the sum of all 0 to 9 pandigital numbers with this
36 ! Brute force generating all the pandigitals then checking
37 ! 3-digit divisiblity properties...this is very slow!
41 : subseq-divisible? ( n index seq -- ? )
42 [ 1 - dup 3 + ] dip subseq digits>number swap divisor? ;
44 : interesting? ( seq -- ? )
46 [ [ 17 8 ] dip subseq-divisible? ]
47 [ [ 13 7 ] dip subseq-divisible? ]
48 [ [ 11 6 ] dip subseq-divisible? ]
49 [ [ 7 5 ] dip subseq-divisible? ]
50 [ [ 5 4 ] dip subseq-divisible? ]
51 [ [ 3 3 ] dip subseq-divisible? ]
52 [ [ 2 2 ] dip subseq-divisible? ]
57 : euler043 ( -- answer )
58 1234567890 number>digits 0 [
62 ] reduce-permutations ;
65 ! 60280 ms run / 59 ms GC time
71 ! Build the number from right to left, generating the next 3-digits according
72 ! to the divisiblity rules and combining them with the previous digits if they
73 ! overlap and still have all unique digits. When done with that, add whatever
74 ! missing digit is needed to make the number pandigital.
78 : candidates ( n -- seq )
79 1000 over <range> [ number>digits 3 0 pad-head ] map [ all-unique? ] filter ;
81 : overlap? ( seq -- ? )
82 [ first 2 tail* ] [ second 2 head ] bi = ;
84 : clean ( seq -- seq )
85 [ unclip 1 head prefix concat ] map [ all-unique? ] filter ;
87 : add-missing-digit ( seq -- seq )
88 dup sort 10 <iota> swap diff prepend ;
90 : interesting-pandigitals ( -- seq )
91 17 candidates { 13 11 7 5 3 2 } [
92 candidates swap cartesian-product concat
93 [ overlap? ] filter clean
94 ] each [ add-missing-digit ] map ;
98 : euler043a ( -- answer )
99 interesting-pandigitals [ digits>number ] map-sum ;
101 ! [ euler043a ] 100 ave-time
102 ! 10 ms ave run time - 1.37 SD (100 trials)