【问题标题】:Generating sorted random ints without the sort? O(n)在没有排序的情况下生成排序的随机整数?在)
【发布时间】:2010-12-24 08:59:26
【问题描述】:

刚刚看到一个关于generating a sorted list of 100 random integers 的高尔夫代码问题。然而,突然出现在我脑海中的想法是,您可以生成一个正增量列表,然后继续将它们添加到运行总数中,因此:

deltas: 1 3 2  7  2
ints:   1 4 6 13 15

其实你会使用浮点数,然后归一化以适应某个上限,然后进行四舍五入,但效果是一样的。

虽然它不会缩短代码,但如果没有排序步骤,它肯定会更快。但我没有真正处理的是:得到的整数分布是否与从均匀分布的概率密度函数生成 100 个随机整数相同?

编辑:示例脚本:

import random,sys
running = 0
max = 1000
deltas = [random.random() for i in range(0,11)]
floats = []
for d in deltas:
    running += d
    floats.append(running)
upper = floats.pop()
ints = [int(round(f/upper*max)) for f in floats]
print(ints)

谁的输出(公平掷骰子)是:

[24, 71, 133, 261, 308, 347, 499, 543, 722, 852]

更新:Alok's answerDan Dyer's comment 指出,使用 exponential distribution 表示增量会得到整数的均匀分布。

【问题讨论】:

  • 这不会是统一的。请参阅我的答案或 Rupert Nash 的答案。

标签: math sorting random


【解决方案1】:

uniform distribution 有一个上限和一个下限。如果您使用您提出的方法,并且您的增量恰好选择得足够大,以至于您在生成所有数字之前就遇到了上限,那么您的算法接下来会做什么?

话虽如此,您可能想研究Poisson distribution,它是在给定平均频率下发生的随机事件之间的间隔时间分布。

【讨论】:

  • 我想他已经回答了这个问题,不是吗?如果您使用您设置的浮点数,则根据最大增量大小的倍数计算最大上限。然后在最后进行标准化,使数据范围与上限完全匹配。
  • 是的,尽管您会生成一个最终的额外数字以丢弃,这样最后一个数字并不总是只是上限。为了清楚起见,添加了代码示例。
  • 泊松分布很有趣,可能是这里需要的,但它给出了在给定时间内发生的许多事件的概率,而不是单个事件之间时间的概率分布。知道如何修改它来获得它吗?
  • @Phil H:对事件之间的时间使用指数分布。
  • @Dan:谢谢你,也感谢 Alok 在他的回答中指出了这一点。
【解决方案2】:

我认为它会非常相似,但由于标准化,极端情况会有所不同。例如,在 1 到 100 之间随机选择的 100 个数字都可能是 1。但是,使用您的系统创建的 100 个数字可能都有 0.01 的增量,但是当您对它们进行标准化时,您会将它们放大到 1 -> 范围内100 这意味着你永远不会得到一组非常低的数字的奇怪可能性。

【讨论】:

  • 我将只生成一个最终增量来获得我的上限,这将被丢弃。因此,第 101 个数字可能很大,符合您描述的情况。
  • 好吧,假设你想得到 1 -> 100,然后随机生成你的上限(比如 67)。这将意味着您的范围自然会倾向于均匀分布在 1 到 67 之间,而不是 1 到 100 之间,而 67 恰好是最高数字。它看起来不太一样......
【解决方案3】:

如果你取1到1000的数字范围,你必须使用这些数字中的100个,delta必须至少为10,否则你无法达到1000标记。一些工作来证明它的实际效果怎么样......

任何给定数字在均匀分布的随机选择中的机会是 100/1000,例如1/10 - 没有震惊,以此为基础。

假设您开始使用 delta 并且该 delta 仅为 10。

获得数字 1 的几率是 1/10 - 看起来不错。 获得数字 2 的几率是 1/10 + (1/10 * 1/10)(因为您可以连续命中 2 个 1 的增量,或者仅将 2 作为第一个增量。) 得到数字 3 的几率是 1/10 + (1/10 * 1/10 * 1/10) + (1/10 * 1/10) + (1/10 * 1/10)

第一种情况是 3 的 delta,第二种情况是连续 3 个 1 的 delta,第三种情况是 1 的 delta 后跟 2,第 4 种情况是 2 的 delta 后跟 a 1.

为了我的手指打字,我们不会生成达到 5 的组合。

前几个数字立即比直接随机具有更大的百分比机会。

这可以通过改变 delta 值来改变,所以分数都不同,但我不相信你能找到产生相同赔率的 delta。

打个比方,如果你认为你的 delta 只是 6 并且你运行两次它相当于扔了 2 个骰子 - 每个 delta 都是独立的,但你知道 7 有更高的被选中的几率大于2。

【讨论】:

  • 您的意思是您不能对增量值使用 uniform 分布。没错,这就是泊松分布在这种情况下提供的优势。
【解决方案4】:

你可以分两次完成;

在第一遍中,生成 0 和 (MAX_RAND/n) 之间的增量

