อัลกอริทึมในการกระจายรายการ“ อย่างสม่ำเสมอ”


25

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

ดังนั้นสำหรับรายการ:

[1, 1, 2, 2, 3, 3]

หนึ่งในผลลัพธ์ที่ดีที่สุดหลังจากการกระจายค่าอีกครั้งคือ:

[1, 2, 3, 1, 2, 3]

อาจมีผลลัพธ์อื่น ๆ ที่ดีเท่านี้และแน่นอนว่าสิ่งนี้จะซับซ้อนมากขึ้นด้วยชุดค่าที่น้อยกว่า

นี่คือวิธีการวัดว่าผลลัพธ์ดีกว่าวิธีอื่นหรือไม่:

  1. นับระยะทางระหว่างแต่ละรายการและรายการถัดไปด้วยค่าเดียวกัน

  2. คำนวณค่าเบี่ยงเบนมาตรฐานสำหรับชุดของระยะทาง การกระจายตัวที่ต่ำกว่าหมายถึงผลลัพธ์ที่ดีกว่า

ข้อสังเกต:

  • เมื่อคำนวณระยะทางและถึงจุดสิ้นสุดของรายการโดยไม่ต้องค้นหารายการที่มีค่าเท่ากันเราจะกลับไปที่จุดเริ่มต้นของรายการ ดังนั้นในที่สุดรายการเดียวกันจะถูกพบและระยะทางสำหรับรายการนั้นจะเป็นความยาวของรายการ ซึ่งหมายความว่ารายการเป็นวงกลม ;
  • รายการทั่วไปมี ~ 50 รายการที่มีค่าแตกต่างกัน ~ 15 ในปริมาณที่แตกต่างกัน

ดังนั้น:

  • สำหรับผลที่[1, 2, 3, 1, 2, 3]ระยะห่างอยู่[3, 3, 3, 3, 3, 3]และค่าเบี่ยงเบนมาตรฐานคือ0;
  • สำหรับผลที่[1, 1, 2, 2, 3, 3]ระยะห่างอยู่[1, 5, 1, 5, 1, 5]และค่าเบี่ยงเบนมาตรฐานคือ2;
  • ซึ่งทำให้ผลลัพธ์แรกดีกว่าวินาที (เบี่ยงเบนต่ำกว่าจะดีกว่า)

ด้วยคำจำกัดความเหล่านี้ฉันขอเบาะแสว่าฉันควรค้นหาอัลกอริทึมหรือกลยุทธ์


ดูเหมือนว่าคุณต้องการแก้ปัญหา ( พาร์ทิชันการเพิ่มประสิทธิภาพของ) ปัญหาพาร์ทิชันอย่างน้อยประมาณ อาจมีอัลกอริทึมมากมายสำหรับอันนั้น!
Raphael

อ่านสิ่งนี้ใหม่ทำไมการนับจำนวนที่เกิดขึ้นของค่าทั้งหมดและจากนั้นการวางค่าแบบวนซ้ำไม่ได้ให้ผลลัพธ์ที่ดีที่สุดเสมอไป
Raphael

คำตอบ:


8

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

หากคุณต้องการผสมของเหลว A, B และ C ในสัดส่วน 30,20,10 (นั่นคือ 30 หน่วยของ A, 20 หน่วยของ B และ 10 หน่วยของ C) คุณจะจบลงด้วยการแบ่งชั้นถ้าคุณเพิ่มทั้งหมด A จากนั้น B ทั้งหมดและ C ทั้งหมดคุณควรผสมหน่วยที่เล็กลง ตัวอย่างเช่นทำการเพิ่มหน่วยเดียวในลำดับ [A, B, A, C, B, A] ที่จะป้องกันการแบ่งชั้นโดยสิ้นเชิง

วิธีที่ฉันพบว่าทำคือการถือเป็นการผสานโดยใช้ลำดับความสำคัญ ถ้าฉันสร้างโครงสร้างเพื่ออธิบายเพิ่มเติม:

MergeItem
    Item, Count, Frequency, Priority

ความถี่ถูกแสดงเป็น "หนึ่งทุก N" ดังนั้น A ซึ่งถูกเพิ่มสามในหกครั้งมีความถี่ 2 (6/3)

และเริ่มต้นฮีปที่ประกอบด้วย:

(A, 3, 2, 2)
(B, 2, 3, 3)
(C, 1, 6, 6)

