การแก้ปัญหาเป็นไปได้เพียงเพราะความแตกต่างระหว่าง 1 เมกะไบต์และ 1 ล้านไบต์ มีประมาณ 2 ถึงกำลัง 8093729.5 วิธีที่แตกต่างในการเลือก 1 ล้าน 8 หลักตัวเลขด้วยซ้ำได้รับอนุญาตและการสั่งซื้อที่ไม่สำคัญดังนั้นเครื่องที่มี RAM เพียง 1 ล้านไบต์ไม่มีสถานะเพียงพอที่จะแสดงถึงความเป็นไปได้ทั้งหมด แต่ 1M (น้อยกว่า 2k สำหรับ TCP / IP) คือ 1022 * 1024 * 8 = 8372224 บิตดังนั้นการแก้ปัญหาจึงเป็นไปได้
ส่วนที่ 1 การแก้ปัญหาเบื้องต้น
วิธีนี้ต้องการมากกว่า 1M เล็กน้อยฉันจะปรับแต่งให้พอดีกับ 1M ในภายหลัง
ฉันจะเก็บรายการที่เรียงลำดับขนาดกะทัดรัดของตัวเลขในช่วง 0 ถึง 99999999 เป็นลำดับของรายการย่อยของตัวเลข 7 บิต รายการย่อยแรกเก็บตัวเลขตั้งแต่ 0 ถึง 127 รายการย่อยที่สองเก็บหมายเลขได้ตั้งแต่ 128 ถึง 255 และ 100000000/128 นั้นมีค่าเท่ากับ 781250 ดังนั้น 781250 จึงจำเป็นต้องมีรายการย่อยดังกล่าว
แต่ละรายการย่อยประกอบด้วยส่วนหัวของรายการย่อยแบบ 2 บิตตามด้วยเนื้อหาของรายการย่อย เนื้อหาย่อยรายการใช้เวลา 7 บิตต่อรายการรายการย่อย รายการย่อยทั้งหมดจะถูกรวมเข้าด้วยกันและรูปแบบทำให้สามารถบอกได้ว่ารายการย่อยหนึ่งจบลงที่ใด พื้นที่เก็บข้อมูลทั้งหมดที่จำเป็นสำหรับรายการที่มีประชากรเต็มรูปแบบคือ 2 * 781250 + 7 * 1000000 = 8562500 บิตซึ่งประมาณ 1.021 M- ไบต์
ค่าส่วนหัวของรายการย่อย 4 ที่เป็นไปได้คือ:
00รายการย่อยว่างเปล่าไม่มีอะไรติดตาม
01 Singleton มีเพียงหนึ่งรายการในรายการย่อยและอีก 7 บิตจะเก็บไว้
10รายการย่อยมีตัวเลขที่แตกต่างกันอย่างน้อย 2 ตัว รายการจะถูกเก็บไว้ในลำดับที่ไม่ลดลงยกเว้นว่ารายการสุดท้ายมีค่าน้อยกว่าหรือเท่ากับรายการแรก สิ่งนี้ยอมให้มีการระบุจุดสิ้นสุดของรายการย่อย ตัวอย่างเช่นตัวเลข 2,4,6 จะถูกเก็บไว้เป็น (4,6,2) ตัวเลข 2,2,3,4,4 จะถูกจัดเก็บเป็น (2,3,4,4,2)
11รายการย่อยมีการซ้ำซ้อน 2 ครั้งขึ้นไปของหมายเลขเดียว 7 บิตถัดไปให้หมายเลข จากนั้นมารายการ 7 บิตที่เป็นศูนย์หรือมากกว่าด้วยค่า 1 ตามด้วยรายการ 7 บิตที่มีค่า 0 ความยาวของเนื้อหาย่อยจะกำหนดจำนวนการทำซ้ำ ตัวอย่างเช่นตัวเลข 12,12 จะถูกจัดเก็บเป็น (12,0), ตัวเลข 12,12,12 จะถูกเก็บไว้เป็น (12,1,0), 12,12,12,12 จะเป็น (12,1) , 1,0) และอื่น ๆ
ฉันเริ่มด้วยรายการที่ว่างอ่านตัวเลขและเก็บไว้เป็นจำนวนเต็ม 32 บิตเรียงลำดับหมายเลขใหม่เข้าที่ (ใช้ heapsort, อาจ) จากนั้นรวมเข้าไว้ในรายการเรียงลำดับขนาดกะทัดรัดใหม่ ทำซ้ำจนกว่าจะไม่มีตัวเลขให้อ่านแล้วเดินรายการขนาดกะทัดรัดอีกครั้งเพื่อสร้างผลลัพธ์
บรรทัดด้านล่างแสดงถึงหน่วยความจำก่อนเริ่มการดำเนินการผสานรายการ "O" คือพื้นที่ที่เก็บจำนวนเต็ม 32- บิตที่เรียงลำดับไว้ "X" เป็นพื้นที่ที่มีรายการขนาดเล็กแบบเก่า เครื่องหมาย "=" เป็นห้องส่วนขยายสำหรับรายการขนาดกะทัดรัด 7 บิตสำหรับแต่ละจำนวนเต็มใน "O" "Z" เป็นค่าใช้จ่ายแบบสุ่มอื่น ๆ
ZZZOOOOOOOOOOOOOOOOOOOOOOOOOO==========XXXXXXXXXXXXXXXXXXXXXXXXXX
รูทีนการผสานเริ่มอ่านที่ "O" ซ้ายสุดและที่ซ้ายสุด "X" และเริ่มเขียนที่ซ้ายสุด "=" ตัวชี้การเขียนไม่จับตัวชี้การอ่านรายการแบบกะทัดรัดจนกว่าจำนวนเต็มใหม่ทั้งหมดจะถูกรวมกันเนื่องจากตัวชี้ทั้งสองเลื่อนไปข้างหน้า 2 บิตสำหรับแต่ละรายการย่อยและ 7 บิตสำหรับแต่ละรายการในรายการขนาดกะทัดรัดเก่าและมีพื้นที่เหลือเพียงพอสำหรับ รายการ 7 บิตสำหรับหมายเลขใหม่
ตอนที่ 2 ยัดเข้าไปใน 1M
ในการบีบโซลูชันข้างต้นเป็น 1M ฉันต้องทำให้รูปแบบรายการแบบกะทัดรัดมีขนาดกะทัดรัดขึ้นเล็กน้อย ฉันจะกำจัดหนึ่งในประเภทรายการย่อยเพื่อที่จะมีค่าส่วนหัวของรายการย่อยที่แตกต่างกันเพียง 3 ค่า จากนั้นฉันสามารถใช้ "00", "01" และ "1" เป็นค่าส่วนหัวของรายการย่อยและบันทึกไม่กี่บิต ประเภทรายการย่อยคือ:
รายการย่อยว่างเปล่าไม่มีอะไรตามมา
B Singleton มีเพียงหนึ่งรายการในรายการย่อยและ 7 บิตถัดไปถือไว้
C รายการย่อยมีตัวเลขที่แตกต่างกันอย่างน้อย 2 ตัว รายการจะถูกเก็บไว้ในลำดับที่ไม่ลดลงยกเว้นว่ารายการสุดท้ายมีค่าน้อยกว่าหรือเท่ากับรายการแรก สิ่งนี้ยอมให้มีการระบุจุดสิ้นสุดของรายการย่อย ตัวอย่างเช่นตัวเลข 2,4,6 จะถูกเก็บไว้เป็น (4,6,2) ตัวเลข 2,2,3,4,4 จะถูกจัดเก็บเป็น (2,3,4,4,2)
D รายการย่อยประกอบด้วยการทำซ้ำ 2 ครั้งขึ้นไปของหมายเลขเดียว
ค่าส่วนหัวของรายการย่อย 3 ของฉันจะเป็น "A", "B" และ "C" ดังนั้นฉันต้องการวิธีในการแสดงรายการย่อยของ D-type
สมมติว่าฉันมีส่วนหัวของรายการย่อยประเภท C ตามด้วย 3 รายการเช่น "C [17] [101] [58]" นี่ไม่สามารถเป็นส่วนหนึ่งของรายการย่อยประเภท C ที่ถูกต้องตามที่อธิบายไว้ข้างต้นเนื่องจากรายการที่สามน้อยกว่ารายการที่สอง แต่มากกว่ารายการแรก ฉันสามารถใช้โครงสร้างประเภทนี้เพื่อเป็นตัวแทนรายการย่อย D-type กล่าวโดยนัยคือทุกที่ที่ฉันมี "C {00 ?????} {1 ??????} {01 ?????}" เป็นรายการย่อยประเภท C ที่เป็นไปไม่ได้ ฉันจะใช้สิ่งนี้เพื่อแสดงรายการย่อยที่ประกอบด้วยการซ้ำ 3 ครั้งขึ้นไปของหมายเลขเดียว คำ 7 บิตสองคำแรกเข้ารหัสหมายเลข (บิต "N" ด้านล่าง) และตามด้วยศูนย์ {0100001} คำหรือมากกว่านั้นตามด้วยคำ {0100000}
For example, 3 repetitions: "C{00NNNNN}{1NN0000}{0100000}", 4 repetitions: "C{00NNNNN}{1NN0000}{0100001}{0100000}", and so on.
ที่เพิ่งออกจากรายการที่ถือซ้ำ 2 หมายเลขเดียว ฉันจะเป็นตัวแทนของรูปแบบรายการย่อยประเภท C ที่เป็นไปไม่ได้อีก: "C {0 ??????} {11 ?????} {10 ?????}" มีพื้นที่เหลือเฟือสำหรับจำนวน 7 บิตใน 2 คำแรก แต่รูปแบบนี้ยาวกว่ารายการย่อยที่แสดงถึงซึ่งทำให้สิ่งต่าง ๆ ซับซ้อนขึ้นเล็กน้อย เครื่องหมายคำถามห้าข้อสุดท้ายถือได้ว่าไม่ได้เป็นส่วนหนึ่งของรูปแบบดังนั้นฉันจึงมี: "C {0NNNNNN} {11N ????} 10" เป็นรูปแบบของฉันพร้อมหมายเลขที่จะเก็บซ้ำใน "N "s นั่นยาวเกินไป 2 บิต
ฉันจะต้องยืม 2 บิตและจ่ายคืนจาก 4 บิตที่ไม่ได้ใช้ในรูปแบบนี้ เมื่ออ่านเมื่อพบ "C {0NNNNNN} {11N00AB} 10" จะแสดงผลลัพธ์ 2 หมายเลขของอินสแตนซ์ใน "N" s เขียนทับ "10" ที่ส่วนท้ายด้วยบิต A และ B และย้อนตัวชี้การอ่าน 2 เกร็ด การอ่านแบบทำลายนั้นใช้ได้สำหรับอัลกอริทึมนี้เนื่องจากแต่ละรายการขนาดกะทัดรัดจะได้รับการเดินเพียงครั้งเดียว
เมื่อเขียนรายการย่อยของการซ้ำ 2 ครั้งของตัวเลขเดียวให้เขียน "C {0NNNNNN} 11N00" และตั้งค่าตัวนับบิตที่ยืมเป็น 2 ที่การเขียนทุกครั้งที่ตัวนับบิตที่ยืมมานั้นไม่ใช่ศูนย์จะลดลงสำหรับแต่ละบิตที่เขียนและ "10" ถูกเขียนเมื่อตัวนับจำนวนเยี่ยมชมเป็นศูนย์ ดังนั้น 2 บิตถัดไปที่เขียนจะเข้าไปในช่อง A และ B แล้ว "10" จะถูกส่งไปยังจุดสิ้นสุด
ด้วยค่าส่วนหัว 3 รายการย่อยที่แสดงด้วย "00", "01" และ "1" ฉันสามารถกำหนด "1" ให้กับประเภทรายการย่อยที่ได้รับความนิยมมากที่สุด ฉันจะต้องใช้ตารางเล็ก ๆ ในการแมปค่าส่วนหัวของรายการย่อยกับประเภทรายการย่อยและฉันต้องการตัวนับเหตุการณ์สำหรับแต่ละรายการย่อยเพื่อให้ฉันรู้ว่าการแมปส่วนหัวของรายการย่อยที่ดีที่สุดคืออะไร
กรณีที่เลวร้ายที่สุดการแสดงรายการขนาดกะทัดรัดที่มีประชากรน้อยที่สุดเกิดขึ้นน้อยที่สุดเมื่อประเภทรายการย่อยทั้งหมดได้รับความนิยมเท่ากัน ในกรณีนี้ฉันบันทึก 1 บิตสำหรับทุก ๆ 3 หัวข้อย่อยดังนั้นขนาดรายการคือ 2 * 781250 + 7 * 1000000 - 781250/3 = 8302083.3 บิต การปัดเศษขึ้นเป็นขอบเขตคำ 32 บิตนั่นคือ 8302112 บิตหรือ 1037764 ไบต์
1M ลบ 2k สำหรับสถานะ TCP / IP และบัฟเฟอร์คือ 1022 * 1024 = 1046528 ไบต์ปล่อยให้ฉัน 8764 ไบต์เล่น
แต่กระบวนการเปลี่ยนการแมปส่วนหัวของรายการย่อยนั้นเป็นอย่างไร ในแผนที่หน่วยความจำด้านล่าง "Z" เป็นค่าใช้จ่ายแบบสุ่ม "=" เป็นพื้นที่ว่าง "X" เป็นรายการขนาดกะทัดรัด
ZZZ=====XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
เริ่มอ่านที่ "X" ซ้ายสุดและเริ่มเขียนที่ซ้ายสุด "=" และทำงานได้ทันที เมื่อเสร็จสิ้นรายการขนาดกะทัดรัดจะสั้นกว่าเล็กน้อยและจะอยู่ในตำแหน่งที่ไม่ถูกต้องของหน่วยความจำ:
ZZZXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=======
ดังนั้นฉันจะต้องปัดมันไปทางขวา:
ZZZ=======XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ในกระบวนการเปลี่ยนแปลงการจับคู่ส่วนหัวส่วนหัวย่อยของรายการย่อยมากถึง 1/3 จะเปลี่ยนจาก 1 บิตเป็น 2 บิต ในกรณีที่เลวร้ายที่สุดสิ่งเหล่านี้จะอยู่ที่ส่วนหัวของรายการดังนั้นฉันจะต้องมีพื้นที่เก็บข้อมูลฟรีอย่างน้อย 781250/3 บิตก่อนที่ฉันจะเริ่มต้นซึ่งจะนำฉันกลับไปที่ข้อกำหนดหน่วยความจำของรายการขนาดกะทัดรัดรุ่นก่อนหน้า: (
ในการหลีกเลี่ยงปัญหานั้นฉันจะแบ่งรายการย่อย 781250 ออกเป็น 10 กลุ่มย่อยของรายการย่อย 78125 รายการ แต่ละกลุ่มมีการแมปส่วนหัวของรายการย่อยอิสระ การใช้ตัวอักษร A ถึง J สำหรับกลุ่ม:
ZZZ=====AAAAAABBCCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
แต่ละกลุ่มรายการย่อยย่อขนาดหรือลดขนาดเดิมในระหว่างการเปลี่ยนการจับคู่ส่วนหัวของรายการย่อย:
ZZZ=====AAAAAABBCCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAA=====BBCCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABB=====CCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCC======DDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDD======EEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEE======FFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFF======GGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGG=======HHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHH=======IJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHHI=======JJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ=======
ZZZ=======AAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
การขยายชั่วคราวกรณีที่เลวร้ายที่สุดของกลุ่มรายการย่อยระหว่างการเปลี่ยนแปลงการแมปคือ 78125/3 = 26042 บิตภายใต้ 4k ถ้าฉันอนุญาต 4k บวก 1037764 bytes สำหรับรายการคอมแพคที่เติมเต็มนั่นทำให้ฉัน 8764 - 4096 = 4668 bytes สำหรับ "Z" ในแผนที่หน่วยความจำ
ควรมีมากพอสำหรับตารางการแมปหัวข้อย่อย 10 รายการการนับส่วนหัวรายการย่อย 30 รายการและอีกไม่กี่ตัวนับพอยน์เตอร์และบัฟเฟอร์เล็ก ๆ ที่ฉันต้องการและพื้นที่ที่ฉันใช้โดยไม่สังเกตเช่นพื้นที่สแต็คสำหรับที่อยู่สำหรับเรียกคืน ตัวแปรท้องถิ่น
ส่วนที่ 3 ใช้เวลานานเท่าไหร่ในการรัน
ด้วยรายการขนาดกะทัดรัดที่ว่างเปล่าส่วนหัวรายการ 1 บิตจะถูกใช้สำหรับรายการย่อยที่ว่างเปล่าและขนาดเริ่มต้นของรายการจะเป็น 781250 บิต ในกรณีที่เลวร้ายที่สุดรายการจะเติบโต 8 บิตสำหรับแต่ละหมายเลขที่เพิ่มดังนั้นจำเป็นต้องมีพื้นที่ว่าง 32 + 8 = 40 บิตสำหรับแต่ละหมายเลข 32 บิตที่จะวางไว้ที่ด้านบนสุดของบัฟเฟอร์รายการจากนั้นเรียงลำดับและผสาน ในกรณีที่เลวร้ายที่สุดการเปลี่ยนการแมปส่วนหัวของรายการย่อยในการใช้พื้นที่ 2 รายการ * 781250 + 7 * - 781250/3 บิต
ด้วยนโยบายการเปลี่ยนการแมปส่วนหัวของรายการย่อยหลังจากการผสานที่ห้าทุกครั้งที่มีอย่างน้อย 800,000 หมายเลขในรายการการเรียกใช้กรณีที่แย่ที่สุดจะเกี่ยวข้องกับกิจกรรมการอ่านและเขียนรายการขนาดกะทัดรัดประมาณ 30 ล้านรายการ
ที่มา:
http://nick.cleaton.net/ramsortsol.html