Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/combination.py
4048 views
1
r"""
2
Combinations
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2007 Mike Hansen <[email protected]>,
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
#
9
# This code is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
# General Public License for more details.
13
#
14
# The full text of the GPL is available at:
15
#
16
# http://www.gnu.org/licenses/
17
#*****************************************************************************
18
19
from sage.interfaces.all import gap
20
from sage.rings.all import ZZ, Integer, binomial
21
from combinat import CombinatorialClass
22
from choose_nk import rank, from_rank
23
from integer_vector import IntegerVectors
24
from sage.misc.misc import uniq
25
26
def Combinations(mset, k=None):
27
"""
28
Returns the combinatorial class of combinations of mset. If k is
29
specified, then it returns the combinatorial class of combinations
30
of mset of size k.
31
32
The combinatorial classes correctly handle the cases where mset has
33
duplicate elements.
34
35
EXAMPLES::
36
37
sage: C = Combinations(range(4)); C
38
Combinations of [0, 1, 2, 3]
39
sage: C.list()
40
[[],
41
[0],
42
[1],
43
[2],
44
[3],
45
[0, 1],
46
[0, 2],
47
[0, 3],
48
[1, 2],
49
[1, 3],
50
[2, 3],
51
[0, 1, 2],
52
[0, 1, 3],
53
[0, 2, 3],
54
[1, 2, 3],
55
[0, 1, 2, 3]]
56
sage: C.cardinality()
57
16
58
59
::
60
61
sage: C2 = Combinations(range(4),2); C2
62
Combinations of [0, 1, 2, 3] of length 2
63
sage: C2.list()
64
[[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
65
sage: C2.cardinality()
66
6
67
68
::
69
70
sage: Combinations([1,2,2,3]).list()
71
[[],
72
[1],
73
[2],
74
[3],
75
[1, 2],
76
[1, 3],
77
[2, 2],
78
[2, 3],
79
[1, 2, 2],
80
[1, 2, 3],
81
[2, 2, 3],
82
[1, 2, 2, 3]]
83
"""
84
85
86
87
#Check to see if everything in mset is unique
88
if isinstance(mset, (int, Integer)):
89
mset = range(mset)
90
else:
91
mset = list(mset)
92
93
d = {}
94
for i in mset:
95
d[mset.index(i)] = 1
96
97
if len(d) == len(mset):
98
if k is None:
99
return Combinations_set(mset)
100
else:
101
return Combinations_setk(mset,k)
102
else:
103
if k is None:
104
return Combinations_mset(mset)
105
else:
106
return Combinations_msetk(mset,k)
107
108
class Combinations_mset(CombinatorialClass):
109
def __init__(self, mset):
110
"""
111
TESTS::
112
113
sage: C = Combinations(range(4))
114
sage: C == loads(dumps(C))
115
True
116
"""
117
self.mset = mset
118
119
def __contains__(self, x):
120
"""
121
EXAMPLES::
122
123
sage: c = Combinations(range(4))
124
sage: all( i in c for i in c )
125
True
126
sage: [3,4] in c
127
False
128
sage: [0,0] in c
129
False
130
"""
131
try:
132
x = list(x)
133
except TypeError:
134
return False
135
136
return all(i in self.mset for i in x) and \
137
len(uniq(x)) == len(x)
138
139
140
def __repr__(self):
141
"""
142
TESTS::
143
144
sage: repr(Combinations(range(4)))
145
'Combinations of [0, 1, 2, 3]'
146
"""
147
return "Combinations of %s"%self.mset
148
149
def __iter__(self):
150
"""
151
TESTS::
152
153
sage: Combinations(['a','a','b']).list() #indirect doctest
154
[[], ['a'], ['b'], ['a', 'a'], ['a', 'b'], ['a', 'a', 'b']]
155
"""
156
for k in range(len(self.mset)+1):
157
for comb in Combinations_msetk(self.mset, k):
158
yield comb
159
160
def cardinality(self):
161
"""
162
TESTS::
163
164
sage: Combinations([1,2,3]).cardinality()
165
8
166
sage: Combinations(['a','a','b']).cardinality()
167
6
168
"""
169
c = 0
170
for k in range(len(self.mset)+1):
171
c += Combinations_msetk(self.mset, k).cardinality()
172
return c
173
174
class Combinations_set(Combinations_mset):
175
def __iter__(self):
176
"""
177
EXAMPLES::
178
179
sage: Combinations([1,2,3]).list() #indirect doctest
180
[[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
181
"""
182
for k in range(len(self.mset)+1):
183
for comb in Combinations_setk(self.mset, k):
184
yield comb
185
186
187
def unrank(self, r):
188
"""
189
EXAMPLES::
190
191
sage: c = Combinations([1,2,3])
192
sage: c.list() == map(c.unrank, range(c.cardinality()))
193
True
194
"""
195
k = 0
196
n = len(self.mset)
197
b = binomial(n, k)
198
while r >= b:
199
r -= b
200
k += 1
201
b = binomial(n,k)
202
203
return map(lambda i: self.mset[i], from_rank(r, n, k))
204
205
206
def rank(self, x):
207
"""
208
EXAMPLES::
209
210
sage: c = Combinations([1,2,3])
211
sage: range(c.cardinality()) == map(c.rank, c)
212
True
213
"""
214
x = map(self.mset.index, x)
215
r = 0
216
n = len(self.mset)
217
for i in range(len(x)):
218
r += binomial(n, i)
219
r += rank(x, n)
220
return r
221
222
class Combinations_msetk(CombinatorialClass):
223
def __init__(self, mset, k):
224
"""
225
TESTS::
226
227
sage: C = Combinations([1,2,3],2)
228
sage: C == loads(dumps(C))
229
True
230
"""
231
self.mset = mset
232
self.k = k
233
234
def __contains__(self, x):
235
"""
236
EXAMPLES::
237
238
sage: c = Combinations(range(4),2)
239
sage: all( i in c for i in c )
240
True
241
sage: [0,1] in c
242
True
243
sage: [0,1,2] in c
244
False
245
sage: [3,4] in c
246
False
247
sage: [0,0] in c
248
False
249
"""
250
try:
251
x = list(x)
252
except TypeError:
253
return False
254
return x in Combinations_mset(self.mset) and len(x) == self.k
255
256
257
def __repr__(self):
258
"""
259
TESTS::
260
261
sage: repr(Combinations([1,2,2,3],2))
262
'Combinations of [1, 2, 2, 3] of length 2'
263
"""
264
return "Combinations of %s of length %s"%(self.mset, self.k)
265
266
def __iter__(self):
267
"""
268
EXAMPLES::
269
270
sage: Combinations(['a','a','b'],2).list() # indirect doctest
271
[['a', 'a'], ['a', 'b']]
272
"""
273
items = map(self.mset.index, self.mset)
274
indices = uniq(sorted(items))
275
counts = [0]*len(indices)
276
for i in items:
277
counts[indices.index(i)] += 1
278
for iv in IntegerVectors(self.k, len(indices), outer=counts):
279
yield sum([[self.mset[indices[i]]]*iv[i] for i in range(len(indices))],[])
280
281
def cardinality(self):
282
"""
283
Returns the size of combinations(mset,k). IMPLEMENTATION: Wraps
284
GAP's NrCombinations.
285
286
EXAMPLES::
287
288
sage: mset = [1,1,2,3,4,4,5]
289
sage: Combinations(mset,2).cardinality()
290
12
291
"""
292
items = map(self.mset.index, self.mset)
293
return ZZ(gap.eval("NrCombinations(%s,%s)"%(items,ZZ(self.k))))
294
295
296
297
class Combinations_setk(Combinations_msetk):
298
def _iterator(self, items, len_items, n):
299
"""
300
An iterator for all the n-combinations of items.
301
302
EXAMPLES::
303
304
sage: it = Combinations([1,2,3,4],3)._iterator([1,2,3,4],4,3)
305
sage: list(it)
306
[[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
307
"""
308
for i in range(len_items):
309
v = items[i:i+1]
310
if n == 1:
311
yield v
312
else:
313
rest = items[i+1:]
314
for c in self._iterator(rest, len_items-i-1, n-1):
315
yield v + c
316
317
def _iterator_zero(self):
318
"""
319
An iterator which just returns the empty list.
320
321
EXAMPLES::
322
323
sage: it = Combinations([1,2,3,4,5],3)._iterator_zero()
324
sage: list(it)
325
[[]]
326
"""
327
yield []
328
329
def __iter__(self):
330
r"""
331
Posted by Raymond Hettinger, 2006/03/23, to the Python Cookbook:
332
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/474124
333
334
EXAMPLES::
335
336
sage: Combinations([1,2,3,4,5],3).list() # indirect doctest
337
[[1, 2, 3],
338
[1, 2, 4],
339
[1, 2, 5],
340
[1, 3, 4],
341
[1, 3, 5],
342
[1, 4, 5],
343
[2, 3, 4],
344
[2, 3, 5],
345
[2, 4, 5],
346
[3, 4, 5]]
347
"""
348
if self.k == 0:
349
return self._iterator_zero()
350
else:
351
return self._iterator(self.mset, len(self.mset), self.k)
352
353
354
def list(self):
355
"""
356
EXAMPLES::
357
358
sage: Combinations([1,2,3,4,5],3).list()
359
[[1, 2, 3],
360
[1, 2, 4],
361
[1, 2, 5],
362
[1, 3, 4],
363
[1, 3, 5],
364
[1, 4, 5],
365
[2, 3, 4],
366
[2, 3, 5],
367
[2, 4, 5],
368
[3, 4, 5]]
369
"""
370
return list(self)
371
372
373
def unrank(self, r):
374
"""
375
EXAMPLES::
376
377
sage: c = Combinations([1,2,3], 2)
378
sage: c.list() == map(c.unrank, range(c.cardinality()))
379
True
380
"""
381
return map(lambda i: self.mset[i], from_rank(r, len(self.mset), self.k))
382
383
384
def rank(self, x):
385
"""
386
EXAMPLES::
387
388
sage: c = Combinations([1,2,3], 2)
389
sage: range(c.cardinality()) == map(c.rank, c.list())
390
True
391
"""
392
x = map(self.mset.index, x)
393
return rank(x, len(self.mset))
394
395