ตอนนี้ฉันลบรายการแรกออกจากฮีปและส่งออก จากนั้นลดจำนวนลง 1 และเพิ่มลำดับความสำคัญตามความถี่และเพิ่มกลับไปที่ฮีป ผลลัพธ์ที่ได้คือ:

(B, 2, 3, 0)
(A, 2, 2, 4)
(C, 1, 6, 6)

ถัดไปลบ B ออกจากฮีปเอาท์พุทและอัปเดตจากนั้นเพิ่มกลับไปที่ฮีป:

(A, 2, 2, 4)
(C, 1, 6, 6)
(B, 1, 3, 6)

ถ้าฉันทำแบบนั้นต่อฉันจะได้ส่วนผสมที่ต้องการ ฉันใช้เครื่องมือเปรียบเทียบที่กำหนดเองเพื่อให้แน่ใจว่าเมื่อมีการใส่รายการลำดับความสำคัญเท่ากันในฮีปรายการที่มีค่าความถี่สูงสุด (เช่นความถี่ที่น้อยที่สุด) จะได้รับคำสั่งก่อน

ฉันเขียนคำอธิบายปัญหาและวิธีแก้ไขปัญหาในบล็อกของฉันให้ครบถ้วนยิ่งขึ้นและแสดงรหัส C # ที่ใช้งานได้ซึ่งแสดงให้เห็น ดูรายการสม่ำเสมอกระจายในรายการ

อัปเดตหลังจากความคิดเห็น

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

การคัดค้านครั้งแรกที่โซลูชันของฉันใช้ A, B และ C มากกว่า 0, 1 และ 2 นั้นสามารถแก้ไขได้ง่าย มันเป็นเพียงเรื่องของการตั้งชื่อ ฉันคิดว่าง่ายกว่าและสับสนน้อยกว่าเมื่อคิดและพูดว่า "two A's" มากกว่า "two 1's" แต่สำหรับวัตถุประสงค์ของการสนทนานี้ฉันได้แก้ไขผลลัพธ์ของฉันด้านล่างเพื่อใช้ระบบการตั้งชื่อของ OP

แน่นอนปัญหาของฉันเกี่ยวกับแนวคิดของระยะทาง หากคุณต้องการ "กระจายสิ่งต่าง ๆ อย่างสม่ำเสมอ" ระยะทางก็จะบอกเป็นนัย แต่อีกครั้งมันเป็นความล้มเหลวของฉันที่ไม่เพียงพอแสดงว่าปัญหาของฉันคล้ายกับปัญหาของ OP

ฉันทดสอบสองสามตัวอย่างจากตัวอย่างที่ OP ให้ไว้ นั่นคือ:

[1,1,2,2,3,3]  // which I converted to [0,0,1,1,2,2]
[0,0,0,0,1,1,1,2,2,3]

ในระบบการตั้งชื่อของฉันสิ่งเหล่านี้แสดงเป็น [2,2,2] และ [4,3,2,1] ตามลำดับ นั่นคือในตัวอย่างสุดท้าย "4 รายการประเภท 0, 3 รายการประเภท 1, 2 รายการประเภท 2 และ 1 รายการประเภท 3"

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

ผมสามารถพูดได้อย่างไรว่าอัลกอริทึมให้เป็นทางออกที่ดีของฉันมีปัญหาในการขจัดชนชั้นเมื่อผสมของเหลว และดูเหมือนว่าจะให้ทางออกที่สมเหตุสมผลในการแก้ไขปัญหาของ OP

สำหรับผลลัพธ์ที่แสดงด้านล่างฉันใช้อัลกอริทึมที่ฉันให้รายละเอียดในรายการบล็อกของฉันโดยมีการตั้งค่าลำดับความสำคัญเริ่มต้นเป็นFrequency/2และตัวเปรียบเทียบฮีปที่ถูกปรับเปลี่ยนเพื่อให้เหมาะกับรายการที่ใช้บ่อยกว่า รหัสที่แก้ไขจะแสดงที่นี่พร้อมกับบรรทัดที่แก้ไขแสดงความคิดเห็น

private class HeapItem : IComparable<HeapItem>
{
    public int ItemIndex { get; private set; }
    public int Count { get; set; }
    public double Frequency { get; private set; }
    public double Priority { get; set; }

    public HeapItem(int itemIndex, int count, int totalItems)
    {
        ItemIndex = itemIndex;
        Count = count;
        Frequency = (double)totalItems / Count;
        // ** Modified the initial priority setting.
        Priority = Frequency/2;
    }

