]> gitweb.factorcode.org Git - factor.git/blob - extra/project-euler/064/064.factor
factor: trim using lists
[factor.git] / extra / project-euler / 064 / 064.factor
1 USING: accessors classes.tuple kernel math math.functions
2 project-euler.common ranges sequences ;
3 IN: project-euler.064
4
5 ! http://projecteuler.net/index.php?section=problems&id=64
6
7 ! DESCRIPTION
8 ! -----------
9
10 ! All square roots are periodic when written as continued
11 ! fractions and can be written in the form:
12
13 ! √N=a0+1/(a1+1/(a2+1/a3+...))
14
15 ! For example, let us consider √23:
16
17 ! √23=4+√(23)−4=4+1/(1/(√23−4)=4+1/(1+((√23−3)/7)
18
19 ! If we continue we would get the following expansion:
20
21 ! √23=4+1/(1+1/(3+1/(1+1/(8+...))))
22
23 ! The process can be summarised as follows:
24
25 ! a0=4, 1/(√23−4) = (√23+4)/7 = 1+(√23−3)/7
26 ! a1=1, 7/(√23−3) = 7*(√23+3)/14 = 3+(√23−3)/2
27 ! a2=3, 2/(√23−3) = 2*(√23+3)/14 = 1+(√23−4)/7
28 ! a3=1, 7/(√23−4) = 7*(√23+4)/7 = 8+√23−4
29 ! a4=8, 1/(√23−4) = (√23+4)/7 = 1+(√23−3)/7
30 ! a5=1, 7/(√23−3) = 7*(√23+3)/14 = 3+(√23−3)/2
31 ! a6=3, 2/(√23−3) = 2*(√23+3)/14 = 1+(√23−4)/7
32 ! a7=1, 7/(√23−4) = 7*(√23+4)/7 = 8+√23−4
33
34 ! It can be seen that the sequence is repeating. For
35 ! conciseness, we use the notation √23=[4;(1,3,1,8)], to
36 ! indicate that the block (1,3,1,8) repeats indefinitely.
37
38 ! The first ten continued fraction representations of
39 ! (irrational) square roots are:
40
41 ! √2=[1;(2)] , period=1
42 ! √3=[1;(1,2)], period=2
43 ! √5=[2;(4)], period=1
44 ! √6=[2;(2,4)], period=2
45 ! √7=[2;(1,1,1,4)], period=4
46 ! √8=[2;(1,4)], period=2
47 ! √10=[3;(6)], period=1
48 ! √11=[3;(3,6)], period=2
49 ! √12=[3;(2,6)], period=2
50 ! √13=[3;(1,1,1,1,6)], period=5
51
52 ! Exactly four continued fractions, for N <= 13, have an odd period.
53
54 ! How many continued fractions for N <= 10000 have an odd period?
55
56 <PRIVATE
57
58 TUPLE: cont-frac
59     { whole integer }
60     { num-const integer }
61     { denom integer } ;
62
63 C: <cont-frac> cont-frac
64
65 : deep-copy ( cont-frac -- cont-frac cont-frac )
66     dup tuple>array rest cont-frac slots>tuple ;
67
68 : create-cont-frac ( n -- n cont-frac )
69     dup sqrt >fixnum dup 1 <cont-frac> ;
70
71 : step ( n cont-frac -- n cont-frac )
72     swap dup
73     ! Store n
74     [let :> n
75         ! Extract the constant
76         swap dup num-const>>
77         :> num-const
78
79         ! Find the new denominator
80         num-const 2 ^ n swap -
81         :> exp-denom
82
83         ! Find the fraction in lowest terms
84         dup denom>>
85         exp-denom simple-gcd
86         exp-denom swap /
87         :> new-denom
88
89         ! Find the new whole number
90         num-const n sqrt + new-denom / >fixnum
91         :> new-whole
92
93         ! Find the new num-const
94         num-const new-denom /
95         new-whole swap -
96         new-denom *
97         :> new-num-const
98
99         ! Finally, update the continuing fraction
100         drop new-whole new-num-const new-denom <cont-frac>
101     ] ;
102
103 :: loop ( c l n cf -- c l n cf )
104     n cf step :> new-cf drop
105     c 1 + l n new-cf
106     l new-cf = [ loop ] unless ;
107
108 : find-period ( n -- period )
109     0 swap
110     create-cont-frac
111     step
112     deep-copy -rot
113     loop
114     drop drop drop ;
115
116 : try-all ( -- n )
117     2 10000 [a..b]
118     [ perfect-square? ] reject
119     [ find-period ] map
120     [ odd? ] filter
121     length ;
122
123 PRIVATE>
124
125 : euler064a ( -- n ) try-all ;
126
127 <PRIVATE
128
129 ! (√n + a)/b
130 TUPLE: cfrac n a b ;
131
132 C: <cfrac> cfrac
133
134 : >cfrac< ( fr -- n a b )
135     [ n>> ] [ a>> ] [ b>> ] tri ;
136
137 ! (√n + a) / b = 1 / (k + (√n + a') / b')
138 !
139 ! b / (√n + a) = b (√n - a) / (n - a^2) = (√n - a) / ((n - a^2) / b)
140 :: reciprocal ( fr -- fr' )
141     fr >cfrac< :> ( n a b )
142     n
143     a neg
144     n a sq - b /
145     <cfrac> ;
146
147 :: split ( fr -- k fr' )
148     fr >cfrac< :> ( n a b )
149     n sqrt a + b /i
150     dup n swap
151     b * a swap -
152     b
153     <cfrac> ;
154
155 : pure ( n -- fr )
156     0 1 <cfrac> ;
157
158 : next ( fr -- fr' )
159     reciprocal split nip ;
160
161 :: period ( n -- period )
162     n sqrt >integer sq n = [ 0 ] [
163         n pure split nip :> start
164         1 start next
165         [ dup start = not ]
166         [ next [ 1 + ] dip ]
167         while drop
168     ] if ;
169
170 PRIVATE>
171
172 : euler064b ( -- ct )
173     10000 [1..b] [ period odd? ] count ;
174
175 SOLUTION: euler064b