2012-07-12 16 views
19

bu kod bloğunun önce Tanımlı:Bölme yakın-eşit parçaya (Scala, ancak dilin agnostik)

  • dataset bir Vector veya List
  • numberOfSlices bir Int olduğunu olabilir veri kümesini kesmek için kaç "kez" ifade eden

Veri kümesini numberOfSlices dilimler halinde bölmek, mümkün olduğunca eşit dağıtmak istiyorum. "Bölünmüş" ile kastediyorum sanırım "bölüm" demek (kesişim boş olmalı, her şey birliği orijinal olmalıdır) set teori terimini kullanmak için, ancak bu zorunlu olarak bir dizi, sadece keyfi bir koleksiyon değildir.

örn.

dataset = List(1, 2, 3, 4, 5, 6, 7) 
numberOfSlices = 3 
slices == ListBuffer(Vector(1, 2), Vector(3, 4), Vector(5, 6, 7)) 

Bunu, sahip olduğumdan daha iyi bir yolu var mı? (ki bu bile emin değilim ki en iyisi ...) Veya belki de bu, algoritmik olarak uygulanabilir bir çaba değil, bu durumda bilinen herhangi bir iyi sezgisel mi?

val slices = new ListBuffer[Vector[Int]] 
val stepSize = dataset.length/numberOfSlices 
var currentStep = 0 
var looper = 0 
while (looper != numberOfSlices) { 
    if (looper != numberOfSlices - 1) { 
    slices += dataset.slice(currentStep, currentStep + stepSize) 
    currentStep += stepSize 
    } else { 
    slices += dataset.slice(currentStep, dataset.length) 
    } 
    looper += 1 
} 
+2

"Nasıl mümkün olduğunca eşit dağıtılır" yorumlanacağından emin değilim. Kodunuza göre, 'Seq: grouped (Int) 'zaten istediğinizi yapar, ancak dilim boyutunun üzerine çıkmaz. – Kaito

+3

"Gruplandırılmış", "x" gruplarına bölünürken, "x" gruplarına ayırmak istiyorum. Cevapta denedim, 'List (1, 2, 3, 4, 5) .gruplu (2) .toList' Listesini veriyor (Liste (1, 2), Liste (3, 4), Liste (5)) '' Ben gibi bir şey istiyorum 'List (Liste (1, 2), Liste (3, 4, 5)) '. – adelbertc

cevap

12

, tam olarak ne istediğinizi tanımlamak oldukça kolaydır. bölüm küçük parçalara boyutudur ve kalan büyük parça sayısı ise:

def cut[A](xs: Seq[A], n: Int) = { 
    val (quot, rem) = (xs.size/n, xs.size % n) 
    val (smaller, bigger) = xs.splitAt(xs.size - rem * (quot + 1)) 
    smaller.grouped(quot) ++ bigger.grouped(quot + 1) 
} 
+1

Bu iyi ama maalesef, 'büyük' ​​parçaların tümü geldiği için, 'mümkün olduğunca eşit dağıtılmış' gereksinim duyulan gereksinimi uzatır - örneğin, 'kes (1 - 15, 10) .toList.map (_. Size) '5 tane tek elemanlı segmenti ve ardından 5 tane iki eleman segmenti verir. –

0

Kaito'dan bahsedildiği gibi grouped tam olarak aradığınız şeydir. Ama sadece böyle bir yöntemi nasıl uygulayacağını bilmek istiyorsanız, birçok yol var ;-). Örneğin bu gibi yapabileceği: xs.grouped(xs.size/n) davranışı sizin için işe yaramazsa

def grouped[A](xs: List[A], size: Int) = { 
    def grouped[A](xs: List[A], size: Int, result: List[List[A]]): List[List[A]] = { 
    if(xs.isEmpty) { 
     result 
    } else { 
     val (slice, rest) = xs.splitAt(size) 
     grouped(rest, size, result :+ slice) 
    } 
    } 
    grouped(xs, size, Nil) 
} 
+0

'grouped' boyutlarını mümkün olduğunca yapmaz, son alt liste diğerlerinden çok daha küçük olabilir. – dividebyzero

0

