只是想知道这类问题是否存在更好的解决方案。
我们知道,对于偶数的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
的任意两个数字。
答案 0 :(得分: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
你可以让代码更紧凑,我只是在这里做了一个大纲让你明白了。我希望这有帮助。
答案 1 :(得分:0)
而不是使用ciel()
或floor()
代替使用round()
。例如:
>>> round(6.6)
7.0
返回的值为float
类型。要获取整数值,请将其类型转换为int
:
>>> int(round(6.6))
7
这将是您第一次拆分的价值。要获得第二次拆分,请使用len(data) - split1_val
进行计算。这适用于2分裂问题。
如果是 3 split ,请取两个 split 的舍入值,并将第3个split的值作为len(my_list) - val_split_1 - val_split2
的值
以通用方式,对于N分割:
获取
round()
分割的N-1
值。对于最后一个值,请执行len(data)
- “N round()值” 。
其中len()
给出列表的长度。
答案 2 :(得分:0)
让我们首先考虑将该组分成两部分。
让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
完成相同的工作,获取
q*r
(这是一个不同的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
。 <{1}}只有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
的依赖关系,那么您可以在开头或偶数间隔添加它们。
我的两个函数都输出索引,但是它们会计算比例,因此可以稍微修改以根据用户偏好输出这些索引。