]> gitweb.factorcode.org Git - factor.git/blob - basis/math/combinatorics/combinatorics.factor
factor: trim using lists
[factor.git] / basis / math / combinatorics / combinatorics.factor
1 ! Copyright (c) 2007-2010 Slava Pestov, Doug Coleman, Aaron Schaefer, John Benediktsson.
2 ! See http://factorcode.org/license.txt for BSD license.
3
4 USING: accessors arrays assocs classes.tuple combinators hints
5 kernel kernel.private math math.functions math.order math.ranges
6 sequences sequences.private sorting strings vectors ;
7 IN: math.combinatorics
8
9 <PRIVATE
10
11 ! Specialized version of nths-unsafe for performance
12 : (nths-unsafe) ( indices seq -- seq' )
13     [ { array } declare ] dip
14     [ [ nth-unsafe ] curry ] keep map-as ; inline
15 GENERIC: nths-unsafe ( indices seq -- seq' )
16 M: string nths-unsafe (nths-unsafe) ;
17 M: array nths-unsafe (nths-unsafe) ;
18 M: vector nths-unsafe (nths-unsafe) ;
19 M: iota nths-unsafe (nths-unsafe) ;
20 M: object nths-unsafe (nths-unsafe) ;
21
22 : possible? ( n m -- ? )
23     0 rot between? ; inline
24
25 : twiddle ( n k -- n k )
26     2dup - dupd > [ dupd - ] when ; inline
27
28 PRIVATE>
29
30 : factorial ( n -- n! )
31     dup 1 > [ [1,b] product ] [ drop 1 ] if ;
32
33 : nPk ( n k -- nPk )
34     2dup possible? [ dupd - [a,b) product ] [ 2drop 0 ] if ;
35
36 : nCk ( n k -- nCk )
37     twiddle [ nPk ] keep factorial /i ;
38
39
40 ! Factoradic-based permutation methodology
41
42 <PRIVATE
43
44 : factoradic ( n -- factoradic )
45     0 [ over 0 > ] [ 1 + [ /mod ] keep swap ] produce reverse! 2nip ;
46
47 : bump-indices ( seq n -- )
48     '[ dup _ >= [ 1 + ] when ] map! drop ; inline
49
50 : (>permutation) ( seq n index -- seq )
51     swap [ dupd head-slice ] dip bump-indices ;
52
53 : >permutation ( factoradic -- permutation )
54     reverse! dup [ (>permutation) ] each-index reverse! ;
55
56 : permutation-indices ( n seq -- permutation )
57     length [ factoradic ] dip 0 pad-head >permutation ;
58
59 : permutation-iota ( seq -- <iota> )
60     length factorial <iota> ; inline
61
62 PRIVATE>
63
64 : permutation ( n seq -- seq' )
65     [ permutation-indices ] keep nths-unsafe ;
66
67 TUPLE: permutations length seq ;
68
69 : <permutations> ( seq -- permutations )
70     [ length factorial ] keep permutations boa ;
71
72 M: permutations length length>> ; inline
73 M: permutations nth-unsafe seq>> permutation ;
74 M: permutations hashcode* tuple-hashcode ;
75
76 INSTANCE: permutations immutable-sequence
77
78 TUPLE: k-permutations length skip k seq ;
79
80 :: <k-permutations> ( seq k -- permutations )
81     seq length :> n
82     n k nPk :> len
83     {
84         { [ len k [ zero? ] either? ] [ { } ] }
85         { [ n k = ] [ seq <permutations> ] }
86         [ len n factorial over /i k seq k-permutations boa ]
87     } cond ;
88
89 M: k-permutations length length>> ; inline
90 M: k-permutations nth-unsafe
91     [ skip>> * ]
92     [ seq>> [ permutation-indices ] keep ]
93     [ k>> swap [ head ] dip nths-unsafe ] tri ;
94 M: k-permutations hashcode* tuple-hashcode ;
95
96 INSTANCE: k-permutations immutable-sequence
97
98 DEFER: next-permutation
99
100 <PRIVATE
101
102 : permutations-quot ( seq quot -- seq quot' )
103     [ [ permutation-iota ] [ length <iota> >array ] [ ] tri ] dip
104     '[ drop _ [ _ nths-unsafe @ ] keep next-permutation drop ] ; inline
105
106 PRIVATE>
107
108 : each-permutation ( ... seq quot: ( ... elt -- ... ) -- ... )
109     permutations-quot each ; inline
110
111 : map-permutations ( ... seq quot: ( ... elt -- ... newelt ) -- ... newseq )
112     permutations-quot map ; inline
113
114 : filter-permutations ( ... seq quot: ( ... elt -- ... ? ) -- ... newseq )
115     selector [ each-permutation ] dip ; inline
116
117 : all-permutations ( seq -- seq' )
118     [ ] map-permutations ;
119
120 : all-permutations? ( ... seq quot: ( ... elt -- ... ? ) -- ... ? )
121     permutations-quot all? ; inline
122
123 : find-permutation ( ... seq quot: ( ... elt -- ... ? ) -- ... elt/f )
124     [ permutations-quot find drop ]
125     [ drop over [ permutation ] [ 2drop f ] if ] 2bi ; inline
126
127 : reduce-permutations ( ... seq identity quot: ( ... prev elt -- ... next ) -- ... result )
128     swapd each-permutation ; inline
129
130 : inverse-permutation ( seq -- permutation )
131     <enumerated> sort-values keys ;
132
133 <PRIVATE
134
135 : cut-point ( seq -- n )
136     [ last ] keep [ [ > ] keep swap ] find-last drop nip ; inline
137
138 : greater-from-last ( n seq -- i )
139     [ nip ] [ nth ] 2bi [ > ] curry find-last drop ; inline
140
141 : reverse-tail! ( n seq -- seq )
142     [ swap 1 + tail-slice reverse! drop ] keep ; inline
143
144 : (next-permutation) ( seq -- seq )
145     dup cut-point [
146         swap [ greater-from-last ] 2keep
147         [ exchange ] [ reverse-tail! nip ] 3bi
148     ] [ reverse! ] if* ;
149
150 HINTS: (next-permutation) array ;
151
152 PRIVATE>
153
154 : next-permutation ( seq -- seq )
155     dup empty? [ (next-permutation) ] unless ;
156
157
158 ! Combinadic-based combination methodology
159
160 <PRIVATE
161
162 ! "Algorithm 515: Generation of a Vector from the Lexicographical Index"
163 ! Buckles, B. P., and Lybanon, M. ACM
164 ! Transactions on Mathematical Software, Vol. 3, No. 2, June 1977.
165
166 :: combination-indices ( x! p n -- seq )
167     x 1 + x!
168     p 0 <array> :> c 0 :> k! 0 :> r!
169     p 1 - [| i |
170         i [ 0 ] [ 1 - c nth ] if-zero i c set-nth
171         [ k x < ] [
172             i c [ 1 + ] change-nth
173             n i c nth - p i 1 + - nCk r!
174             k r + k!
175         ] do while k r - k!
176     ] each-integer
177     p 2 < [ 0 ] [ p 2 - c nth ] if
178     p 1 < [ drop ] [ x + k - p 1 - c set-nth ] if
179     c [ 1 - ] map! ;
180
181 PRIVATE>
182
183 : combination ( m seq k -- seq' )
184     swap [ length combination-indices ] [ nths-unsafe ] bi ;
185
186 TUPLE: combinations seq k length ;
187
188 : <combinations> ( seq k -- combinations )
189     2dup [ length ] [ nCk ] bi* combinations boa ;
190
191 M: combinations length length>> ; inline
192 M: combinations nth-unsafe [ seq>> ] [ k>> ] bi combination ;
193 M: combinations hashcode* tuple-hashcode ;
194
195 INSTANCE: combinations immutable-sequence
196
197 <PRIVATE
198
199 : find-max-index ( seq n -- i )
200     over length - '[ _ + >= ] find-index drop ; inline
201
202 : increment-rest ( i seq -- )
203     [ nth ] [ swap tail-slice ] 2bi
204     [ drop 1 + dup ] map! 2drop ; inline
205
206 : increment-last ( seq -- )
207     [ [ length 1 - ] keep [ 1 + ] change-nth ] unless-empty ; inline
208
209 :: next-combination ( seq n -- seq )
210     seq n find-max-index [
211         1 [-] seq increment-rest
212     ] [
213         seq increment-last
214     ] if* seq ; inline
215
216 :: combinations-quot ( seq k quot -- seq quot' )
217     seq length :> n
218     n k nCk <iota> k <iota> >array seq quot n
219     '[ drop _ [ _ nths-unsafe @ ] keep _ next-combination drop ] ; inline
220
221 PRIVATE>
222
223 : each-combination ( ... seq k quot: ( ... elt -- ... ) -- ... )
224     combinations-quot each ; inline
225
226 : map-combinations ( ... seq k quot: ( ... elt -- ... newelt ) -- ... newseq )
227     combinations-quot map ; inline
228
229 : filter-combinations ( ... seq k quot: ( ... elt -- ... ? ) -- ... newseq )
230     selector [ each-combination ] dip ; inline
231
232 : map>assoc-combinations ( ... seq k quot: ( ... elt -- ... key value ) exemplar -- ... assoc )
233     [ combinations-quot ] dip map>assoc ; inline
234
235 : all-combinations ( seq k -- seq' )
236     [ ] map-combinations ;
237
238 : all-combinations? ( ... seq k quot: ( ... elt -- ... ? ) -- ... ? )
239     combinations-quot all? ; inline
240
241 : find-combination ( ... seq k quot: ( ... elt -- ... ? ) -- ... elt/f )
242     [ f ] 3dip '[ nip _ keep swap ] combinations-quot find drop swap and ; inline
243
244 : reduce-combinations ( ... seq k identity quot: ( ... prev elt -- ... next ) -- ... result )
245     -rotd each-combination ; inline
246
247 : all-subsets ( seq -- subsets )
248     dup length [0,b] [ all-combinations ] with map concat ;
249
250 <PRIVATE
251
252 :: next-selection ( seq n -- )
253     1 seq length 1 - [
254         dup 0 >= [ over 0 = ] [ t ] if
255     ] [
256         [ seq [ + n /mod ] change-nth-unsafe ] keep 1 -
257     ] do until 2drop ; inline
258
259 :: selections-quot ( seq n quot -- seq quot' )
260     seq length :> len
261     n 0 <array> :> idx
262     n [ 0 ] [ len swap ^ ] if-zero <iota> [
263         drop
264         idx seq nths-unsafe quot call
265         idx len next-selection
266     ] ; inline
267
268 PRIVATE>
269
270 : each-selection ( ... seq n quot: ( ... elt -- ... ) -- ... )
271     selections-quot each ; inline
272
273 : map-selections ( ... seq n quot: ( ... elt -- ... newelt ) -- ... newseq )
274     selections-quot map ; inline
275
276 : filter-selections ( ... seq n quot: ( ... elt -- ... newelt ) -- ... newseq )
277     selector [ each-selection ] dip ; inline
278
279 : all-selections ( seq n -- seq' )
280     [ ] map-selections ;
281
282 : all-selections? ( seq n -- ? )
283     selections-quot all? ; inline
284
285 : find-selection ( ... seq n quot: ( ... elt -- ... ? ) -- ... elt/f )
286     [ f ] 3dip '[ nip _ keep swap ] selections-quot find drop swap and ; inline
287
288 : reduce-selections ( ... seq n identity quot: ( ... prev elt -- ... next ) -- ... result )
289     -rotd each-selection ; inline