Coverage for /usr/lib/python3/dist-packages/sympy/ntheory/multinomial.py: 46%

87 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from sympy.utilities.misc import as_int 

2 

3 

4def binomial_coefficients(n): 

5 """Return a dictionary containing pairs :math:`{(k1,k2) : C_kn}` where 

6 :math:`C_kn` are binomial coefficients and :math:`n=k1+k2`. 

7 

8 Examples 

9 ======== 

10 

11 >>> from sympy.ntheory import binomial_coefficients 

12 >>> binomial_coefficients(9) 

13 {(0, 9): 1, (1, 8): 9, (2, 7): 36, (3, 6): 84, 

14 (4, 5): 126, (5, 4): 126, (6, 3): 84, (7, 2): 36, (8, 1): 9, (9, 0): 1} 

15 

16 See Also 

17 ======== 

18 

19 binomial_coefficients_list, multinomial_coefficients 

20 """ 

21 n = as_int(n) 

22 d = {(0, n): 1, (n, 0): 1} 

23 a = 1 

24 for k in range(1, n//2 + 1): 

25 a = (a * (n - k + 1))//k 

26 d[k, n - k] = d[n - k, k] = a 

27 return d 

28 

29 

30def binomial_coefficients_list(n): 

31 """ Return a list of binomial coefficients as rows of the Pascal's 

32 triangle. 

33 

34 Examples 

35 ======== 

36 

37 >>> from sympy.ntheory import binomial_coefficients_list 

38 >>> binomial_coefficients_list(9) 

39 [1, 9, 36, 84, 126, 126, 84, 36, 9, 1] 

40 

41 See Also 

42 ======== 

43 

44 binomial_coefficients, multinomial_coefficients 

45 """ 

46 n = as_int(n) 

47 d = [1] * (n + 1) 

48 a = 1 

49 for k in range(1, n//2 + 1): 

50 a = (a * (n - k + 1))//k 

51 d[k] = d[n - k] = a 

52 return d 

53 

54 

55def multinomial_coefficients(m, n): 

56 r"""Return a dictionary containing pairs ``{(k1,k2,..,km) : C_kn}`` 

57 where ``C_kn`` are multinomial coefficients such that 

58 ``n=k1+k2+..+km``. 

59 

60 Examples 

61 ======== 

62 

63 >>> from sympy.ntheory import multinomial_coefficients 

64 >>> multinomial_coefficients(2, 5) # indirect doctest 

65 {(0, 5): 1, (1, 4): 5, (2, 3): 10, (3, 2): 10, (4, 1): 5, (5, 0): 1} 

66 

67 Notes 

68 ===== 

69 

70 The algorithm is based on the following result: 

71 

72 .. math:: 

73 \binom{n}{k_1, \ldots, k_m} = 

74 \frac{k_1 + 1}{n - k_1} \sum_{i=2}^m \binom{n}{k_1 + 1, \ldots, k_i - 1, \ldots} 

75 

76 Code contributed to Sage by Yann Laigle-Chapuy, copied with permission 

77 of the author. 

78 

79 See Also 

80 ======== 

81 

82 binomial_coefficients_list, binomial_coefficients 

83 """ 

84 m = as_int(m) 

85 n = as_int(n) 

86 if not m: 

87 if n: 

88 return {} 

89 return {(): 1} 

90 if m == 2: 

91 return binomial_coefficients(n) 

92 if m >= 2*n and n > 1: 

93 return dict(multinomial_coefficients_iterator(m, n)) 

94 t = [n] + [0] * (m - 1) 

95 r = {tuple(t): 1} 

96 if n: 

97 j = 0 # j will be the leftmost nonzero position 

98 else: 

99 j = m 

100 # enumerate tuples in co-lex order 

101 while j < m - 1: 

102 # compute next tuple 

103 tj = t[j] 

104 if j: 

105 t[j] = 0 

106 t[0] = tj 

107 if tj > 1: 

108 t[j + 1] += 1 

109 j = 0 

110 start = 1 

111 v = 0 

112 else: 

113 j += 1 

114 start = j + 1 

115 v = r[tuple(t)] 

116 t[j] += 1 

117 # compute the value 

118 # NB: the initialization of v was done above 

119 for k in range(start, m): 

120 if t[k]: 

121 t[k] -= 1 

122 v += r[tuple(t)] 

123 t[k] += 1 

124 t[0] -= 1 

125 r[tuple(t)] = (v * tj) // (n - t[0]) 

126 return r 

127 

128 

129def multinomial_coefficients_iterator(m, n, _tuple=tuple): 

130 """multinomial coefficient iterator 

131 

132 This routine has been optimized for `m` large with respect to `n` by taking 

133 advantage of the fact that when the monomial tuples `t` are stripped of 

134 zeros, their coefficient is the same as that of the monomial tuples from 

135 ``multinomial_coefficients(n, n)``. Therefore, the latter coefficients are 

136 precomputed to save memory and time. 

137 

138 >>> from sympy.ntheory.multinomial import multinomial_coefficients 

139 >>> m53, m33 = multinomial_coefficients(5,3), multinomial_coefficients(3,3) 

140 >>> m53[(0,0,0,1,2)] == m53[(0,0,1,0,2)] == m53[(1,0,2,0,0)] == m33[(0,1,2)] 

141 True 

142 

143 Examples 

144 ======== 

145 

146 >>> from sympy.ntheory.multinomial import multinomial_coefficients_iterator 

147 >>> it = multinomial_coefficients_iterator(20,3) 

148 >>> next(it) 

149 ((3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 1) 

150 """ 

151 m = as_int(m) 

152 n = as_int(n) 

153 if m < 2*n or n == 1: 

154 mc = multinomial_coefficients(m, n) 

155 yield from mc.items() 

156 else: 

157 mc = multinomial_coefficients(n, n) 

158 mc1 = {} 

159 for k, v in mc.items(): 

160 mc1[_tuple(filter(None, k))] = v 

161 mc = mc1 

162 

163 t = [n] + [0] * (m - 1) 

164 t1 = _tuple(t) 

165 b = _tuple(filter(None, t1)) 

166 yield (t1, mc[b]) 

167 if n: 

168 j = 0 # j will be the leftmost nonzero position 

169 else: 

170 j = m 

171 # enumerate tuples in co-lex order 

172 while j < m - 1: 

173 # compute next tuple 

174 tj = t[j] 

175 if j: 

176 t[j] = 0 

177 t[0] = tj 

178 if tj > 1: 

179 t[j + 1] += 1 

180 j = 0 

181 else: 

182 j += 1 

183 t[j] += 1 

184 

185 t[0] -= 1 

186 t1 = _tuple(t) 

187 b = _tuple(filter(None, t1)) 

188 yield (t1, mc[b])