Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/multichoose_nk.py
4057 views
1
"""
2
Low-level multichoose
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
from combinat import CombinatorialClass
19
from sage.rings.arith import binomial
20
import sage.misc.prandom as rnd
21
22
class MultichooseNK(CombinatorialClass):
23
def __init__(self, n, k):
24
"""
25
TESTS::
26
27
sage: a = MultichooseNK(3,2)
28
sage: a == loads(dumps(a))
29
True
30
"""
31
self._n = n
32
self._k = k
33
34
def cardinality(self):
35
"""
36
Returns the number of multichoices of k things from a list of n
37
things.
38
39
EXAMPLES::
40
41
sage: MultichooseNK(3,2).cardinality()
42
6
43
"""
44
n,k = self._n, self._k
45
return binomial(n+k-1,k)
46
47
def __iter__(self):
48
"""
49
An iterator for all multichoices of k things from range(n).
50
51
EXAMPLES::
52
53
sage: [c for c in MultichooseNK(3,2)]
54
[[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]
55
"""
56
n,k = self._n, self._k
57
dif = 0
58
if k == 0:
59
yield []
60
return
61
62
if n < 1+(k-1)*dif:
63
return
64
else:
65
subword = [ i*dif for i in range(k) ]
66
67
yield subword[:]
68
finished = False
69
70
while not finished:
71
#Find the biggest element that can be increased
72
if subword[-1] < n-1:
73
subword[-1] += 1
74
yield subword[:]
75
continue
76
77
finished = True
78
for i in reversed(range(k-1)):
79
if subword[i]+dif < subword[i+1]:
80
subword[i] += 1
81
#Reset the bigger elements
82
for j in range(1,k-i):
83
subword[i+j] = subword[i]+j*dif
84
yield subword[:]
85
finished = False
86
break
87
88
return
89
90
def random_element(self):
91
"""
92
Returns a random multichoice of k things from range(n).
93
94
EXAMPLES::
95
96
sage: MultichooseNK(5,2).random_element()
97
[0, 2]
98
sage: MultichooseNK(5,2).random_element()
99
[0, 1]
100
"""
101
n,k = self._n, self._k
102
rng = range(n)
103
r = []
104
for i in range(k):
105
r.append( rnd.choice(rng))
106
107
r.sort()
108
return r
109
110