如何在Python中随机生成递减的数?

4
我想知道是否有一种方法可以在特定范围内生成递减的数字?我希望编程能够持续输出直到达到0,且该范围中最大的数字必须是正数。
例如,如果范围为(0,100),则可能的输出如下: 96 57 43 23 9 0
对于我原始帖子所造成的混淆,请谅解。

你的输入是什么?你需要能够指定或保证生成多少个数字吗?所有生成的数字是否都应该是唯一的? - Silas Ray
1
另外,您需要整数吗?浮点数值?范围是否包括0或负值? - Silas Ray
4
这个问题的语义似乎会导致各种不同的答案。是范围在缩小还是仅仅是数字数量减少了?你是在寻找一个生成器,还是只需要一个数字列表就满足了。 - Lewis Norton
@Fiona,你知道随机变量概率分布吗?如果不知道,找一本数学书,标题类似于“概率论导论”,并阅读第一章。然后你就有了准确陈述问题的语言,如果你继续阅读,还可以学到解决问题的方法。 - Colonel Panic
我必须运行一个循环,直到它达到0为止不断发送数字。因此,范围可以从0到非常大的数字。这里是一个简短的列表作为示例:105,97,43,35,29,18,3,0。 - Fiona Kwok
8个回答

18

我会生成n个随机数,然后将它们从高到低排序。


1
这并不像在更小的范围内每次生成一个新数字那样随机。 - jd.
4
“as random”是一个相当模糊的术语。我认为这将是一种不同类型的随机性。我认为这将是均匀的。 - Andy Hayden
@hayden 是的,我认为使用这种方法会在时间范围内获得更加统一的分布,而不是将值限制在上一个生成的值。但是,这样做会牺牲效率。 - Silas Ray
1
这个问题存在歧义,不清楚Fiona是想要一个生成器还是想要生成数字。鉴于她似乎只是想要即时生成数字,这就是我的答案。 - Lewis Norton
实际上,目前这个问题过于模糊,无法提供有意义的答案。 ;) - Silas Ray
显示剩余6条评论

6
需要注意几点。以X>0开始,并在每个步骤中从(0,X)中选择一个随机数并将X替换为它的算法是不好的。为什么?因为(假设random行为正常)每个步骤的期望值都在区间(0,X)的中间。这意味着这些数字的序列预计会尽快收敛于0,即(1/2)^N。事实上,可以很容易地看到大多数数字都接近0,即使对于极大的初始值也是如此。这意味着这些数字的分布不均匀,这通常是所需的属性。
即使生成第N个数字的复杂度为O(N),而且(更重要的是)内存使用量为O(1),这仍然是一个主要缺点。
另一种解决方案是只取N个随机数并对它们进行排序。虽然这不错,但该算法的复杂度为O(N log(N))(或者与底层排序算法的复杂度相同),如果我们按顺序放置元素而不是排序,则可以将其降低到O(N),但内存使用量为O(N)——我们必须记住所有元素。然而,这些数字将均匀分布,这是一个巨大的优势!根据Jon Louis Bentley在论文“生成排序的随机数列表”中的想法,以下算法可能是最优的(至少在我所知道的范围内),并且产生均匀分布的数字:
import math
import random

def generate( min = 0, max = 10, number = 100 ):
    start = 0
    for i in xrange( number, 0, -1 ):
        start = start + math.log( random.random( ) ) / i
        next = math.exp( start ) * ( max - min ) + min
        yield next

for number in generate( ):
    print number

请注意,该算法的复杂度仍为O(N)(我怀疑无法更低),但内存使用量为O(1),这些数字均匀分布在区间(min,max)中,这一点并不明显,但却是真实的。唯一的缺点是我们必须知道要生成多少个数字才能开始。
另外,请查看以下帖子: 不使用排序生成排序的随机整数? O(n) 可能会有用。

谢谢,你的代码运行得非常好,但是有没有办法只生成整数而不是浮点数以避免重复? - Fiona Kwok
@FionaKwok 嗯,你可以始终产生next的整数部分,并且仅在它与先前产生的next值不同时这样做。但是,您将失去对要获取多少个整数的控制,但由于分布仍然是均匀的,因此您肯定会得到一些。因此,如果您想对所有内容进行控制,则可能生成随机值并将其排序是实现目标的方法。 - freakish