    public int CompareTo(HeapItem other)
    {
        if (other == null) return 1;
        var rslt = Priority.CompareTo(other.Priority);
        if (rslt == 0)
        {
            // ** Modified to favor the more frequent item.
            rslt = Frequency.CompareTo(other.Frequency);
        }
        return rslt;
    }
}

ใช้โปรแกรมทดสอบของฉันกับตัวอย่างแรกของ OP ฉันจะได้รับ:

Counts: 2,2,2
Sequence: 1,0,2,1,0,2
Distances for item type 0: 3,3
Stddev = 0
Distances for item type 1: 3,3
Stddev = 0
Distances for item type 2: 3,3
Stddev = 0

อัลกอริทึมของฉันทำงานได้กับปัญหาเล็ก ๆ น้อย ๆ ของการนับทั้งหมดที่เท่ากัน

สำหรับปัญหาที่สองที่ OP โพสต์ฉันได้รับ:

Counts: 4,3,2,1
Sequence: 0,1,2,0,1,3,0,2,1,0
Distances for item type 0: 3,3,3,1
Stddev = 0.866025403784439
Distances for item type 1: 3,4,3
Stddev = 0.471404520791032
Distances for item type 2: 5,5
Stddev = 0
Distances for item type 3: 10
Stddev = 0
Standard dev: 0.866025403784439,0.471404520791032,0,0

ฉันไม่เห็นวิธีที่ชัดเจนในการปรับปรุง อาจถูกจัดเรียงใหม่เพื่อให้ระยะทางสำหรับรายการ 0 [2,3,2,3] หรือการจัดเรียงอื่นของ 2 และ 3 แต่จะเปลี่ยนความเบี่ยงเบนสำหรับรายการ 1 และ / หรือ 2 ฉันไม่รู้จริง ๆ ว่าอะไร "เหมาะสม" อยู่ในสถานการณ์นี้ มันจะดีกว่าหรือไม่ที่จะมีการเบี่ยงเบนมากขึ้นในรายการที่บ่อยขึ้นหรือในรายการที่น้อยกว่า?

ไม่มีปัญหาอื่น ๆ จาก OP ฉันใช้คำอธิบายของเขาเพื่อสร้างบางส่วนของตัวเอง เขาพูดในโพสต์ของเขา:

รายการทั่วไปมี ~ 50 รายการที่มีค่าแตกต่างกัน ~ 15 ในปริมาณที่แตกต่างกัน

ดังนั้นการทดสอบทั้งสองของฉันคือ:

[8,7,6,5,5,4,3,3,2,2,2,1,1,1,1]  // 51 items, 15 types
[12,6,5,4,4,3,3,3,2,2,2,1,1]     // 48 items, 13 types

และผลลัพธ์ของฉัน:

Counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sequence: 0,1,2,3,4,5,7,6,0,1,2,8,9,10,4,3,0,1,5,2,0,1,3,4,6,7,14,11,13,12,0,2,5,1,0,3,4,2,8,10,9,1,0,7,6,5,3,4,2,1,0
Distances for item type 0: 8,8,4,10,4,8,8,1
Stddev = 2.82566363886433
Distances for item type 1: 8,8,4,12,8,8,3
Stddev = 2.76272565797339
Distances for item type 2: 8,9,12,6,11,5
Stddev = 2.5
Distances for item type 3: 12,7,13,11,8
Stddev = 2.31516738055804
Distances for item type 4: 10,9,13,11,8
Stddev = 1.72046505340853
Distances for item type 5: 13,14,13,11
Stddev = 1.08972473588517
Distances for item type 6: 17,20,14
Stddev = 2.44948974278318
Distances for item type 7: 19,18,14
Stddev = 2.16024689946929
Distances for item type 8: 27,24
Stddev = 1.5
Distances for item type 9: 28,23
Stddev = 2.5
Distances for item type 10: 26,25
Stddev = 0.5
Distances for item type 11: 51
Stddev = 0
Distances for item type 12: 51
Stddev = 0
Distances for item type 13: 51
Stddev = 0
Distances for item type 14: 51
Stddev = 0

และสำหรับตัวอย่างที่สอง:

Counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sequence: 0,1,2,0,3,4,7,5,6,0,1,8,9,10,0,2,0,3,4,1,0,2,6,7,5,12,11,0,1,0,3,4,2,0,1,10,8,9,0,7,5,6,0,
4,3,2,1,0
Distances for item type 0: 3,6,5,2,4,7,2,4,5,4,5,1
Stddev = 1.68325082306035
Distances for item type 1: 9,9,9,6,12,3
Stddev = 2.82842712474619
Distances for item type 2: 13,6,11,13,5
Stddev = 3.44093010681705
Distances for item type 3: 13,13,14,8
Stddev = 2.34520787991171
Distances for item type 4: 13,13,12,10
Stddev = 1.22474487139159
Distances for item type 5: 17,16,15
Stddev = 0.816496580927726
Distances for item type 6: 14,19,15
Stddev = 2.16024689946929
Distances for item type 7: 17,16,15
Stddev = 0.816496580927726
Distances for item type 8: 25,23
Stddev = 1
Distances for item type 9: 25,23
Stddev = 1
Distances for item type 10: 22,26
Stddev = 2
Distances for item type 11: 48
Stddev = 0
Distances for item type 12: 48
Stddev = 0

@DW โปรดดูการอัปเดตของฉัน ฉันเชื่อว่าฉันแสดงให้เห็นว่าปัญหาของฉันคล้ายกับปัญหาของ OP อย่างไรและอัลกอริทึมของฉันมีวิธีการแก้ไขปัญหาของ OP อย่างไร
Jim Mischel

สิ่งที่ดี! ขอบคุณสำหรับการปรับปรุงที่ยอดเยี่ยม upvoted
DW

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

@babou: การคำนวณระยะทางของฉันล้อมรอบอย่างที่คุณเห็นในผลลัพธ์ แต่อัลกอริทึมนั้นไม่ได้ให้ค่าเผื่อพิเศษสำหรับลักษณะวัฏจักรของปัญหา OP ฉันไม่เห็นวิธีใดที่ฉันจะสามารถปรับอัลกอริทึมให้ทำได้ หรือสำหรับเรื่องนั้นการคำนึงถึงลักษณะของวัฏจักรเป็นอย่างไรจะปรับปรุงผลลัพธ์ แม้ว่าจะเป็นเรื่องที่น่าสนใจที่จะพิจารณาเพิ่มจำนวนทั้งหมด (เช่นเปลี่ยน [3,2,1] เป็น [6,4,2]) ซึ่งจะเป็นสิ่งเดียวกันอย่างมีประสิทธิภาพ ฉันสงสัยว่าอัลกอริทึมจะให้ผลลัพธ์ที่เหมือนกัน
Jim Mischel

6

"กลิ่น" แบบนี้อาจจะเป็นเรื่องยาก แล้วคุณจะทำอย่างไรเมื่อคุณมีปัญหา NP-hard โยนฮิวริสติกหรืออัลกอริธึมการประมาณหรือใช้ตัวแก้ SAT

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

ttt2xi,jxi,jijt2

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


คำแนะนำของคุณเป็นวิธีมาตรฐานในการแก้ไขปัญหาการตั้งเวลาประเภทนี้หรือไม่ ฉันเดาว่ามีซอฟต์แวร์เชิงพาณิชย์สำหรับเรื่องนี้ พวกเขาจัดการกับมันอย่างไร?
babou

@Babou คำถามที่ดี - ฉันไม่มีความคิด!
DW

ฉันพัฒนารายละเอียดของอัลกอริทึมต่อไป แต่ฉันสงสัยว่าแอปพลิเคชันที่มีอยู่จะใช้มันเป็นอย่างมาก ที่จริงแล้วฉันยังสงสัยว่าแอปพลิเคชั่นกำหนดเวลาจัดการกับปัญหาประเภทนี้หรือไม่ ฉันได้รับการขอข้อมูลเกี่ยวกับ SE.softwarerecs เนื่องจากฉันไม่เห็นวิธีการถามคำถามที่นี่นอกเหนือจากความคิดเห็นตามที่ฉันเพิ่งทำ
babou

ที่ดีที่สุดวิธีการแก้ปัญหาอาจจะ NP-ยาก แต่วิธีแก้ปัญหาที่ใช้งานได้ดีก็คือ O (n log k) โดยที่ n คือจำนวนรายการทั้งหมดและ k คือจำนวนของประเภทรายการ ดูคำตอบของฉันและโพสต์บล็อกที่เชื่อมโยงของฉัน
Jim Mischel

2

ร่างของอัลกอริทึมแบบฮิวริสติก

ฉันไม่มีวิธีแก้ไขปัญหาที่แน่นอนสำหรับปัญหานี้ แต่เนื่องจากความคิดเห็นของราฟาเอลชี้ให้เห็นว่าดูเหมือนปัญหาพาร์ติชันซึ่งอัลกอริทึมฮิวริสติกได้รับการพัฒนาขึ้นฉันจะลองใช้วิธีแก้ปัญหาแบบฮิวริสติก นี่เป็นเพียงภาพร่างของอัลกอริทึมแบบฮิวริสติก