ben bu şekilde yaklaşmak istiyorum: n element ve m bölümleri (n> m) Verilen ya n mod m == 0 bu durumda, her bir bölüm n/m öğelerine veya n mod m = y'ye sahip olacak, bu durumda her bir bölüm n/m öğelerine sahip olacak ve 'u m üzerinden dağıtmanız gerekir.

n/m+1 öğeleri ve (m-y) yuvaları n/m olan y yuvalarına sahip olacaksınız. Onları nasıl dağıttığın seçim senin.

6

tipik "optimum" bölümü kestikten sonra tam bir fraksiyonel uzunluğu hesaplar ve sonra almaya gerçek sayısını bulmak için yuvarlar: genellikle daha olan

def cut[A](xs: Seq[A], n: Int):Vector[Seq[A]] = { 
    val m = xs.length 
    val targets = (0 to n).map{x => math.round((x.toDouble*m)/n).toInt} 
    def snip(xs: Seq[A], ns: Seq[Int], got: Vector[Seq[A]]): Vector[Seq[A]] = { 
    if (ns.length<2) got 
    else { 
     val (i,j) = (ns.head, ns.tail.head) 
     snip(xs.drop(j-i), ns.tail, got :+ xs.take(j-i)) 
    } 
    } 
    snip(xs, targets, Vector.empty) 
} 

Bu yolu olan uzun ve kısa bloklar olabiliyor düzlük arzu:

hatta unsurlara sahip daha fazla kez kesebilir
scala> cut(List(1,2,3,4,5,6,7,8,9,10),4) 
res5: Vector[Seq[Int]] = 
    Vector(List(1, 2, 3), List(4, 5), List(6, 7, 8), List(9, 10)) 

:

scala> cut(List(1,2,3),5) 
res6: Vector[Seq[Int]] = 
    Vector(List(1), List(), List(2), List(), List(3)) 
2

İşte, bir Stream döndüren özyinelemeli bir işlevin tanıdık Scala numarasını kullanarak, işi yapan bir tek liner. Parça boyutlarını yuvarlamak için (x+k/2)/k kullanımının dikkatini çekin; son listedeki daha küçük ve daha büyük parçaları birleştirerek, tümü en fazla bir fark unsuru olan boyutlarda kullanın.Bunun yerine (x+k-1)/k ile yuvarlarsanız, küçük blokları sonuna kadar taşırsınız ve x/k bunları en başa taşır.

def k_folds(k: Int, vv: Seq[Int]): Stream[Seq[Int]] = 
    if (k > 1) 
     vv.take((vv.size+k/2)/k) +: k_folds(k-1, vv.drop((vv.size+k/2)/k)) 
    else 
     Stream(vv) 

Demo:

scala> val indices = scala.util.Random.shuffle(1 to 39) 

scala> for (ff <- k_folds(7, indices)) println(ff) 
Vector(29, 8, 24, 14, 22, 2) 
Vector(28, 36, 27, 7, 25, 4) 
Vector(6, 26, 17, 13, 23) 
Vector(3, 35, 34, 9, 37, 32) 
Vector(33, 20, 31, 11, 16) 
Vector(19, 30, 21, 39, 5, 15) 
Vector(1, 38, 18, 10, 12) 

scala> for (ff <- k_folds(7, indices)) println(ff.size) 
6 
6 
5 
6 
5 
6 
5 

scala> for (ff <- indices.grouped((indices.size+7-1)/7)) println(ff) 
Vector(29, 8, 24, 14, 22, 2) 
Vector(28, 36, 27, 7, 25, 4) 
Vector(6, 26, 17, 13, 23, 3) 
Vector(35, 34, 9, 37, 32, 33) 
Vector(20, 31, 11, 16, 19, 30) 
Vector(21, 39, 5, 15, 1, 38) 
Vector(18, 10, 12) 

scala> for (ff <- indices.grouped((indices.size+7-1)/7)) println(ff.size) 
6 
6 
6 
6 
6 
6 
3 

grouped tüm alt listelerinin boyutunu bile dışarı çalışmaz nasıl dikkat edin.

+0

sembolü çözemezse '# ::' – Vasily802

+0

'sembolü çözemezse çalışmadığınız neden' ​​# :: 'Bilmiyorum, ama yerini ve ayrıca kod biraz iyileşti ve sabit olması Vasily802 @ – Vasily802

+1

folds' demo. Teşekkürler... – dividebyzero

İlgili konular