อัลกอริทึมออนไลน์สำหรับค่าเบี่ยงเบนสัมบูรณ์เฉลี่ยและชุดข้อมูลขนาดใหญ่


16

ฉันมีปัญหาเล็กน้อยที่ทำให้ฉันประหลาดใจ ฉันต้องเขียนขั้นตอนสำหรับกระบวนการซื้อแบบออนไลน์ของอนุกรมเวลาหลายตัวแปร ในทุกช่วงเวลา (เช่น 1 วินาที) ฉันจะได้รับตัวอย่างใหม่ซึ่งโดยทั่วไปเป็นเวกเตอร์จุดลอยตัวของขนาด N การดำเนินการที่ฉันต้องทำค่อนข้างยุ่งยากเล็กน้อย:

  1. สำหรับตัวอย่างใหม่แต่ละอันฉันคำนวณเปอร์เซ็นต์สำหรับตัวอย่างนั้น (โดยการทำให้เวกเตอร์เป็นมาตรฐานเพื่อให้องค์ประกอบรวมเป็น 1)

  2. ฉันคำนวณเปอร์เซ็นต์เฉลี่ยของเวคเตอร์ในวิธีเดียวกัน แต่ใช้ค่าที่ผ่านมา

  3. สำหรับค่าในแต่ละครั้งที่ผ่านมาฉันคำนวณค่าเบี่ยงเบนสัมบูรณ์ของเปอร์เซ็นต์เวกเตอร์ที่เกี่ยวข้องกับตัวอย่างนั้นกับค่าเฉลี่ยเวกเตอร์เปอร์เซ็นต์ส่วนกลางที่คำนวณได้ในขั้นตอนที่ 2 ด้วยวิธีนี้ค่าเบี่ยงเบนสัมบูรณ์จะเป็นจำนวนเสมอระหว่าง 0 (เมื่อเวกเตอร์เท่ากับค่าเฉลี่ย เวกเตอร์) และ 2 (เมื่อแตกต่างกันโดยสิ้นเชิง)

  4. การใช้ค่าเฉลี่ยของการเบี่ยงเบนสำหรับตัวอย่างก่อนหน้านี้ทั้งหมดฉันคำนวณค่าเบี่ยงเบนสัมบูรณ์แบบเฉลี่ยซึ่งเป็นตัวเลขอีกครั้งระหว่าง 0 ถึง 2

  5. ฉันใช้การเบี่ยงเบนสัมบูรณ์แบบเฉลี่ยเพื่อตรวจสอบว่าตัวอย่างใหม่เข้ากันได้กับตัวอย่างอื่น (โดยการเปรียบเทียบการเบี่ยงเบนสัมบูรณ์กับการเบี่ยงเบนสัมบูรณ์แบบเฉลี่ยของทั้งชุดคำนวณในขั้นตอนที่ 4)

เนื่องจากทุกครั้งที่มีการเก็บตัวอย่างใหม่จะมีการเปลี่ยนแปลงค่าเฉลี่ยทั่วโลก (และดังนั้นค่าเบี่ยงเบนสัมบูรณ์ที่เปลี่ยนแปลงเช่นกัน) มีวิธีคำนวณค่านี้โดยไม่สแกนข้อมูลทั้งหมดที่ตั้งไว้หลายครั้งหรือไม่ (หนึ่งครั้งสำหรับการคำนวณเปอร์เซ็นต์เฉลี่ยทั่วโลกและหนึ่งครั้งสำหรับการรวบรวมค่าเบี่ยงเบนสัมบูรณ์) ตกลงฉันรู้ว่ามันง่ายมากที่จะคำนวณค่าเฉลี่ยทั่วโลกโดยไม่ต้องสแกนทั้งชุดเนื่องจากฉันต้องใช้เวกเตอร์ชั่วคราวเพื่อเก็บผลรวมของแต่ละมิติ แต่สิ่งที่เกี่ยวกับการเบี่ยงเบนสัมบูรณ์แบบเฉลี่ย การคำนวณมันรวมถึงabs()โอเปอเรเตอร์ดังนั้นฉันจำเป็นต้องเข้าถึงข้อมูลที่ผ่านมาทั้งหมด!

ขอบคุณสำหรับความช่วยเหลือของคุณ.

คำตอบ:


6