vn[1..n]ini

nvnvn/nv

v

in/ninmodnin/ni

นั่นจะเป็นแนวทางของอัลกอริทึมของเรา

n

i|n/niv|

มันอาจเป็นค่าที่เกิดขึ้นน้อยมากในตอนแรก ฉันคิดว่ามันไม่ได้สร้างความแตกต่างจริง ๆ เนื่องจากข้อ จำกัด ที่สร้างโดยการครอบครองสล็อตเป็นสัดส่วนของจำนวนค่าที่ดี (?)

Thr ค่าที่พิจารณาครั้งแรกสามารถวางได้โดยไม่มีข้อ จำกัด ใด ๆ จากนั้นจะต้องวางค่าอื่น ๆ เพื่อลดการมีส่วนร่วมในการเบี่ยงเบนมาตรฐาน แต่จะอยู่ในสล็อตที่เหลือโดยค่าใด ๆ ที่ถูกวางก่อนหน้านี้เท่านั้น

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

v

j|n/njv|

จากนั้นคุณใส่ค่าซิงเกิลในช่องที่เหลือ

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


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

vn/vV

คุณหมายถึงว่าสำหรับรายการที่มี 10 ค่า[0, 0, 0, 0, 1, 1, 1, 2, 2, 3]และ v 4เราจะวางค่าแรก1( 10/3 = 3.33ใกล้เคียงกับ v) แล้ว2( 10/2 = 5ใกล้ถัดไป) แล้วจากนั้น0( 10/4 = 2.5)? หรือ: คุณช่วยยกตัวอย่างของ "การลดความเบี่ยงเบนเฉลี่ยของระยะทางจากค่า v" ได้หรือไม่?
moraes

1
ไม่ฉันทำตรงข้าม จากตัวอย่างของคุณลำดับของการจัดตำแหน่งคือ O แรกเนื่องจากระยะทางเฉลี่ยอยู่ที่ 2.5 เบี่ยงเบนจาก v = 4 จากนั้น 2 จากนั้น 1 และ 1 ซิงเกิลที่ 3 - - - ypu แนะนำว่าฉันควรเขียนให้ชัดเจนยิ่งขึ้น เป็นส่วนหนึ่งของคำอธิบายของฉันสำหรับกลยุทธ์นี้หรือไม่?
babou

ไม่เป็นไร. ฉันจะลองทำตามความคิดนี้และรายงานกลับ
moraes

1

ดูเหมือนว่าฉันจะไปงานปาร์ตี้ช้ามาก แต่การโพสต์ในกรณีที่ใครก็ตามพบเจออีกครั้ง โซลูชันของฉันคล้ายกับ @ babou's plus ก่อนหน้านี้วันนี้ฉันมีปัญหาเกี่ยวกับการตั้งเวลาในระบบสมองกลฝังตัวซึ่งทำให้ฉันไปที่หัวข้อนี้ ฉันมีการนำไปใช้เฉพาะกับปัญหาของฉันใน C แต่ฉันคิดว่าฉันโพสต์คำตอบทั่วไปใน Python ที่นี่ (เวอร์ชั่น C มีความซับซ้อนเนื่องจากข้อเท็จจริงที่ว่าฉันได้ จำกัด ตัวเองเป็นสแต็กขนาดคงที่ขนาดเล็กและไม่มีหน่วยความจำ การจัดสรรดังนั้นฉันทำอัลกอริธึมทั้งหมดในสถานที่) เทคนิคลดรอยหยักที่ใช้ด้านล่างเป็นสิ่งที่คุณอาจใช้ในการวาดเส้นบนหน้าจอที่มีสี 2 บิต อัลกอริทึมที่นี่ได้คะแนนต่ำกว่า (กล่าวคือดีกว่า) เมื่อวัดโดยใช้ผลรวมของค่าเบี่ยงเบนมาตรฐานสำหรับอินพุตที่ Jim Mischel ใช้มากกว่าโซลูชันเฉพาะนั้น

def generate(item_counts):
'''item_counts is a list of counts of "types" of items. E.g., [3, 1, 0, 2] represents
   a list containing [1, 1, 1, 2, 4, 4] (3 types of items/distinct values). Generate
   a new list with evenly spaced values.'''
# Sort number of occurrences by decreasing value.
item_counts.sort(reverse=True)
# Count the total elements in the final list.
unplaced = sum(item_counts)
# Create the final list.
placements = [None] * unplaced

