Betrachten wir zuerst das Aufteilen des Sets in zwei Teile.
Lassen n
die Anzahl der Elemente, die wir Spaltung sind, und p
und q
die Proportionen, so dass
p+q == 1
ich behaupten, dass die Teile nach dem Komma wird immer entweder Summe 1
oder 0
, so sollten wir floor
auf einem und ceil
auf dem anderen verwenden, und wir werden immer Recht haben.
Hier ist eine Funktion, die das zusammen mit einem Test tut. Ich habe die Print-Statements bei mir gelassen, aber sie sind auskommentiert.
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);
Für die mathematisch geneigt, hier ist der Beweis, dass die Dezimalzahl Proportionen 1
-----------------------
Summe werden wir p*n
als n/(1/p)
zum Ausdruck bringen können, und so durch den Divisionsalgorithmus erhalten wir ganze Zahlen k
und r
n == k*(1/p) + r
mit 0 <= r < (1/p)
So r/(1/p) == p*r < 1
Wir genau für q
das gleiche tun, bekommen
q*r < 1
(das ist eine andere r)
Es ist wichtig zu beachten, dass q*r
und p*r
sind die Teil nach die Dezimalzahl, wenn wir unsere n
teilen.
Jetzt können wir sie zusammen addieren
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
Aber durch Schließung der ganzen Zahlen (wir haben jetzt Indizes hinzugefügt), n + k_1 + k_2
eine ganze Zahl ist und so
0 < n + k_1 + k_2 < 2
bedeutet, dass p*r + q*r
muss sei entweder 0
oder 1
. Es wird nur 0
für den Fall sein, dass unser n
gleichmäßig geteilt wird.
Sonst können wir jetzt sehen, dass unsere Nachkommastellen immer zu 1
summieren werden.
-----------------------
Wir haben einen sehr ähnlichen tun können (aber komplizierter leicht) Beweis n
in eine beliebige Anzahl (etwa N
) Teile zur Aufspaltung, sondern von ihnen 1
Summieren, werden sie auf eine ganze Zahl Summe weniger als N
.
Hier ist die allgemeine Funktion, es hat unkommentierte Druckanweisungen für Verifikationszwecke.
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)
Wenn wir Reste haben, werden wir nie eine noch Split bekommen, so dass wir verteilen sie nach dem Zufall, wie gesagt @Thanassis. Wenn Sie die Abhängigkeit von random
nicht mögen, dann können Sie sie einfach am Anfang oder in gleichen Intervallen hinzufügen.
Beide meiner Funktionen geben Indizes aus, aber sie berechnen Proportionen und können daher leicht geändert werden, um diese stattdessen pro Benutzerpräferenz auszugeben.
@anonymous im Fall von zwei Splits, ja, Sie könnten Boden und nehmen Sie den Rest als der zweite Satz. In einem Fall, wo wir 3 Splits haben, können Sie das nicht so einfach tun. – rec
Im Falle von 3 Split, nehmen Sie den Rundenwert von zwei Split und nehmen Sie den Wert von 3rd Split als Wert von len (my_list) - val_split_1 - val_split2 wobei len() gibt die Länge der Liste. Hinzugefügte Antwort –