]> gitweb.factorcode.org Git - factor.git/blob - basis/math/combinatorics/combinatorics.factor
factor: Move math.ranges => ranges.
[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 make math math.functions math.order
6 ranges 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 <PRIVATE
158
159 : should-swap? ( start curr seq -- ? )
160     [ nipd nth ] [ <slice> member? not ] 3bi ; inline
161
162 :: unique-permutations ( ... seq i n quot: ( ... elt -- ... ) -- ... )
163     i n >= [
164         seq clone quot call
165     ] [
166         i n [a..b) [| j |
167             i j seq should-swap? [
168                 i j seq exchange-unsafe
169                 seq i 1 + n quot unique-permutations
170                 i j seq exchange-unsafe
171             ] when
172         ] each
173     ] if ; inline recursive
174
175 PRIVATE>
176
177 : each-unique-permutation ( ... seq quot: ( ... elt -- ... ) -- ... )
178     [ 0 over length ] dip unique-permutations ; inline
179
180 : all-unique-permutations ( seq -- seq' )
181     [ [ , ] each-unique-permutation ] { } make ;
182
183 ! Combinadic-based combination methodology
184
185 <PRIVATE
186
187 ! "Algorithm 515: Generation of a Vector from the Lexicographical Index"
188 ! Buckles, B. P., and Lybanon, M. ACM
189 ! Transactions on Mathematical Software, Vol. 3, No. 2, June 1977.
190
191 :: combination-indices ( x! p n -- seq )
192     x 1 + x!
193     p 0 <array> :> c 0 :> k! 0 :> r!
194     p 1 - [| i |
195         i [ 0 ] [ 1 - c nth ] if-zero i c set-nth
196         [ k x < ] [
197             i c [ 1 + ] change-nth
198             n i c nth - p i 1 + - nCk r!
199             k r + k!
200         ] do while k r - k!
201     ] each-integer
202     p 2 < [ 0 ] [ p 2 - c nth ] if
203     p 1 < [ drop ] [ x + k - p 1 - c set-nth ] if
204     c [ 1 - ] map! ;
205
206 PRIVATE>
207
208 : combination ( m seq k -- seq' )
209     swap [ length combination-indices ] [ nths-unsafe ] bi ;
210
211 TUPLE: combinations seq k length ;
212
213 : <combinations> ( seq k -- combinations )
214     2dup [ length ] [ nCk ] bi* combinations boa ;
215
216 M: combinations length length>> ; inline
217 M: combinations nth-unsafe [ seq>> ] [ k>> ] bi combination ;
218 M: combinations hashcode* tuple-hashcode ;
219
220 INSTANCE: combinations immutable-sequence
221
222 <PRIVATE
223
224 : find-max-index ( seq n -- i )
225     over length - '[ _ + >= ] find-index drop ; inline
226
227 : increment-rest ( i seq -- )
228     [ nth ] [ swap tail-slice ] 2bi
229     [ drop 1 + dup ] map! 2drop ; inline
230
231 : increment-last ( seq -- )
232     [ [ length 1 - ] keep [ 1 + ] change-nth ] unless-empty ; inline
233
234 :: next-combination ( seq n -- seq )
235     seq n find-max-index [
236         1 [-] seq increment-rest
237     ] [
238         seq increment-last
239     ] if* seq ; inline
240
241 :: combinations-quot ( seq k quot -- seq quot' )
242     seq length :> n
243     n k nCk <iota> k <iota> >array seq quot n
244     '[ drop _ [ _ nths-unsafe @ ] keep _ next-combination drop ] ; inline
245
246 PRIVATE>
247
248 : each-combination ( ... seq k quot: ( ... elt -- ... ) -- ... )
249     combinations-quot each ; inline
250
251 : map-combinations ( ... seq k quot: ( ... elt -- ... newelt ) -- ... newseq )
252     combinations-quot map ; inline
253
254 : filter-combinations ( ... seq k quot: ( ... elt -- ... ? ) -- ... newseq )
255     selector [ each-combination ] dip ; inline
256
257 : map>assoc-combinations ( ... seq k quot: ( ... elt -- ... key value ) exemplar -- ... assoc )
258     [ combinations-quot ] dip map>assoc ; inline
259
260 : all-combinations ( seq k -- seq' )
261     [ ] map-combinations ;
262
263 : all-combinations? ( ... seq k quot: ( ... elt -- ... ? ) -- ... ? )
264     combinations-quot all? ; inline
265
266 : find-combination ( ... seq k quot: ( ... elt -- ... ? ) -- ... elt/f )
267     [ f ] 3dip '[ nip _ keep swap ] combinations-quot find drop swap and ; inline
268
269 : reduce-combinations ( ... seq k identity quot: ( ... prev elt -- ... next ) -- ... result )
270     -rotd each-combination ; inline
271
272 : all-subsets ( seq -- subsets )
273     dup length [0..b] [ all-combinations ] with map concat ;
274
275 <PRIVATE
276
277 :: next-selection ( seq n -- )
278     1 seq length 1 - [
279         dup 0 >= [ over 0 = ] [ t ] if
280     ] [
281         [ seq [ + n /mod ] change-nth-unsafe ] keep 1 -
282     ] do until 2drop ; inline
283
284 :: selections-quot ( seq n quot -- seq quot' )
285     seq length :> len
286     n 0 <array> :> idx
287     n [ 0 ] [ len swap ^ ] if-zero <iota> [
288         drop
289         idx seq nths-unsafe quot call
290         idx len next-selection
291     ] ; inline
292
293 PRIVATE>
294
295 : each-selection ( ... seq n quot: ( ... elt -- ... ) -- ... )
296     selections-quot each ; inline
297
298 : map-selections ( ... seq n quot: ( ... elt -- ... newelt ) -- ... newseq )
299     selections-quot map ; inline
300
301 : filter-selections ( ... seq n quot: ( ... elt -- ... newelt ) -- ... newseq )
302     selector [ each-selection ] dip ; inline
303
304 : all-selections ( seq n -- seq' )
305     [ ] map-selections ;
306
307 : all-selections? ( seq n -- ? )
308     selections-quot all? ; inline
309
310 : find-selection ( ... seq n quot: ( ... elt -- ... ? ) -- ... elt/f )
311     [ f ] 3dip '[ nip _ keep swap ] selections-quot find drop swap and ; inline
312
313 : reduce-selections ( ... seq n identity quot: ( ... prev elt -- ... next ) -- ... result )
314     -rotd each-selection ; inline