หากคุณสามารถยอมรับความไม่ถูกต้องบางอย่างปัญหานี้สามารถแก้ไขได้อย่างง่ายดายโดยการนับจำนวนbinning นั่นคือเลือกจำนวนที่มีขนาดใหญ่ (พูดM = 1,000 ) จากนั้นเริ่มต้นถังขยะจำนวนเต็มบางส่วนB i , jสำหรับi = 1 Mและj = 1 Nที่Nคือขนาดเวกเตอร์เป็นศูนย์ จากนั้นเมื่อคุณเห็นการสังเกตแบบลำดับที่kของเวกเตอร์เปอร์เซ็นต์, การเพิ่มB i , jหากองค์ประกอบที่jของเวกเตอร์นี้อยู่ระหว่าง(MM=1000Bi,ji=1Mj=1NNkBi,jjและ i / Mวนซ้ำไปตามองค์ประกอบ Nของเวกเตอร์ (ฉันสมมติว่าเวกเตอร์อินพุตของคุณไม่ใช่ค่าลบดังนั้นเมื่อคุณคำนวณ 'เปอร์เซ็นต์' ของคุณเวกเตอร์จะอยู่ในช่วง [ 0 , 1 ] )(i1)/Mi/MN[0,1]

คุณสามารถประมาณค่าเฉลี่ยของเวกเตอร์จากถังขยะและค่าเบี่ยงเบนสัมบูรณ์เฉลี่ย หลังจากการสังเกตเวกเตอร์เหล่านั้นองค์ประกอบที่jของค่าเฉลี่ยนั้นประมาณโดย ˉ X j = 1Kjและเจองค์ประกอบ TH ของส่วนเบี่ยงเบนสัมบูรณ์เฉลี่ยประมาณโดย1

X¯j=1Kii1/2MBi,j,
j
1Ki|Xj¯i1/2M|Bi,j

แก้ไข : นี่เป็นกรณีเฉพาะของแนวทางทั่วไปที่คุณกำลังสร้างการประมาณความหนาแน่นเชิงประจักษ์ สิ่งนี้สามารถทำได้ด้วยพหุนาม, เส้นโค้ง, ฯลฯ แต่วิธีการ binning เป็นวิธีที่ง่ายที่สุดในการอธิบายและดำเนินการ


ว้าวแนวทางที่น่าสนใจจริงๆ ฉันไม่รู้เกี่ยวกับสิ่งนั้นและฉันจะเก็บไว้ในใจ น่าเสียดายที่ในกรณีนี้มันใช้งานไม่ได้เนื่องจากฉันมีข้อกำหนดที่ จำกัด มากจากมุมมองของการใช้หน่วยความจำดังนั้น M ควรมีขนาดเล็กมากและฉันคิดว่าจะมีการสูญเสียความแม่นยำมากเกินไป
gianluca

@gianluca: ดูเหมือนว่าคุณมี 1. ข้อมูลจำนวนมาก 2. ทรัพยากรหน่วยความจำที่ จำกัด 3. ความต้องการความแม่นยำสูง ฉันสามารถดูว่าทำไมปัญหานี้ทำให้คุณตกใจ! บางทีตามที่ @kwak คุณสามารถคำนวณการแพร่กระจายอื่น ๆ ได้บ้าง: MAD, IQR, ส่วนเบี่ยงเบนมาตรฐาน ทุกคนมีแนวทางที่อาจช่วยแก้ปัญหาของคุณได้
shabbychef

gianluca:> ให้แนวคิดเชิงปริมาณเพิ่มเติมเกี่ยวกับขนาดของหน่วยความจำอาร์เรย์และความแม่นยำที่คุณต้องการ อาจเป็นไปได้ว่าคำถามของคุณจะได้รับคำตอบที่ดีที่สุด @ stackoverflow
user603

4

ฉันเคยใช้วิธีการต่อไปนี้ในอดีตเพื่อคำนวณการเบี่ยงเบนการอภัยโทษในระดับปานกลางอย่างมีประสิทธิภาพ (โปรดทราบว่านี่เป็นวิธีการเขียนโปรแกรมไม่ใช่นักสถิติดังนั้นจึงอาจมีเทคนิคที่ฉลาดเช่นshabbychefซึ่งอาจมีประสิทธิภาพมากกว่า)

คำเตือน:นี่ไม่ใช่อัลกอริทึมออนไลน์ มันต้องใช้O(n)หน่วยความจำ นอกจากนี้มันยังมีประสิทธิภาพของตัวพิมพ์ที่แย่ที่สุดO(n)สำหรับชุดข้อมูลเช่น[1, -2, 4, -8, 16, -32, ...](เช่นเดียวกับการคำนวณใหม่ทั้งหมด) [1]

อย่างไรก็ตามเนื่องจากมันยังทำงานได้ดีในการใช้งานหลายกรณีมันอาจคุ้มค่าที่จะโพสต์ที่นี่ ตัวอย่างเช่นในการคำนวณค่าเบี่ยงเบนสัมบูรณ์ของ 10,000 ตัวเลขสุ่มระหว่าง -100 ถึง 100 เมื่อแต่ละรายการมาถึงอัลกอริทึมของฉันใช้เวลาน้อยกว่าหนึ่งวินาทีในขณะที่การคำนวณใหม่ทั้งหมดใช้เวลามากกว่า 17 วินาที (บนเครื่องของฉันจะแตกต่างกันต่อเครื่องและ ตามข้อมูลอินพุต) คุณจำเป็นต้องบำรุงรักษาเวกเตอร์ทั้งหมดในหน่วยความจำอย่างไรก็ตามซึ่งอาจเป็นข้อ จำกัด สำหรับการใช้งานบางอย่าง โครงร่างของอัลกอริทึมเป็นดังนี้:

  1. แทนที่จะมีเวกเตอร์เดียวเพื่อเก็บการวัดที่ผ่านมาให้ใช้ลำดับความสำคัญที่เรียงลำดับสามลำดับ (คล้ายกับ min / max heap) รายการทั้งสามนี้แบ่งพาร์ติชันอินพุตเป็นสาม: รายการที่สูงกว่าค่าเฉลี่ยรายการที่น้อยกว่าค่าเฉลี่ยและรายการเท่ากับค่าเฉลี่ย
  2. (เกือบ) ทุกครั้งที่คุณเพิ่มรายการการเปลี่ยนแปลงค่าเฉลี่ยดังนั้นเราจึงจำเป็นต้องแบ่งพาร์ติชันใหม่ สิ่งสำคัญคือลักษณะที่จัดเรียงของพาร์ติชันซึ่งหมายความว่าแทนที่จะสแกนทุกรายการในรายการเพื่อแบ่งส่วนเราเพียงแค่อ่านรายการที่เรากำลังเคลื่อนไหว ในขณะที่ในกรณีที่เลวร้ายที่สุดนี้จะยังคงต้องO(n)ดำเนินการย้ายสำหรับกรณีการใช้งานจำนวนมากนี้ไม่เป็นเช่นนั้น
  3. ด้วยการทำบัญชีที่ชาญฉลาดเราสามารถตรวจสอบให้แน่ใจว่าค่าเบี่ยงเบนถูกคำนวณอย่างถูกต้องตลอดเวลาเมื่อแบ่งพาร์ติชันและเมื่อเพิ่มรายการใหม่

โค้ดตัวอย่างบางส่วนในไพ ธ อนอยู่ด้านล่าง โปรดทราบว่าจะอนุญาตให้เพิ่มรายการในรายการเท่านั้นไม่ได้ลบออก สามารถเพิ่มได้อย่างง่ายดาย แต่ในขณะที่ฉันเขียนสิ่งนี้ฉันไม่จำเป็นต้องใช้มัน แทนที่จะใช้คิวลำดับความสำคัญของตัวเองผมได้ใช้SortedListจากแดเนียล Stutzbach ยอดเยี่ยมของแพคเกจ BLISTซึ่งใช้B + ต้นไม้ s ภายใน

พิจารณารหัสนี้ได้รับใบอนุญาตภายใต้ใบอนุญาตเอ็มไอที มันไม่ได้รับการปรับปรุงหรือขัดอย่างมีนัยสำคัญ แต่ได้ทำงานให้ฉันในอดีต รุ่นใหม่จะสามารถใช้ได้ที่นี่ แจ้งให้เราทราบหากคุณมีข้อสงสัยหรือพบข้อบกพร่องใด ๆ

from blist import sortedlist
import operator

class deviance_list:
    def __init__(self):
        self.mean =  0.0
        self._old_mean = 0.0
        self._sum =  0L
        self._n =  0  #n items
        # items greater than the mean
        self._toplist =  sortedlist()
        # items less than the mean
        self._bottomlist = sortedlist(key = operator.neg)
        # Since all items in the "eq list" have the same value (self.mean) we don't need
        # to maintain an eq list, only a count
        self._eqlistlen = 0

        self._top_deviance =  0
        self._bottom_deviance =  0

    @property
    def absolute_deviance(self):
        return self._top_deviance + self._bottom_deviance

    def append(self,  n):
        # Update summary stats
        self._sum += n
        self._n +=  1
        self._old_mean =  self.mean
        self.mean =  self._sum /  float(self._n)

        # Move existing things around
        going_up = self.mean > self._old_mean
        self._rebalance(going_up)

        # Add new item to appropriate list
        if n >  self.mean:
            self._toplist.add(n)
            self._top_deviance +=  n -  self.mean
        elif n == self.mean: 
            self._eqlistlen += 1
        else:
            self._bottomlist.add(n)
            self._bottom_deviance += self.mean -  n


    def _move_eqs(self,  going_up):
        if going_up:
            self._bottomlist.update([self._old_mean] *  self._eqlistlen)
            self._bottom_deviance += (self.mean - self._old_mean) * self._eqlistlen
            self._eqlistlen = 0
        else:
            self._toplist.update([self._old_mean] *  self._eqlistlen)
            self._top_deviance += (self._old_mean - self.mean) * self._eqlistlen
            self._eqlistlen = 0


    def _rebalance(self, going_up):
        move_count,  eq_move_count = 0, 0
        if going_up:
            # increase the bottom deviance of the items already in the bottomlist
            if self.mean !=  self._old_mean:
                self._bottom_deviance += len(self._bottomlist) *  (self.mean -  self._old_mean)
                self._move_eqs(going_up)


            # transfer items from top to bottom (or eq) list, and change the deviances
            for n in iter(self._toplist):
                if n < self.mean:
                    self._top_deviance -= n -  self._old_mean
                    self._bottom_deviance += (self.mean -  n)
                    # we increment movecount and move them after the list
                    # has finished iterating so we don't modify the list during iteration
                    move_count +=  1
                elif n == self.mean:
                    self._top_deviance -= n -  self._old_mean
                    self._eqlistlen += 1
                    eq_move_count +=  1
                else:
                    break
            for _ in xrange(0,  move_count):
                self._bottomlist.add(self._toplist.pop(0))
            for _ in xrange(0,  eq_move_count):
                self._toplist.pop(0)

            # decrease the top deviance of the items remain in the toplist
            self._top_deviance -= len(self._toplist) *  (self.mean -  self._old_mean)
        else:
            if self.mean !=  self._old_mean:
                self._top_deviance += len(self._toplist) *  (self._old_mean -  self.mean)
                self._move_eqs(going_up)
            for n in iter(self._bottomlist): 
                if n > self.mean:
                    self._bottom_deviance -= self._old_mean -  n
                    self._top_deviance += n -  self.mean
                    move_count += 1
                elif n == self.mean:
                    self._bottom_deviance -= self._old_mean -  n
                    self._eqlistlen += 1
                    eq_move_count +=  1
                else:
                    break
            for _ in xrange(0,  move_count):
                    self._toplist.add(self._bottomlist.pop(0))
            for _ in xrange(0,  eq_move_count):
                self._bottomlist.pop(0)

            # decrease the bottom deviance of the items remain in the bottomlist
            self._bottom_deviance -= len(self._bottomlist) *  (self._old_mean -  self.mean)


if __name__ ==  "__main__":
    import random
    dv =  deviance_list()
    # Test against some random data,  and calculate result manually (nb. slowly) to ensure correctness
    rands = [random.randint(-100,  100) for _ in range(0,  1000)]
    ns = []
    for n in rands: 
        dv.append(n)
        ns.append(n)
        print("added:%4d,  mean:%3.2f,  oldmean:%3.2f,  mean ad:%3.2f" %
              (n, dv.mean,  dv._old_mean,  dv.absolute_deviance / dv.mean))
        assert sum(ns) == dv._sum,  "Sums not equal!"
        assert len(ns) == dv._n,  "Counts not equal!"
        m = sum(ns) / float(len(ns))
        assert m == dv.mean,  "Means not equal!"
        real_abs_dev = sum([abs(m - x) for x in ns])
        # Due to floating point imprecision, we check if the difference between the
        # two ways of calculating the asb. dev. is small rather than checking equality
        assert abs(real_abs_dev - dv.absolute_deviance) < 0.01, (
            "Absolute deviances not equal. Real:%.2f,  calc:%.2f" %  (real_abs_dev,  dv.absolute_deviance))

[1] หากอาการยังคงอยู่ให้ไปพบแพทย์


2
ฉันขาดอะไรบางอย่าง: ถ้าคุณต้อง "รักษาเวกเตอร์ทั้งหมดในหน่วยความจำ" สิ่งนี้มีคุณสมบัติอย่างไรในฐานะอัลกอริทึม "ออนไลน์"
whuber

@ ไม่มีเบอร์ไม่ขาดอะไรเลยฉันคิดว่ามันไม่ใช่อัลกอริทึมออนไลน์ มันต้องใช้O(n)หน่วยความจำและในกรณีที่เลวร้ายที่สุดใช้เวลา O (n) เวลาสำหรับการเพิ่มแต่ละรายการ ในข้อมูลที่กระจายแบบปกติ (และอาจเป็นการกระจายแบบอื่น ๆ ) มันทำงานได้อย่างมีประสิทธิภาพ
fmark

3

นอกจากนี้ยังมีวิธีการตามพารามิเตอร์ ไม่สนใจธรรมชาติเวกเตอร์ของข้อมูลของคุณและมองไปที่ระยะขอบมันพอเพียงในการแก้ปัญหา: ค้นหาอัลกอริทึมออนไลน์เพื่อคำนวณค่าเบี่ยงเบนสัมบูรณ์เฉลี่ยของสเกลาร์X. ถ้า (และนี่คือเรื่องใหญ่ 'ถ้า' ที่นี่) คุณคิดอย่างนั้นXตามการแจกแจงความน่าจะเป็นบางส่วนพร้อมพารามิเตอร์ที่ไม่รู้จักคุณสามารถประมาณค่าพารามิเตอร์โดยใช้อัลกอริทึมออนไลน์แล้วคำนวณค่าเบี่ยงเบนสัมบูรณ์แบบเฉลี่ยตามการแจกแจงแบบพารามิเตอร์ ตัวอย่างเช่นถ้าคุณคิดว่าX ได้รับการกระจาย (โดยประมาณ) ตามปกติคุณสามารถประมาณค่าเบี่ยงเบนมาตรฐานของมันเป็น sและค่าเบี่ยงเบนสัมบูรณ์เฉลี่ยจะถูกประมาณ s2/π(ดูการกระจายปกติครึ่งหนึ่ง )


นั่นเป็นแนวคิดที่น่าสนใจ คุณสามารถเสริมด้วยการตรวจหาค่าผิดปกติทางออนไลน์และใช้ค่าเหล่านั้นเพื่อปรับเปลี่ยนค่าประมาณตามที่คุณไป
whuber

คุณอาจใช้วิธีของ Welford ในการคำนวณค่าเบี่ยงเบนมาตรฐานทางออนไลน์ที่ฉันบันทึกไว้ในคำตอบที่สอง
fmark

1
อย่างไรก็ตามหนึ่งควรทราบว่าวิธีนี้อาจสูญเสียความแข็งแกร่งของการประมาณเช่น MAD ที่ชัดเจนซึ่งบางครั้งขับรถไปที่ทางเลือกกับทางเลือกที่ง่ายกว่า
ควอตซ์

2

MAD (x)เป็นเพียงการคำนวณค่ามัธยฐานสองค่าพร้อมกันซึ่งแต่ละอย่างสามารถทำผ่านออนไลน์ได้ binmedianอัลกอริทึม

คุณสามารถค้นหาเอกสารที่เกี่ยวข้องรวมทั้งรหัส C และ FORTRAN ออนไลน์ได้ที่นี่ที่นี่

(นี่เป็นเพียงการใช้เคล็ดลับที่ชาญฉลาดด้านบนของเคล็ดลับที่ฉลาดของ Shabbychef เพื่อประหยัดหน่วยความจำ)

ภาคผนวก:

มีโฮสต์ของวิธีการหลายรอบที่เก่ากว่าสำหรับการคำนวณปริมาณ วิธีการที่ได้รับความนิยมคือการรักษา / อัพเดทอ่างเก็บน้ำขนาดใหญ่แบบสังเกตได้จากการสุ่มเลือกจากกระแสและคำนวณปริมาณซ้ำ (ดูรีวิวนี้ ) บนอ่างเก็บน้ำนี้ วิธีการ (และที่เกี่ยวข้อง) นี้ถูกแทนที่โดยแนวทางที่เสนอข้างต้น


Could you please detail or reference the relation between MAD and the two medians?
Quartz

it's really the formula of the MAD: medi=1n|ximedi=1n| (hence two medians)
user603

Hehm, I actually meant if you can explain how is this relation allowing for the two medians to be concurrent; those seem dependent to me, since the inputs to the outer median may all change at each added sample to the inner calculation. How would you perform them in parallel?
Quartz

I have to go back to the binmedian paper for details...but given a computed value of the median (medi=1nxi) and a new value of xn+1 the algorithm could compute medi=1n+1xi much faster than O(n) by identifying the bin to which xn+1 belongs. I don't see how this insight could not be generalized to the outer median in the mad computation.
user603

1

The following provides an inaccurate approximation, although the inaccuracy will depend on the distribution of the input data. It is an online algorithm, but only approximates the absolute deviance. It is based on a well known algorithm for calculating variance online, described by Welford in the 1960s. His algorithm, translated into R, looks like:

M2 <- 0
mean <- 0
n <- 0

var.online <- function(x){
    n <<- n + 1
    diff <- x - mean
    mean <<- mean + diff / n
    M2 <<- M2 + diff * (x - mean)
    variance <- M2 / (n - 1)
    return(variance)
}

It performs very similarly to R's builtin variance function:

set.seed(2099)
n.testitems <- 1000
n.tests <- 100
differences <- rep(NA, n.tests)
for (i in 1:n.tests){
        # Reset counters
        M2 <- 0
        mean <- 0
        n <- 0

        xs <- rnorm(n.testitems)
        for (j in 1:n.testitems){
                v <- var.online(xs[j])
        }

        differences[i] <- abs(v - var(xs))

}
summary(differences)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
0.000e+00 2.220e-16 4.996e-16 6.595e-16 9.992e-16 1.887e-15 

Modifying the algorithm to calculate absolute deviation simply involves an additional sqrt call. However, the sqrt introduces inaccuracies that are reflected in the result:

absolute.deviance.online <- function(x){
    n <<- n + 1
    diff <- x - mean
    mean <<- mean + diff / n
    a.dev <<- a.dev + sqrt(diff * (x - mean))
    return(a.dev)
}

The errors, calculated as above, are much greater than for the variance calculation:

    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.005126 0.364600 0.808000 0.958800 1.360000 3.312000 

However, depending on your use case, this magnitude of error might be acceptable.

historgram of differences


This does not give the exact answer, for the following reason: ixiixi. You are computing the former, while the OP wants the latter.
shabbychef

I agree that the method is inexact. However, I disagree with your diagnosis of the inexactness. Welford's method for calculating variance, which does not even contain a sqrt, has a similar error. However, as n gets large, the error/n gets vanishingly small, suprisingly quickly.
fmark

Welford's method has no sqrt because it is computing the variance, not the standard deviation. By taking the sqrt, it seems like you are estimating the standard deviation, not the mean absolute deviation. am I missing something?
shabbychef

@shabbychef Each iteration of Welfords is calculating the contribution of the new datapoint to the absolute deviation, squared. So I take the square root of each contribution squared to get back to the absolute deviance. You might note, for example, that I take the square root of the delta before I add it to the deviance sum, rather than afterward as in the case of the standard deviation.
fmark

3
I see the problem; Welfords obscures the problem with this method: the online estimate of the mean is being used instead of the final estimate of the mean. While Welford's method is exact (up to roundoff) for variance, this method is not. The problem is not due to the sqrt imprecision. It is because it uses the running mean estimate. To see when this will break, try xs <- sort(rnorm(n.testitems)) When I try this with your code (after fixing it to return a.dev / n), I get relative errors on the order of 9%-16%. So this method is not permutation invariant, which could cause havoc...
shabbychef
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.