只是想知道这类问题是否存在更好的解决方案。
我们知道,对于偶数的X / Y百分比分割,我们可以得到数据的精确分割 - 例如对于数据大小10:
10 * .6 = 6
10 * .4 = 4
10
以这种方式拆分数据很容易,我们可以保证我们拥有所有数据而且不会丢失任何数据。然而,我在努力的地方是不太友好的数字 - 拿11
11 * .6 = 6.6
11 * .4 = 4.4
11
但是我们不能在i = 6.6
索引到一个数组。所以我们必须决定如何做到这一点。如果我们取JUST整数部分,我们会丢失1个数据点 -
First set = 0..6
Second set = 6..10
如果我们对数字进行处理,情况也是如此。
但是,如果我们采用数字的上限:
First set = 0..7
Second set = 7..12
我们已经阅读了数组的末尾。
当我们投掷第3或第4次分裂(例如30,30,20,20)时,这会变得更糟。
这些问题是否有标准的分裂程序?数据丢失是否被接受?似乎数据丢失对于依赖数据(例如时间序列)是不可接受的。
谢谢!
编辑:我选择值.6
和.4
。它们可以是任何两个与1
相加的数字。
首先,请注意您的问题不仅限于您声称的奇数大小的数组,而是任何大小的数组。你将如何对10个元素阵列进行56%-44%的分割?或者是4元素阵列的60%-40%分裂?
没有标准程序。在许多情况下,程序员并不关心精确的分割,他们要么通过地板或四舍五入(第一组的大小),而另一个(阵列长度 - 圆形大小)(另一个)(大小的第二个)。
在大多数情况下,这可能是一次性计算,并且不需要准确性。你必须问自己你的要求是什么。例如:您是否正在使用数千个10个大小的数组,每次分割它们时,56%-44%进行一些计算并返回结果?你必须问自己你想要什么准确性。您是否关心您的结果是60%-40%的分割还是50%-50%的分割?
另一个例子假设您正在进行25%-25%-25%-25%的4路相等分割。如果您有10个元素并且应用了舍入技术,则最终会得到3,3,3,1个元素。当然这会弄乱你的结果。
如果您确实关心所有这些不准确性,那么第一步是考虑是否可以调整阵列大小和/或分流比率。
如果这些都是一成不变的,那么对任何大小的阵列的任何比率进行精确分割的唯一方法就是使其具有概率性。您必须拆分多个数组才能工作(这意味着您必须多次将相同的拆分比率应用于相同大小的数组)。数组越多越好(或者您可以多次使用相同的数组)。
所以想象一下,你需要对10个大小的阵列进行56%-44%的分割。这意味着您需要将其拆分为5.6个元素和4.4个元素。
有很多方法可以达到5.6平均值。最简单的一个(和尝试序列中方差最小的那个)是60%的时间是一组有6个元素,40%的时间是有5个元素的集合。
0.6*6 + 0.4*5 = 5.6
在代码方面,您可以通过以下方式决定每次的大小:
import random
array_size = 10
first_split = 0.56
avg_split_size = array_size * first_split
floored_split_size = int(avg_split_size)
if avg_split_size > floored_split_size:
if random.uniform(0,1) > avg_split_size - floored_split_size:
this_split_size = floored_split_size
else:
this_split_size = floored_split_size + 1
else:
this_split_size = avg_split_size
你可以让代码更紧凑,我只是在这里做了一个大纲让你明白了。我希望这有帮助。
而不是使用ciel()
或floor()
而不是使用round()
。例如:
>>> round(6.6)
7.0
返回的值将是float
类型。要获取整数值,请将其类型转换为int
:
>>> int(round(6.6))
7
这将是您第一次拆分的价值。要获得第二次拆分,请使用len(data) - split1_val
计算。这适用于2分裂问题。
在3分割的情况下,取两个分割的圆值并取第3个分割的值作为len(my_list) - val_split_1 - val_split2
的值
以通用方式,For N split:
取
round()
值N-1
分裂。对于最后一个值,请执行len(data)
- “N round()值的值”。
其中len()
给出了列表的长度。
我们首先考虑将该组分成两部分。
让n
成为我们分裂的元素数量,p
和q
是比例,所以
p+q == 1
我断言小数点后面的部分总是总和为1
或0
,所以我们应该在一个上使用floor
而在另一个上使用ceil
,我们将永远是正确的。
这是一个功能,它与测试一起执行。我把打印声明留在了,但是它们被注释掉了。
def simpleSplitN(n, p, q):
"split n into proportions p and q and return indices"
np = math.ceil(n*p)
nq = math.floor(n*q)
#print n, sum([np, nq]) #np and nq are the proportions
return [0, np] #these are the indices we would use
#test for simpleSplitN
for i in range(1, 10):
p = i/10.0;
q = 1-p
simpleSplitN(37, p, q);
对于数学倾向,这里证明小数比例将总和为1
-----------------------
我们可以将p*n
表达为n/(1/p)
,因此通过除法算法得到整数k
和r
n == k*(1/p) + r
与0 <= r < (1/p)
因此r/(1/p) == p*r < 1
我们可以为q
做同样的事情
q*r < 1
(这是一个不同的r)
值得注意的是,当我们划分q*r
时,p*r
和n
是小数点后面的部分。
现在我们可以将它们一起添加(我们现在添加了下标)
0 <= p*(r_1) < 1
0 <= q*(r_2) < 1
=> 0 < p*r + q*r == p*n + q*n + k_1 + k_2 == n + k_1 + k_2 < 2
但是通过关闭整数,n + k_1 + k_2
是一个整数等等
0 < n + k_1 + k_2 < 2
意味着p*r + q*r
必须是0
或1
。在我们的0
被均分的情况下,它只会是n
。
否则,我们现在可以看到我们的小数部分总是与1
相加。
-----------------------
我们可以做一个非常相似(但稍微复杂一点)的证据,将n
分成任意数字(比如N
)部分,但是它们总和为1
而不是它们总和为N
。
这是一般功能,它有用于验证目的的未注释的打印语句。
import math
import random
def splitN(n, c):
"""Compute indices that can be used to split
a dataset of n items into a list of proportions c
by first dividing them naively and then distributing
the decimal parts of said division randomly
"""
nc = [n*i for i in c];
nr = [n*i - int(n*i) for i in c] #the decimal parts
N = int(round(sum(nr))) #sum of all decimal parts
print N, nc
for i in range(0, len(nc)):
nc[i] = math.floor(nc[i])
for i in range(N): #randomly distribute leftovers
nc[random.randint(1, len(nc)) - 1] += 1
print n,sum(nc); #nc now contains the proportions
out = [0] #compute a cumulative sum
for i in range(0, len(nc) - 1):
out.append(out[-1] + nc[i])
print out
return out
#test for splitN with various proportions
c = [.1,.2,.3,.4]
c = [.2,.2,.2,.2,.2]
c = [.3, .2, .2, .3]
for n in range( 10, 40 ):
print splitN(n, c)
如果我们有剩菜,我们将永远不会分开,所以我们随机分发,如@Thanassis说。如果您不喜欢对random
的依赖,那么您可以在开头或偶数间隔添加它们。
我的两个函数都输出索引,但它们计算比例,因此可以稍微修改以根据用户偏好输出这些索引。