例如,给定列表
[0,1,2]
,有 七个 可能的连续随机片段:
[ ]
[ 0 ]
[ 0, 1 ]
[ 0, 1, 2 ]
[ 1 ]
[ 1, 2]
[ 2 ]
我需要这样做,以确保这7种可能性具有相等的概率。
[0,1,2]
,有 七个 可能的连续随机片段:
[ ]
[ 0 ]
[ 0, 1 ]
[ 0, 1, 2 ]
[ 1 ]
[ 1, 2]
[ 2 ]
只需确定一种排序所有可能切片的顺序,然后找出一种将该列表中的索引转换回切片端点的方法。例如,您使用的顺序可以描述为:
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
是序列的起始索引。那是一个算术级数,因此该总和为(k + 1)(2n-k)/ 2 =(2n +(2n-1)k-k²)/ 2
。如果将该项设为给定索引,并求解k
,则会得到涉及平方根的某个公式。然后,您可以使用天花板函数将其转换为与该起始点的最后一个索引相对应的整数值k
。一旦您知道了k
,计算终点就很容易了。n
。但由于大多数人首先考虑正常(前向)字典序,因此这个答案可能对许多人更直观,甚至可能是一些应用程序所需的方式。[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))
c
中的 -1
项不是来自我上面介绍的数学公式。它更像是从每个 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))
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
s==e
时,您的代码会重复生成空切片。 - MvG[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。第一个索引必须小于第二个索引,因此我们需要计算组合数如此描述。
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.
l
和单个随机数字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]
祝你好运并玩得开心!
a
的切片,并且还会过度呈现空切片。 - MvGa
(我想是吧?),我同意空切片会被过度表示。这里需要稍微不同的方法... - Konrad Talik
list_slice[random.randint(0,len(list)) :]
- letsc