@@ -1174,11 +1174,30 @@ def random_sublist(X, s):
1174
1174
return [a for a in X if random .random () <= s ]
1175
1175
1176
1176
1177
- def some_tuples (elements , repeat , bound ):
1177
+ def some_tuples (elements , repeat , bound , max_samples = None ):
1178
1178
r"""
1179
1179
Return an iterator over at most ``bound`` number of ``repeat``-tuples of
1180
1180
``elements``.
1181
1181
1182
+ INPUT:
1183
+
1184
+ - ``elements`` -- an iterable
1185
+ - ``repeat`` -- integer (default ``None``), the length of the tuples to be returned.
1186
+ If ``None``, just returns entries from ``elements``.
1187
+ - ``bound`` -- the maximum number of tuples returned (ignored if ``max_samples`` given)
1188
+ - ``max_samples`` -- non-negative integer (default ``None``). If given,
1189
+ then a sample of the possible tuples will be returned,
1190
+ instead of the first few in the standard order.
1191
+
1192
+ OUTPUT:
1193
+
1194
+ If ``max_samples`` is not provided, an iterator over the first
1195
+ ``bound`` tuples of length ``repeat``, in the standard nested-for-loop order.
1196
+
1197
+ If ``max_samples is provided, a list of at most ``max_samples`` tuples,
1198
+ sampled uniformly from the possibilities. In this case, ``elements``
1199
+ must be finite.
1200
+
1182
1201
TESTS::
1183
1202
1184
1203
sage: from sage.misc.misc import some_tuples
@@ -1192,15 +1211,44 @@ def some_tuples(elements, repeat, bound):
1192
1211
sage: len(list(l))
1193
1212
10
1194
1213
1195
- .. TODO::
1214
+ sage: l = some_tuples(range(3), 2, None, max_samples=10)
1215
+ sage: len(list(l))
1216
+ 9
1217
+ """
1218
+ if max_samples is None :
1219
+ from itertools import islice , product
1220
+ P = elements if repeat is None else product (elements , repeat = repeat )
1221
+ return islice (P , bound )
1222
+ else :
1223
+ if not (hasattr (elements , '__len__' ) and hasattr (elements , '__getitem__' )):
1224
+ elements = list (elements )
1225
+ n = len (elements )
1226
+ N = n if repeat is None else n ** repeat
1227
+ if N <= max_samples :
1228
+ from itertools import product
1229
+ return elements if repeat is None else product (elements , repeat = repeat )
1230
+ return _some_tuples_sampling (elements , repeat , max_samples , n )
1196
1231
1197
- Currently, this only return an iterator over the first element of the
1198
- Cartesian product. It would be smarter to return something more
1199
- "random like" as it is used in tests. However, this should remain
1200
- deterministic.
1232
+ def _some_tuples_sampling (elements , repeat , max_samples , n ):
1201
1233
"""
1202
- from itertools import islice , product
1203
- return islice (product (elements , repeat = repeat ), bound )
1234
+ Internal function for :func:`some_tuples`.
1235
+
1236
+ TESTS::
1237
+
1238
+ sage: from sage.misc.misc import _some_tuples_sampling
1239
+ sage: list(_some_tuples_sampling(range(3), 3, 2, 3))
1240
+ [(0, 1, 0), (1, 1, 1)]
1241
+ sage: list(_some_tuples_sampling(range(20), None, 4, 20))
1242
+ [0, 6, 9, 3]
1243
+ """
1244
+ from sage .rings .integer import Integer
1245
+ N = n if repeat is None else n ** repeat
1246
+ # We sample on range(N) and create tuples manually since we don't want to create the list of all possible tuples in memory
1247
+ for a in random .sample (range (N ), max_samples ):
1248
+ if repeat is None :
1249
+ yield elements [a ]
1250
+ else :
1251
+ yield tuple (elements [j ] for j in Integer (a ).digits (n , padto = repeat ))
1204
1252
1205
1253
def powerset (X ):
1206
1254
r"""
0 commit comments