|
| 1 | +" |
| 2 | +Permutation is an Array, that - if it's reduced - consists of the numbers from (1 to: self size) in the original order. |
| 3 | +example: |
| 4 | +Permutation ordering: #(5 4 1). -> a Permutation(3 2 1) |
| 5 | +
|
| 6 | +you can think of a permutation as a positioning specification for a SequentialCollection. |
| 7 | +another example: |
| 8 | +a:=Permutation randomPermutation: 4. -> a Permutation(1 4 2 3) |
| 9 | +a permute: #(a b cd e). -> #(#a #e #b #cd) |
| 10 | +yet another one: |
| 11 | +Permutation ordering: #(a e b cd). ""a Permutation(1 4 2 3)"" |
| 12 | + |
| 13 | +
|
| 14 | +" |
| 15 | +Class { |
| 16 | + #name : #PMPermutation, |
| 17 | + #superclass : #Array, |
| 18 | + #type : #variable, |
| 19 | + #classVars : [ |
| 20 | + 'RandomGenerator' |
| 21 | + ], |
| 22 | + #category : #'Math-Permutation' |
| 23 | +} |
| 24 | + |
| 25 | +{ #category : #accessing } |
| 26 | +PMPermutation class >> allOfSize: anInteger [ |
| 27 | +"generates all permutations of the given size, in other words it produces the symmetric group of degree anInteger. |
| 28 | +Heap's algorithm, used here, seems to be just a tiny bit faster than using #permutationsDo:" |
| 29 | + | result perm c i ci| |
| 30 | + anInteger = 0 ifTrue:[^#()]. |
| 31 | + perm := self identity: anInteger. |
| 32 | + (result := WriteStream on:(Array new: anInteger factorial)) nextPut: perm copy. |
| 33 | + c := Array new: anInteger withAll: 1. |
| 34 | + i := 1. |
| 35 | + [ i <= anInteger ] |
| 36 | + whileTrue: [ |
| 37 | + (ci :=(c at: i)) < i |
| 38 | + ifTrue: [ |
| 39 | + i odd |
| 40 | + ifTrue: [ perm swap: 1 with: i ] |
| 41 | + ifFalse: [ perm swap: ci with: i ]. |
| 42 | + result nextPut: perm copy. |
| 43 | + c at: i put: ci + 1. |
| 44 | + i := 1 ] |
| 45 | + ifFalse: [ c at: i put: 1. i := i + 1 ] ]. |
| 46 | + ^ result contents |
| 47 | +] |
| 48 | + |
| 49 | +{ #category : #'instance creation' } |
| 50 | +PMPermutation class >> fromCycles: aCollectionofCollections [ |
| 51 | + | length | |
| 52 | + length := aCollectionofCollections flattened. |
| 53 | + length := length isEmpty |
| 54 | + ifTrue: [ 0 ] |
| 55 | + ifFalse: [ length max ]. |
| 56 | + ^ self size: length fromCycles: aCollectionofCollections |
| 57 | +] |
| 58 | + |
| 59 | +{ #category : #accessing } |
| 60 | +PMPermutation class >> generator:arrayOfPermutations [ |
| 61 | +|f max generators| |
| 62 | +max:=(arrayOfPermutations collect:[:g|g size])max. |
| 63 | +generators:=arrayOfPermutations collect:[:g| g extendTo: max]. |
| 64 | +f:=PMFixpoint |
| 65 | + block: [ :s| |aSet| |
| 66 | + aSet:=Set newFrom: s. |
| 67 | + s do:[:p|s do:[:q| |
| 68 | + aSet add:(p permute:q)]]. |
| 69 | + aSet] |
| 70 | + value: generators. |
| 71 | +f verbose:false. |
| 72 | +^f evaluate asArray. |
| 73 | + |
| 74 | +] |
| 75 | + |
| 76 | +{ #category : #'instance creation' } |
| 77 | +PMPermutation class >> identity: size [ |
| 78 | +^ super withAll: (1 to: size) |
| 79 | +] |
| 80 | + |
| 81 | +{ #category : #'instance creation' } |
| 82 | +PMPermutation class >> newFrom: aCollection [ |
| 83 | +"returns the unreduced form, for a reduced form use #ordering:. |
| 84 | +uses super withAll: since this way a primitive can be used, which is generally much faster than super newFrom:" |
| 85 | +^( super withAll: aCollection ) |
| 86 | +] |
| 87 | + |
| 88 | +{ #category : #'instance creation' } |
| 89 | +PMPermutation class >> ordering: aCollection [ |
| 90 | +"use #newFrom: for an unreduced Permutation! but then most things won't work before you call #reduce. |
| 91 | +aCollection must consist of elements that can be sorted via #<=" |
| 92 | +^( super withAll: aCollection ) reduce |
| 93 | +] |
| 94 | + |
| 95 | +{ #category : #accessing } |
| 96 | +PMPermutation class >> randomGenerator [ |
| 97 | +^RandomGenerator ifNil: [ RandomGenerator := Random new ] |
| 98 | +] |
| 99 | + |
| 100 | +{ #category : #'instance creation' } |
| 101 | +PMPermutation class >> randomPermutation: size [ |
| 102 | +^self ordering: (self randomGenerator next:size) |
| 103 | +] |
| 104 | + |
| 105 | +{ #category : #'instance creation' } |
| 106 | +PMPermutation class >> size: anInteger fromCycles: aCollectionofCollections [ |
| 107 | + | result | |
| 108 | + result := self identity: anInteger. |
| 109 | + aCollectionofCollections do: [ :cycle | |
| 110 | + 1 to: cycle size do: [ :i | |
| 111 | + result at: (cycle at: i) put: (cycle atWrap: i + 1) ] ]. |
| 112 | + ^ result |
| 113 | +] |
| 114 | + |
| 115 | +{ #category : #'instance creation' } |
| 116 | +PMPermutation class >> size: size shift: aNumber [ |
| 117 | +"number positive -> leftshift, negative -> rightshift" |
| 118 | +^ (super withAll: (1 to: size) )shift: aNumber |
| 119 | +] |
| 120 | + |
| 121 | +{ #category : #accessing } |
| 122 | +PMPermutation class >> stirling1:anInteger over:anotherInteger [ |
| 123 | +"unsigned Stirling number of the first kind: the number of permutations of size anInteger with anotherInteger number of cycles" |
| 124 | +|block| |
| 125 | +block:=[:nandk||n k| |
| 126 | + n:=nandk first. |
| 127 | + k:=nandk second. |
| 128 | + (n=k and:[n isZero]) |
| 129 | + ifTrue:[1] |
| 130 | + ifFalse:[ (n * k) isZero |
| 131 | + ifTrue:[0] |
| 132 | + ifFalse:[ (block value:{n-1.k-1})+((n-1)*(block value:{n-1.k}))]]]memoized . |
| 133 | +^block value:{anInteger . anotherInteger } |
| 134 | +] |
| 135 | + |
| 136 | +{ #category : #converting } |
| 137 | +PMPermutation >> asCycles [ |
| 138 | + | unused start next result cycle | |
| 139 | + unused := (1 to: self size) asOrderedCollection. |
| 140 | + result := OrderedCollection new. |
| 141 | + [ unused isEmpty ] |
| 142 | + whileFalse: [ |
| 143 | + next := start := unused first. |
| 144 | + cycle := OrderedCollection new. |
| 145 | + [ cycle add: (unused remove: next). |
| 146 | + next := self at: next ] doWhileFalse: [ next = start ]. |
| 147 | + result add: cycle asArray ]. |
| 148 | + ^ result asArray |
| 149 | +] |
| 150 | + |
| 151 | +{ #category : #converting } |
| 152 | +PMPermutation >> asMatrix [ |
| 153 | + ^ PMMatrix |
| 154 | + rows: |
| 155 | + (self asPMVector |
| 156 | + collect: [ :n | |
| 157 | + (PMVector new: self size) |
| 158 | + atAllPut: 0; |
| 159 | + at: n put: 1; |
| 160 | + yourself ]) |
| 161 | +] |
| 162 | + |
| 163 | +{ #category : #applying } |
| 164 | +PMPermutation >> discriminant [ |
| 165 | +^self size - self asCycles size |
| 166 | +] |
| 167 | + |
| 168 | +{ #category : #testing } |
| 169 | +PMPermutation >> even [ |
| 170 | +^self odd not |
| 171 | +] |
| 172 | + |
| 173 | +{ #category : #applying } |
| 174 | +PMPermutation >> extendTo: size [ |
| 175 | + | c | |
| 176 | + size=self size ifTrue: [ ^self copy ]. |
| 177 | + c := self class identity: size. |
| 178 | + c |
| 179 | + replaceFrom: 1 |
| 180 | + to: self size |
| 181 | + with: self |
| 182 | + startingAt: 1. |
| 183 | + ^ c |
| 184 | +] |
| 185 | + |
| 186 | +{ #category : #applying } |
| 187 | +PMPermutation >> inverse [ |
| 188 | +|c| |
| 189 | +c:=self class new:self size. |
| 190 | +1 to: self size do: [:i | c at: i put: (self indexOf: i)]. |
| 191 | +^c |
| 192 | +] |
| 193 | + |
| 194 | +{ #category : #testing } |
| 195 | +PMPermutation >> isCollection [ |
| 196 | +"pffh, i found this useful, but i dont remember anymore why." |
| 197 | + ^ false |
| 198 | +] |
| 199 | + |
| 200 | +{ #category : #testing } |
| 201 | +PMPermutation >> odd [ |
| 202 | +"using the number of transpositions is faster than using the number of inversions" |
| 203 | +^self discriminant odd. |
| 204 | +] |
| 205 | + |
| 206 | +{ #category : #applying } |
| 207 | +PMPermutation >> permute: aSequentialCollection [ |
| 208 | + | s c | |
| 209 | + (s := aSequentialCollection size) < self size |
| 210 | + ifTrue: [ aSequentialCollection class==self class |
| 211 | + ifTrue: [ ^ self permute: (aSequentialCollection extendTo: self size) ] |
| 212 | + ifFalse: [ ^ SizeMismatch signal ] ]. |
| 213 | + c := aSequentialCollection copy. |
| 214 | + 1 to: self size do: [ :i | c at: i put: (aSequentialCollection at: (self at: i)) ]. |
| 215 | + ^ c |
| 216 | +] |
| 217 | + |
| 218 | +{ #category : #private } |
| 219 | +PMPermutation >> reduce [ |
| 220 | +"automatically used only in #withAll: so far" |
| 221 | + | sorted range | |
| 222 | + (sorted := self sorted) = (range := 1 to: self size) |
| 223 | + ifTrue: [ ^ self ]. |
| 224 | + self size = self asSet size |
| 225 | + ifFalse: [ ^ self error: 'Permutation has doubles' ]. |
| 226 | + range do: [ :n | self at: n put: (sorted indexOf: (self at: n)) ] |
| 227 | +] |
| 228 | + |
| 229 | +{ #category : #converting } |
| 230 | +PMPermutation >> reversed [ |
| 231 | + "copy of SequenceableCollection>>reversed, but uses class instead of species." |
| 232 | + | n result src | |
| 233 | + n := self size. |
| 234 | + result := self class new: n. |
| 235 | + src := n + 1. |
| 236 | + 1 to: n do: [:i | result at: i put: (self at: (src := src - 1))]. |
| 237 | + ^ result |
| 238 | +] |
| 239 | + |
| 240 | +{ #category : #converting } |
| 241 | +PMPermutation >> shift: anInteger [ |
| 242 | +"number positive -> leftshift, negative -> rightshift. |
| 243 | + does _not_ return a new Permutation!" |
| 244 | + | n c | |
| 245 | + self ifEmpty: [ ^ self ]. |
| 246 | + n := anInteger \\ self size. |
| 247 | + c := self copy. |
| 248 | + self |
| 249 | + replaceFrom: 1 to: self size - n with: c startingAt: n + 1; |
| 250 | + replaceFrom: self size - n + 1 to: self size with: c startingAt: 1 |
| 251 | +] |
| 252 | + |
| 253 | +{ #category : #private } |
| 254 | +PMPermutation >> species [ |
| 255 | + ^ Array |
| 256 | +] |
0 commit comments