【发布时间】:2020-06-09 13:50:45
【问题描述】:
我正在阅读一本名为“编程面试要素”的书,但遇到了以下问题:
实现一个算法,该算法将一个不同的数组作为输入 元素和大小,并返回给定大小的子集 数组元素。所有子集的可能性应该相等。返回 结果是输入数组本身。
他们提供的解决方案如下:
import random
def random_sampling(k, A):
for i in range(k):
# Generate a random index in [i, len(A) - 1].
r = random.randint(i, len(A) - 1)
A[i], A[r] = A[r], A[i]
A = [3, 7, 5, 11]
k = 3
print(random_sampling(k, A))
我不明白作者试图直观地做什么。他们的解释如下
另一种方法是枚举所有大小为 k 的子集,然后选择 从这些中随机一个。由于有 (n 到 k) 个大小为 k 的子集, 时间和空间的复杂性是巨大的。高效的关键 构建一个大小正好为 k 的随机子集是首先构建一个 大小 k - 1 然后再添加一个元素,随机选择 其余的部分。当 k = 1 时,这个问题是微不足道的。我们调用 随机数生成器,取返回值 mod n(称为 r), 并将 A[0] 与 A[r] 交换。条目 A[0] 现在保存结果。
对于 k > 1,我们首先像上面一样随机选择一个元素,然后我们 现在对 n - 1 个元素的子数组 A[1, n -1] 重复相同的过程。 最终,随机子集占据槽 A[0, k - 1] 和 剩余元素在最后 n - k 个槽中。
直观地说,如果所有大小为 k 的子集都是等可能的,那么 构造过程确保大小为 k + 1 的子集也是 同样可能。对此的正式证明使用数学归纳法 - 归纳假设是每个大小 k 的每个排列 A 的子集同样可能在 A[0, k -1] 中。
作为一个具体的例子,让输入是 A = 和大小 是 3. 在第一次迭代中,我们使用随机数生成器 在区间 [0,3] 中选择一个随机整数。让返回的随机 数字是 2。我们用 A[2] 交换 A[0] - 现在数组是 。 现在我们在区间 [1, 3] 中选择一个随机整数。让返回的 随机数为 3。我们将 A[1] 与 A[3] 交换 - 现在是结果数组 是 。现在我们在区间 [2,3] 中选择一个随机整数。 令返回的随机数为 2。当我们将 A[2] 与自身交换时, 结果数组不变。随机子集首先由他组成 三个条目,即 {5, 11, 3}。
抱歉,文字太长了;我的问题是这样的
- 他们所指的效率的关键是什么?它没有在我的脑海中点击
- “最终,随机子集占据插槽 A[0, k-1] 而其余元素位于最后 n - k 个插槽中”是什么意思
- 是否有明确的理由说明“A 的每个大小 k 子集的每个排列都同样可能在 A[0, k - 1] 中”?
- 您能否更清楚地解释算法背后的理论?
- 算法的返回值应该是多少?
谢谢
【问题讨论】:
-
(这里的问题太多了...)你明白
A[i], A[r] = A[r], A[i]的那一行在做什么吗?对我来说,这个算法似乎很关键。 -
On 5:“
return语句返回一个函数的值。return没有表达式参数返回None。从函数的末尾落下也返回 @ 987654327@。” (The Official Pyhton Tutorial)。所以这会返回并打印None(顺便说一句,如果你运行它,你也可以看到它)。
标签: python arrays algorithm random data-structures