# For each type of item, place it into the list item_count times.
for item_type, item_count in enumerate(item_counts):
    # The number of times the item has already been placed
    instance = 0
    # Evenly divide the item amongst the remaining unused spaces, starting with
    # the first unused space encountered.
    # blank_count is the number of unused spaces seen so far and is reset for each
    # item type.
    blank_count = -1
    for position in range(len(placements)):
        if placements[position] is None:
            blank_count += 1
            # Use an anti-aliasing technique to prevent bunching of values.
            if blank_count * item_count // unplaced == instance:
                placements[position] = item_type
                instance += 1
    # Update the count of number of unplaced items.
    unplaced -= item_count

return placements

ผลลัพธ์สำหรับ

Input counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sum of stddev: 16.8 (vs. 22.3 via Jim Mischel)

Input of counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sum of stddev: 18.0 (vs. 19.3 via Jim Mischel)

หากอินพุตที่กำหนดของรูปแบบที่ระบุโดย @moraes สามารถแปลงเป็นรูปแบบที่สามารถใช้งานได้โดยฟังก์ชั่นนี้ในขั้นตอน O (n) โดยใช้บิทหน่วยความจำขนาดใหญ่ Omega (n * log (n)) โดยที่ n คือจำนวนรายการ ในรายการที่มี 255 องค์ประกอบคุณไม่จำเป็นต้องมากกว่า 255 ไบต์พิเศษ) โดยการรักษาอาร์เรย์ขนานที่มีจำนวนการทำซ้ำ อีกวิธีหนึ่งสามารถดำเนินการเรียงลำดับคู่กับหน่วยความจำเสริม O (1)

PS

import numpy
import collections

def evaluate(l):
    '''Given a distribution solution, print the sum of stddevs for each type in the solution.'''
    l2 = l * 2
    distances = [None] * len(l)
    distance_dict = collections.defaultdict(list)
    for i in range(len(l)):
        distances[i] = l2.index(l[i], i + 1) - i
        distance_dict[l[i]].append(l2.index(l[i], i + 1) - i)

    keys = list(distance_dict.keys())
    keys.sort()
    score = 0
    # Calculate standard deviations for individual types.
    for key in keys:
        sc = numpy.std(distance_dict[key])
        score += sc
    print('Stddev sum: ', score)

แก้ไข:ฉันรู้ว่าวิธีการแก้ปัญหานี้ไม่ได้สร้างผลลัพธ์ที่ดีที่สุดโดย counterexample อินพุตของ[6, 2, 1]ผลิตผล[0, 1, 0, 0, 2, 0, 0, 1, 0]; [0, 0, 1, 0, 2, 0, 0, 1, 0]เป็นทางออกที่ดีคือ


ฉันเชื่อว่าฉันอธิบายอัลกอริทึมของฉันในการแสดงความคิดเห็นรหัสและพื้นฐานสำหรับอัลกอริทึมในการเริ่มนำ
lungj

ฉันอยากจะเห็นคำอธิบายในตัวเองของแนวคิดเบื้องหลังอัลกอริทึมของคุณและ pseudocode ที่กระชับสำหรับอัลกอริทึม ขณะนี้สิ่งที่ฉันเห็นในข้อความเกริ่นนำคือ (1) วิธีการของคุณคล้ายกับ @ babou's และ (2) มันใช้เทคนิคต่อต้านนามแฝง (อย่างใด) นอกจากนี้ไม่ใช่ทุกคนที่อ่าน Python ที่นี่ ไม่ว่าในกรณีใดมันเป็นคำตอบเก่าดังนั้นฉันเข้าใจว่าคุณไม่ต้องการปรับปรุง แต่ฉันเพิ่งสังเกตเห็นความคาดหวังของเราในเว็บไซต์นี้ - ไม่ใช่แค่สำหรับคุณ แต่สำหรับคนอื่น ๆ ที่อาจวิ่งข้ามหน้านี้ใน อนาคตและมีแนวโน้มที่จะตอบ
DW

0

อัลกอริทึมนี้ทำงานร่วมกับอาร์เรย์ของจำนวนเต็มโดยที่จำนวนเต็มแต่ละตัวแทนหมวดหมู่ที่แตกต่างกัน มันสร้างอาร์เรย์แยกต่างหากสำหรับแต่ละหมวดหมู่ ตัวอย่างเช่นถ้าอาร์เรย์เริ่มต้นคือ [1, 1, 1, 2, 2, 3] มันจะสร้างสามอาร์เรย์ [3], [2, 2], [1, 1, 1]

