基于单个随机整数的Python中列表的随机连续切片

时间:2015-03-25 18:32:53

标签: python python-2.7 math random

使用单个随机数和列表,您将如何返回该列表的随机切片?

例如,给定列表[0,1,2],有七个随机连续切片的可能性:

  1. [ ]
  2. [ 0 ]
  3. [ 0, 1 ]
  4. [ 0, 1, 2 ]
  5. [ 1 ]
  6. [ 1, 2]
  7. [ 2 ]
  8. 不是获取随机起始索引和随机结束索引,必须有一种方法生成单个随机数并使用该值来计算起始索引和结束/长度。

    我需要这样,以确保这7种可能性具有相同的概率。

4 个答案:

答案 0 :(得分:3)

只需修复一个顺序,在其中对所有可能的切片进行排序,然后找出一种方法将所有切片的列表中的索引转回切片端点。例如,您使用的订单可以通过

来描述
  • 空切片在所有其他切片之前
  • 非空切片按起点排序
  • 具有相同起点的切片按其终点
  • 排序

因此索引0应返回空列表。索引1n应返回[0:1][0:n]。指数n+1n+(n-1)=2n-1将为[1:2][1:n]; 2nn+(n-1)+(n-2)=3n-3[2:3][2:n],依此类推。您在此处看到一种模式:给定起点的最后一个索引的格式为n+(n-1)+(n-2)+(n-3)+…+(n-k),其中k是序列的起始索引。这是arithmetic series,因此总和为(k+1)(2n-k)/2=(2n+(2n-1)k-k²)/2。如果您将该术语设置为等于给定索引,并将solve that设置为k,则会得到一些涉及平方根的公式。然后,您可以使用ceiling函数将其转换为k的整数值,该值对应于该起始点的最后一个索引。一旦你知道k,计算终点就相当容易了。

但上面解决方案中的二次方程式使得事情变得非常丑陋。所以你最好还是使用其他订单。现在我想不出一种可以避免这种二次项的方法。道格拉斯在his answer中使用的顺序并没有避免平方根,但至少他的平方根有点简单,因为他先按终点排序。您的问题和我的答案中的顺序称为lexicographical order,他的名称为reverse lexicographical,并且通常更容易处理,因为它不依赖于n。但由于大多数人首先考虑正常(前向)词典顺序,因此对于许多人来说,这个答案可能更直观,甚至可能是某些应用程序所需的方法。

以下是一些Python代码,它按顺序列出了所有序列元素,并按照上述方式从索引i到端点[k:m]进行转换:

from math import ceil, sqrt
n = 3
print("{:3} []".format(0))
for i in range(1, n*(n+1)//2 + 1):
    b = 1 - 2*n
    c = 2*(i - n) - 1
    # solve k^2 + b*k + c = 0
    k = int(ceil((- b - sqrt(b*b - 4*c))/2.))
    m = k + i - k*(2*n-k+1)//2
    print("{:3} [{}:{}]".format(i, k, m))

- 1中的c字词并非来自我上面提到的数学公式。这更像是从i的每个值中减去0.5。这样可以确保即使sqrt的结果略微过大,也不会导致k过大。因此,该术语会导致数字不精确,并且应该使整个事情变得非常强大。

术语k*(2*n-k+1)//2是属于起点k-1的最后一个索引,因此i减去该术语是所考虑的子序列的长度。

您可以进一步简化事情。您可以在循环外执行一些计算,如果必须重复选择随机序列,这可能很重要。您可以将b除以因子2,然后在其他许多地方除去该因子。结果可能如下所示:

from math import ceil, sqrt
n = 3
b = n - 0.5
bbc = b*b + 2*n + 1
print("{:3} []".format(0))
for i in range(1, n*(n+1)//2 + 1):
    k = int(ceil(b - sqrt(bbc - 2*i)))
    m = k + i - k*(2*n-k+1)//2
    print("{:3} [{}:{}]".format(i, k, m))

答案 1 :(得分:2)

将空列表与其他列表相等重量有点奇怪。如果列表中有n个元素,则为空列表赋予权重0或n + 1倍更自然。但如果你想让它具有相同的重量,你可以做到这一点。

有n *(n + 1)/ 2个非空的连续子列表。您可以通过结束点(从0到n-1)和起点(从0到端点)指定它们。

生成从0到n *(n + 1)/ 2的随机整数x。

如果x = 0,则返回空列表。否则,x从1到n(n + 1)/ 2不整齐地分布。

计算e =楼层(sqrt(2 * x)-1/2)。这取值0,1,1,2,2,2,3,3,3,3等。

计算s =(x-1) - e *(e + 1)/ 2。这取值0,0,1,0,1,2,0,1,2,3 ......

返回从索引s开始到索引e结束的间隔。

(s,e)取值(0,0),(0,1),(1,1),(0,2),(1,2),(2,2),......

import random
import math

n=10

x = random.randint(0,n*(n+1)/2)

if (x==0):
    print(range(n)[0:0]) // empty set
    exit()

e = int(math.floor(math.sqrt(2*x)-0.5))
s = int(x-1 - (e*(e+1)/2))

print(range(n)[s:e+1]) // starting at s, ending at e, inclusive

答案 2 :(得分:1)

首先创建所有可能的切片索引。

[0:0][1:1]等等,所以我们只包含其中一个。

最后,你选择一个随机索引对,并应用它。

import random

l = [0, 1, 2]

combination_couples = [(0, 0)]
length = len(l)

# Creates all index couples.
for j in range(1, length+1):
    for i in range(j):
        combination_couples.append((i, j))

print(combination_couples)

rand_tuple = random.sample(combination_couples, 1)[0]
final_slice = l[rand_tuple[0]:rand_tuple[1]]

print(final_slice) 

为了确保我们得到所有这些:

for i in combination_couples:
    print(l[i[0]:i[1]])

或者,通过一些数学......

对于长度为3的列表,有0到3个可能的索引号,即n = 4。你有2个,即k = 2。第一个索引必须小于秒,因此我们需要计算组合as described here

from math import factorial as f    

def total_combinations(n, k=2):
    result = 1

    for i in range(1, k+1):
        result *= n - k + i
    result /= f(k)
    # We add plus 1 since we included [0:0] as well.
    return result + 1

print(total_combinations(n=4))    # Prints 7 as expected.

答案 3 :(得分:0)

  

必须有一种生成单个随机数的方法,并使用该值来计算起始索引和结束/长度。

很难说哪种方法最好,但如果您只想将单个随机数绑定到连续切片,则可以使用模数。

给定一个列表l和一个随机的nubmer r,您可以获得这样的连续切片:

l[r % len(l) : some_sparkling_transformation(r) % len(l)]

some_sparkling_transformation(r)至关重要。它取决于您的需求,但由于我在您的问题中没有看到任何特殊要求,例如:

l[r % len(l) : (2 * r) % len(l)]

这里最重要的是切片的左右边缘都与r相关联。这使得定义不遵循任何可观察模式的这种连续切片成为问题。上面的示例(使用2 * r)生成的切片始终为空列表或遵循[a : 2 * a]模式。

让我们用一些直觉。我们知道我们希望以连续切片的形式找到数字r的良好随机表示。我们需要找到两个数字:ab,它们分别是切片的左右边缘。假设r是一个好的随机数(我们在某种程度上喜欢它),我们可以说a = r % len(l)是一个很好的方法。

现在让我们尝试找b。生成另一个漂亮随机数的最佳方法是使用支持播种(两者都有)的随机数生成器(randomnumpy)。 random模块示例:

import random
def contiguous_slice(l, r):
    random.seed(r)
    a = int(random.uniform(0, len(l)+1))
    b = int(random.uniform(0, len(l)+1))
    a, b = sorted([a, b])
    return l[a:b]

祝你好运,玩得开心!