-
Notifications
You must be signed in to change notification settings - Fork 13
/
derangements.factor
54 lines (40 loc) · 1.74 KB
/
derangements.factor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
USING: arrays kernel math math.combinatorics
math.combinatorics.private math.factorials random ranges
sequences ;
FROM: sequences.private => nth-unsafe exchange-unsafe ;
IN: derangements
: derangement? ( indices -- ? )
dup length <iota> [ = ] 2any? not ;
: <derangement-iota> ( seq -- <iota> )
length subfactorial <iota> ; inline
: next-derangement ( seq -- seq )
[ dup derangement? ] [ next-permutation ] do until ;
: derangements-quot ( seq quot -- seq quot' )
[ [ <derangement-iota> ] [ length <iota> >array ] [ ] tri ] dip
'[ drop _ next-derangement _ nths-unsafe @ ] ; inline
: each-derangement ( ... seq quot: ( ... elt -- ... ) -- ... )
derangements-quot each ; inline
: map-derangements ( ... seq quot: ( ... elt -- ... newelt ) -- ... newseq )
derangements-quot map ; inline
: filter-derangements ( ... seq quot: ( ... elt -- ... ? ) -- ... newseq )
selector [ each-derangement ] dip ; inline
: all-derangements ( seq -- seq' )
[ ] map-derangements ;
: all-derangements? ( ... seq quot: ( ... elt -- ... ? ) -- ... ? )
derangements-quot all? ; inline
: find-derangement ( ... seq quot: ( ... elt -- ... ? ) -- ... elt/f )
'[ _ keep and ] derangements-quot map-find drop ; inline
: reduce-derangements ( ... seq identity quot: ( ... prev elt -- ... next ) -- ... result )
swapd each-derangement ; inline
:: random-derangement-indices ( n -- indices )
n <iota> >array :> seq
f [
dup :> v
n 1 (a..b] [| j |
j 1 + random :> p
p v nth-unsafe j =
[ t ] [ j p v exchange-unsafe f ] if
] any? v first zero? or
] [ drop seq clone ] do while ;
: random-derangement ( seq -- seq' )
[ length random-derangement-indices ] [ nths-unsafe ] bi ;