จากที่นั่นมันรวมสองอาร์เรย์ที่เล็กที่สุดซ้ำ (ในตัวอย่างนี้ [3] และ [2,2]) และเว้นช่องว่างการจัดวางองค์ประกอบของอาร์เรย์ขนาดเล็กลงในอาร์เรย์ที่เล็กที่สุดเป็นอันดับสองโดยส่วนใหญ่ขึ้นอยู่กับอัตราส่วนของจำนวน ของการเกิดขึ้นของประเภทที่มีขนาดใหญ่กว่าและขนาดเล็ก ในตัวอย่างนี้เราจะปิดท้ายด้วย [2,3,2] จากนั้นมันจะใช้อาเรย์นี้เป็นอาเรย์ที่เล็กกว่าซึ่งจะถูกรวมเข้าในอาเรย์ที่ใหญ่กว่าถัดไปจนกว่าจะเหลือเพียงอาเรย์เดียวเท่านั้น

<?php
/**
 *This will separete the source array into separate arrays for each category
 */
function splitArrayByCategory($source) {

    //  index is the category, value is the tally
    $categoryCounts  = array_count_values($source);

    // Sort that list, keep index associations
    asort($categoryCounts);

    // build separate arrays for each category
    // index = order, smaller index = smaller category count
    // value = array of each category
    $separated = array();
    foreach ($categoryCounts as $category => $tally)
        $separated[] = array_fill(0, $tally, $category);

    return $separated;
}

/**
 * Will take the array of arrays generated by splitArrayByCategory, and merge
 * them together so categories are evenly distributed
 */
function mergeCategoryArrays($categoryArray) {

    // How many entries are there, for the smallest & second smallest categories
    $smallerCount = count($categoryArray[0]);
    $largerCount  = count($categoryArray[1]);

    // Used to determine how much space there should be between these two categories
    $space = $largerCount/$smallerCount;

    // Merge the array of the smallest category into the array of the second smallest
    foreach ($categoryArray[0] as $domIndex => $domain) {
        // Where should we splice the value into the second array?
        $location = floor($domIndex*$space+$domIndex+($space/2));
        // actually perform the splice
        array_splice($categoryArray[1], $location, 0, $domain);
    }

    // remove the smallest domain we just spliced into the second smallest
    unset($categoryArray[0]);

    // reset the indexes
    $categoryArray = array_values($categoryArray);

    // If we have more than one index left in the categoryArray (i.e. some things
    // still need to get merged), let's re-run this function,
    if (count($categoryArray)>1)
        $categoryArray = mergeCategoryArrays($categoryArray);

    return $categoryArray;
}

// The sample list we're working with.
// each integer represents a different category
$listSample = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,6,6,7,7,7,7];

// Split the sample list into separate arrays for each category
$listSplitByCategory = splitArrayByCategory($listSample);

// If there are not at least two categories, what's the point?
if (count($listSplitByCategory) < 2) throw new Exception("You need at least two categories");

// perform the actual distribution of categories.
$listEvenlyDistributed = mergeCategoryArrays($listSplitByCategory)[0];

// Display the array before and after the categories are evenly distributed
for ($i=0; $i<count($listSample); $i++) {
    print $listSample[$i].",";
    print $listEvenlyDistributed[$i]."\n";
}

2
นี่ไม่ใช่ไซต์การเข้ารหัส กรุณาอย่าโพสต์คำตอบรหัสเท่านั้น แต่เราต้องการให้คุณอธิบายความคิดที่อยู่เบื้องหลังคำตอบของคุณและให้ pseudocode ที่กระชับสำหรับอัลกอริทึมของคุณ
DW

ยินดีต้อนรับสู่วิทยาการคอมพิวเตอร์ ! ในกรณีที่คุณไม่ทราบหรือคุณลืมไปสักครู่การอ่านโค้ดในภาษาใดภาษาหนึ่งเป็นงานที่ยากที่สุดที่เราสามารถทำได้แม้บางครั้งรหัสจะถูกเขียนขึ้นเอง นี่เป็นส่วนหนึ่งของเหตุผลที่เราไม่ได้ชื่นชมรหัสจริงมากในเว็บไซต์นี้แม้ว่ามันอาจเป็นตัวแทนของการทำงานมากกว่า pseudocode ที่เขียนขึ้นอย่างหลวม ๆ แน่นอนฉันขอขอบคุณรหัสการทำงานจริงทั้งหมดที่สามารถเรียกใช้หรือ twinkled ได้ทันที
Apass.Jack

