使用单个随机数和列表,您将如何返回该列表的随机切片?
例如,给定列表[0,1,2]
,有七个随机连续切片的可能性:
[ ]
[ 0 ]
[ 0, 1 ]
[ 0, 1, 2 ]
[ 1 ]
[ 1, 2]
[ 2 ]
不是获取随机起始索引和随机结束索引,必须有一种方法生成单个随机数并使用该值来计算起始索引和结束/长度。
我需要这样,以确保这7种可能性具有相同的概率。
答案 0 :(得分:3)
只需修复一个顺序,在其中对所有可能的切片进行排序,然后找出一种方法将所有切片的列表中的索引转回切片端点。例如,您使用的订单可以通过
来描述因此索引0
应返回空列表。索引1
到n
应返回[0:1]
到[0:n]
。指数n+1
到n+(n-1)=2n-1
将为[1:2]
到[1:n]
; 2n
到n+(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
的良好随机表示。我们需要找到两个数字:a
和b
,它们分别是切片的左右边缘。假设r
是一个好的随机数(我们在某种程度上喜欢它),我们可以说a = r % len(l)
是一个很好的方法。
现在让我们尝试找b
。生成另一个漂亮随机数的最佳方法是使用支持播种(两者都有)的随机数生成器(random
或numpy
)。 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]
祝你好运,玩得开心!