2016-06-14 101 views
2

我使用scala Range.by來分割一個範圍來獲得一個數組,但它錯過了某個特殊的bucket num的最後一個,例如100.我感到困惑,並且演示如下:scala範圍split缺少最後一個

object SplitDemo extends App { 
    val min = 0.0 
    val max = 7672.142857142857 
    val bucketNum = 100 

    def splitsBucket1(min: Double, max: Double, num: Int) = (min to max by ((max - min)/num)).toArray 
    def splitsBucket2(min: Double, max: Double, num: Int): Array[Double] = { 
    val rst = Array.fill[Double](num + 1)(0) 
    rst(0) = min 
    rst(num) = max 

    val step = (max-min)/num 
    for(i <- 1 until num) rst(i) = rst(i-1)+step 

    rst 
    } 

    val split1 = splitsBucket1(min, max, bucketNum) 
    println(s"Split1 size = ${split1.size}, %s".format(split1.takeRight(4).mkString(","))) 

    val split2 = splitsBucket2(min, max, bucketNum) 
    println(s"Split2 size = ${split2.size}, %s".format(split2.takeRight(4).mkString(","))) 

} 

輸出以下

分割1大小= 100,7365.257142857143,7441.978571428572,7518.700000000001,7595.421428571429
Split2大小= 101,7441.978571428588,7518.700000000017,7595.421428571446,7672.142857142857

當num = 100時,split1 錯過了最後一個,但是split2沒有(這是我的期望)。當num是其他num時,e.t. 130,split1和split2得到樣本結果。
有什麼理由貶低這種差異?

+0

你試過調試嗎? –

回答

2

這是通常的floating point inaccuracy

看,max劃分和multiplicating回來後怎麼弄出來是不同的:

scala> 7672.142857142857/100 * 100 
res1: Double = 7672.142857142858 

,這個數字比max大,所以它不適合範圍:

scala> max/bucketNum * bucketNum > max 
res2: Boolean = true 

這還是比增加step 100百倍更準確:

scala> var result = 0.0 
result: Double = 0.0 

scala> for (_ <- 0 until 100) result += (max - min)/bucketNum 

scala> result 
res4: Double = 7672.142857142875 

這比maxmax/bucketNum * bucketNum都大。儘管您明確指定rst(num) = max,但您通過splitBuckets2避免了此情況。


你可以嘗試以下方法拆分實施:

def splitsBucket3(min: Double, max: Double, num: Int): Array[Double] = { 
    val step = (max - min)/num 
    Array.tabulate(num + 1)(min + step * _) 
} 

但保證有正確的元素數量,且具有比splitsBucket2少數值精度問題。

+0

首先感謝@Kolmar。 Hoever,splitsBucket1的最後結果是7595.421428571429,這是** max **的步距(約76.72),似乎不是準確性的問題。 – bourneli

+0

@bourneli最後*失蹤*元素。它缺少,因爲它大於'max'。嘗試通過((max - min)/ num))。toArray'運行'(min to(max + 1),你會看到它 – Kolmar

+0

明白了,謝謝@Kolmar – bourneli