คำอธิบายอยู่ที่นั่น ในรหัสการสาธิตความเห็น; ซึ่งไม่ได้อยู่ในไวยากรณ์ที่เก่าแก่บางอย่างเช่น APL แต่ง่ายต่อการเข้าใจไวยากรณ์ใกล้พอที่จะหลอกรหัส มันจะช่วยได้ไหมถ้าคำอธิบายของฉันไม่ได้อยู่ในรูปแบบตัวอักษร monospace?
vtim

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

-1

รหัส ANSI C

รหัสนี้ทำงานโดยการจินตนาการเป็นเส้นตรงในปริภูมิมิติ n (โดยที่ n คือจำนวนหมวดหมู่) ผ่านจุดกำเนิดด้วยเวกเตอร์ทิศทาง (v1, v2, ... , vi, ... vn) โดยที่ vi คือจำนวน รายการในหมวดหมู่ i เริ่มต้นจากจุดกำเนิดจุดมุ่งหมายคือการหาจุดที่ใกล้ที่สุดต่อไปถึงเส้น ใช้ตัวอย่าง [0 0 0 0 0 1 1 1 2 2 2 3] มันสร้างผลลัพธ์ [0 1 2 0 3 1 0 2 0 1 2 0] ใช้ตัวอย่างของ Lungj [0 0 0 0 0 0 1 1 2] เราได้ [0 1 0 0 2 0 0 1 0] ซึ่งตรงกับผลลัพธ์ของ Lungj

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

#define MAXCATEGORIES 100

int main () {int i = 0; int j = 0; int catsize = 0; เวกเตอร์ int [MAXCATEGORIES]; จุด int [MAXCATEGORIES]; หมวดหมู่ int = 0; int totalitems = 0; int ที่ดีที่สุด = 0; ยาว d2 = 0L; vp ยาว = 0L; ยาว v2 = 0L; เดลต้ายาว = 0L; เบต้ายาว = 0L;

printf("Enter the size of each category (enter 0 to finish):\r\n");
do
{
    catsize = 0;
    #ifdef _MSVC_LANG
            scanf_s("%d", &catsize);
    #else
            scanf("%d", &catsize)
    #endif
    if (catsize > 0)
    {
        vector[categories] = catsize;
        totalitems += catsize;
        categories++;
    }
} while (catsize > 0);

for (i = 0; i < categories; i++)
{
    v2 += vector[i] * vector[i];
    point[i] = 0;
}

for (i = 0; i < totalitems; i++)
{
    for (j = 0; j < categories; j++)
    {
        delta = (2 * point[j] + 1)*v2 - (2 * vp + vector[j])*vector[j];
        if (j == 0 || delta < beta)
        {
            best = j;
            beta = delta;
        }
    }
    point[best]++;
    vp += vector[best];
    printf("%c ", best + '0');  // Change '0' to 'A' if you like letters instead
}
return 0;

}


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

นี่ไม่ใช่ไซต์การเข้ารหัส กรุณาอย่าโพสต์คำตอบรหัสเท่านั้น แต่เราต้องการให้คุณอธิบายความคิดที่อยู่เบื้องหลังคำตอบของคุณและให้ pseudocode ที่กระชับสำหรับอัลกอริทึมของคุณ
DW

-1

ทางออกของฉัน:

    vc = train['classes'].value_counts()
    vc = dict(sorted(vc.items()))
    df = pd.DataFrame()
    train['col_for_sort'] = 0.0

    i=1
    for k,v in vc.items():
        step = train.shape[0]/v
        indent = train.shape[0]/(v+1)
        df2 = train[train['classes'] == k].reset_index(drop=True)
        for j in range(0, v):
        df2.at[j, 'col_for_sort'] = indent + j*step + 0.0001*i   
    df= pd.concat([df2,df])
    i+=1

    train = df.sort_values('col_for_sort', ascending=False).reset_index(drop=True)
    del train['col_for_sort']

โปรดใช้รหัสเทียม (พร้อมความคิดเห็นที่จำเป็น) เพื่ออธิบายอัลกอริทึมของคุณ
xskxzr

นี่ไม่ใช่ไซต์การเข้ารหัส กรุณาอย่าโพสต์คำตอบรหัสเท่านั้น แต่เราต้องการให้คุณอธิบายความคิดที่อยู่เบื้องหลังคำตอบของคุณและให้ pseudocode ที่กระชับสำหรับอัลกอริทึมของคุณ
DW
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.