2

像这样:

from random import random
min=0
max=10
oldval=1.

while True:
  oldval=oldval*random()
  randval=min+(max-min)*oldval

只要您想生成浮点数值,或者不介意在整数中重复,并且不关心控制输出数量,这就非常稳定。 - Silas Ray
这个解决方案存在相同的问题:它收敛得非常快,但分布根本不均匀。 - freakish
肯定的;但它没有说需要使用均匀分布。 - umläute

2

以下是几种替代方案。这将产生接近卡方分布的值,后面选取的值范围比前面的值范围小:

import random
random_range = range(10) 
numbers = [random.choice(random_range[:i]) for i in range(10, 0, -1)]

这也可以通过浮动来实现:
import random
max = 10.0
min = 0.0
desired = 100
step = (max - min) / desired
numbers = [random.random() * (max - (i * step)) for i in range(desired)]

另一种方法是从一个递减的滑动窗口中选择随机值,这样可以提供均匀分布。

import random, numpy
max = 10.0
min = 0.0
desired = 100
step = float(min - max) / desired
window = 1.0
numbers = [x + (random.random() * window) - (window / 2.0) for x in numpy.arange(max, min, step)]

如果需要一个单调递减的数字列表,则设置 window <= step 即可实现。祝好运!


-1:完全没有下降。例如,它可能以“2, 5”开头。 - freakish
4
阅读原帖。它没有说要生成一列递减的数字,而是要在一个递减的范围内生成数字。 - brentlance
好的,我会撤销-1,但我真的怀疑这是否是OP想要的。 - freakish
2
也许不是。但问题提出者没有明确他们在寻找什么。 - brentlance

1

在 @brentlance 的想法基础上,这个方法适用于任何整数范围,包括正数、负数或两者都有:

import random

random_decreasing_integers_from_range = (i for i in xrange(max, min - 1, -1) if random.random() > .5)

如果您想要能够指定输出数量,这里有一个尝试,至少试图保持范围内的分布相对均匀:

import random

def random_decreasing_integers_from_range(min, max, num_outputs):
    range_size = abs(max - min)
    if range_size < num_outputs:
        raise ValueError('Distance from min to max must be equal to or greater than num_outputs.')
    output_count = 0
    for iteration, value in enumerate(xrange(max, min - 1, -1)):
        # if we only have enough values left to satisfy the number requested,
        # yield value
        if num_outputs - output_count == range_size - iteration + 1:
            output_count += 1
            yield value
        # else yield value randomly, weighted by how far in to the range we are
        # and how many values we have left to yield of the total requested
        else:
            ratio_consumed = float(iteration + 1) / range_size
            ratio_yielded = float(output_count) / num_outputs
            if random.random() < (1 - ratio_yielded) * ratio_consumed:
                output_count += 1
                yield value
        # if we've yielded the requested number of values, stop
        if output_count == num_outputs:
            break

这个方法在很大程度上是有效的,但当 num_outputs 不在 range_size 的约 10% 到 25% 之间时,它似乎会出现问题。在下限处,偏向于范围中间确实会显现出来,而在上限处,短路条件开始导致结果真正偏向于范围的下端。


我之前一直在编写一个使用itertools的更冗长版本,但我将其简化为这个版本并忘记移除import了。现已移除。 - Silas Ray

1
谢谢大家的回答。但是我已经找到了解决自己问题的方法,我觉得这个方法很简单,我想和大家分享一下。
import random
i = 1000000  

while i > 0:
    i = random.randint(0, i)
    print i

0

这将为您提供在0..1范围内递减的随机数。

import random
def generate():
    n = 1.0
    while n:
        n = random.random() * n
        yield n
iterator = generate()
iterator.next()

请注意,由于浮点数的有限精度,该函数在一段时间后停止产生数字,因为这些数字最终会达到0。

0

虽然我不是Python专家,但这是我的基本想法:

a=10000
for i in range(1,50):
   b=random.randint(1,a)
   print(b)
   a=b

注意该解决方案的一个很大的弱点:由于在每个停靠点您缩短了范围,因此它会收敛到1(并且实际上非常快地呈指数级下降)。因此,分布根本不均匀,对于足够大的范围(1000?),大多数元素将简单地为1。 - freakish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接