在第二遍中,将随机数标准化为范围内

仍然是 O(n),具有良好的参考局部性。

【讨论】:

  • 嗯,再看一遍OP,我想他已经到了这一点
【解决方案5】:

所以你问的是,以这种方式生成的数字是否会均匀分布。

您正在生成一个系列:

yj = ∑i=0j ( xi / A )

其中A 是所有 xi 的总和。 xi 是(正)增量列表。

如果 xi 呈指数分布(具有任何固定均值),则可以做到这一点。所以,如果 xi 是均匀分布的,那么得到的 yj 将不会是均匀分布的。

话虽如此,生成指数 xi 值相当容易。

一个例子是:

sum := 0
for I = 1 to N do:
    X[I] = sum = sum - ln(RAND)
sum = sum - ln(RAND)
for I = 1 to N do:
    X[I] = X[I]/sum

您的随机数将在[0, 1) 范围内排序。

参考:Generating Sorted Lists of Random Numbers。该论文还有其他(更快的)算法。

当然,这会生成浮点数。对于整数的均匀分布,可以在最后一步将上面的sum替换为sum/RANGE(即RHS变为X[I]*RANGE/sum,然后将数字四舍五入到最接近的整数)。

【讨论】:

  • 太棒了!以前一直都是这样!我不明白 X[I] = sum = sum - ln(RAND) 的代码行。为什么要减去?顺便说一句,也许在 HTML 中将您的方程式格式化为: yj = ∑i=0j ( x / A)
  • 这是一个比我更有用的答案!
  • 谢谢菲尔,我已经使用 HTML 编辑了回复。关于减法的原因,我们添加了 -ln(RAND),它是指数分布的逆累积分布函数。第一个循环之后的减法确保 X[N] != 1。
  • 干杯,符号让我有点吃惊,我忘记了 RAND 将是
  • 这里是不在付费墙后面的论文版本:repository.cmu.edu/cgi/…
【解决方案6】:

问:得到的整数分布是否与从均匀分布的概率密度函数生成 100 个随机整数相同?

A:每个增量都会均匀分布。中心极限定理告诉我们,大量此类偏差的总和的分布(因为它们具有有限的均值和方差)将趋于正态分布。因此,您序列中后面的偏差将不会均匀分布。

所以简短的回答是“不”。恐怕我不能在不做代数的情况下给出一个简单的解决方案,我今天没时间做!

【讨论】:

  • 您的意思是,如果我在[1..n] 范围内有一个统一(未排序)的随机数序列,那么增量将不会均匀分布在[0..n-1] 范围内?
  • 我的意思是均匀分布的偏差(即增量)的总和不会按要求均匀分布。你所说的也很简单:因为你提到的未排序的数字平均有一半的差异是负的。
  • @Andreas:不,他们会有三角分布:en.wikipedia.org/wiki/Triangular_distribution
【解决方案7】:

Alok's answerDan Dyer's comment 指出,使用exponential distribution 表示增量会得到整数的均匀分布。

所以问题中代码示例的新版本将是:

import random,sys
running = 0
max = 1000
deltas = [random.expovariate(1.0) for i in range(0,11)]
floats = []
for d in deltas:
    running += d
    floats.append(running)
upper = floats.pop()
ints = [int(round(f/upper*max)) for f in floats]
print(ints)

注意random.expovariate(1.0) 的使用,Python exponential distribution random number generator(非常有用!)。这里调用它的平均值为 1.0,但由于脚本会根据序列中的最后一个数字进行标准化,因此平均值本身并不重要。

输出(公平掷骰子):

[11, 43, 148, 212, 249, 458, 539, 725, 779, 871]

【讨论】:

    【解决方案8】:

    Alok's answer 中的 reference (1979) 很有趣。它给出了一种生成统一顺序统计量的算法,而不是通过加法而是通过连续乘法:

    max = 1.
    for i = N downto 1 do
       out[i] = max = max * RAND^(1/i)
    

    其中 RAND 在 [0,1) 上是均匀的。这样您就不必在最后进行规范化,实际上甚至不必将数字存储在数组中;您可以将其用作迭代器。

    The Exponential distribution: theory, methods and applications By N. Balakrishnan, Asit P. Basu 在第 22 页给出了该算法的另一个推导,并归功于 Malmquist (1950)。

    【讨论】:

    • 一开始我以为马奎斯特与天文学中被命名为“马奎斯特偏差”的那个是同一个,但事实证明并非如此。所以至少有两位著名的马尔奎斯特统计学家:-)
    • 如何避免标准化?对于 1 到 255 之间的整数,您的代码只会生成越来越小的值,并且都小于 1。如果提供了 max int,则必须一次性生成该集合,否则平均值等会发生变化。跨度>
    • 从 max = 255 开始,用 Int(*) 量化输出。加一得到 [1,255]。
    猜你喜欢
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-28
    • 2011-08-03
    • 2020-06-07
    相关资源
    最近更新 更多