การเรียงลำดับอัลกอริธึมที่ยอมรับตัวเปรียบเทียบแบบสุ่ม


22

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

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

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

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


  1. สำหรับการอ้างอิงตัวเปรียบเทียบคือลำดับความสัมพันธ์ถ้ามันเป็นฟังก์ชันที่เหมาะสม (กำหนดขึ้น) และสอดคล้องกับหลักการของความสัมพันธ์ของลำดับ:

    • มันเป็นการกำหนดขึ้น: compare(a,b)สำหรับสิ่งใดสิ่งหนึ่งaและbส่งกลับผลลัพธ์เดียวกันเสมอ
    • มันเป็นสกรรมกริยา: compare(a,b) and compare(b,c) implies compare( a,c )
    • มันเป็น antisymmetric compare(a,b) and compare(b,a) implies a == b

(สมมติว่าองค์ประกอบอินพุตทั้งหมดนั้นแตกต่างกันดังนั้นการสะท้อนกลับจึงไม่เป็นปัญหา)

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


(1) คุณหมายถึงอะไรโดยฟังก์ชั่นเปรียบเทียบความเสถียร? (2) คำพ้องความหมาย "ไม่เสถียร" และ "สุ่ม" หรือไม่?
Tsuyoshi Ito

"ทำงานที่ซับซ้อนเวลาที่ยกมาโดยทั่วไปของพวกเขา (เมื่อเทียบกับการลดลงของสถานการณ์กรณีที่เลวร้ายที่สุด" - โดยทั่วไปแล้วเวลาซับซ้อนอ้างถึงเป็นกรณีที่เลวร้ายที่สุด! "การสั่งซื้อจะเป็นการสั่งซื้อแบบสุ่มยุติธรรม" - BY "ยุติธรรม" คุณคิดว่าตัวเปรียบเทียบนั้นเหมือนกันหรือไม่
ราฟาเอล

อาจไม่ได้อยู่ในทฤษฎีอย่างเป็นทางการ แต่ในทางปฏิบัติ (ภาษาการเขียนโปรแกรม) หลายสิ่งจะถูกยกมาในเวลาตัดจำหน่าย ยกตัวอย่างเช่น quicksort มักจะแสดงให้เห็นว่าแต่จริง ๆ แล้ว2) O ( n 2 )O(logn)O(n2)
edA-qa mort-ora-y

4
@ EDA-qamort-Ora-Y: (1) คุณเฉลี่ยไม่n) (2) นั่นไม่ใช่ความหมายของ " เวลาตัดจำหน่าย " คุณหมายถึง " เวลาที่คาดหวัง " หรือน้อยกว่าอย่างเป็นทางการ "เวลาปกติ" O ( log n )O(nlogn)O(logn)
JeffE

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

คำตอบ:


13

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

int Compare(object a, object b) { return Random.Next(-1,1); }

... โดยที่ Random.Next () เป็นวิธีการบางอย่างที่จะสร้างจำนวนเต็มที่สร้างแบบสุ่มระหว่างขอบเขตรวมล่างและบนที่ระบุ

คำตอบคือจริง ๆ แล้วว่าอัลกอริธึมการเรียงลำดับพื้นฐานส่วนใหญ่จะดำเนินการตามกรณีเฉลี่ยของพวกเขาเพราะพวกเขาเชื่อฟังอย่างน้อยหนึ่งในสองเงื่อนไขต่อไปนี้:

  1. การเปรียบเทียบระหว่างสององค์ประกอบที่ไม่ซ้ำกันจะไม่ทำสองครั้งในการเรียงลำดับและ / หรือ
  2. ในแต่ละการวนซ้ำของการเรียงลำดับตำแหน่งที่ถูกต้องขององค์ประกอบอย่างน้อยหนึ่งจะถูกกำหนดและเพื่อที่จะไม่เปรียบเทียบองค์ประกอบอีกครั้ง

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

MergeSort ทำตามเงื่อนไขที่ 1 แต่ไม่ใช่ 2 เมื่ออาร์เรย์ย่อยถูกรวมเข้าด้วยกันองค์ประกอบในอาเรย์ย่อยเดียวกัน (ทางซ้ายหรือขวา) จะไม่ถูกนำมาเปรียบเทียบกันเนื่องจากมันได้ถูกกำหนดแล้วว่าองค์ประกอบด้านอาเรย์นั้นอยู่ในลำดับเดียวกัน อัลกอริทึมจะเปรียบเทียบองค์ประกอบที่ยังไม่ได้รวมน้อยที่สุดของแต่ละ subarray กับอีกอันเพื่อพิจารณาว่าอันไหนที่น้อยกว่าและควรดำเนินการต่อไปในรายการที่ผสาน ซึ่งหมายความว่าวัตถุ A และ B ที่ไม่ซ้ำกันสองรายการใด ๆ จะถูกนำมาเปรียบเทียบกันสูงสุดครั้งเดียว แต่ดัชนี "ขั้นสุดท้าย" ขององค์ประกอบที่กำหนดในคอลเลกชันเต็มไม่เป็นที่รู้จักจนกว่าอัลกอริทึมจะเสร็จสมบูรณ์

InsertionSort ปฏิบัติตามเงื่อนไขที่ 1 เท่านั้นแม้ว่ากลยุทธ์โดยรวมและความซับซ้อนจะมีลักษณะเหมือน SelectionSort แต่ละองค์ประกอบที่ไม่เรียงลำดับจะถูกเปรียบเทียบกับองค์ประกอบที่เรียงลำดับมากที่สุดก่อนจนกระทั่งพบว่ามีองค์ประกอบน้อยกว่าองค์ประกอบภายใต้การตรวจสอบ องค์ประกอบถูกแทรกที่จุดนั้นจากนั้นองค์ประกอบถัดไปจะถูกพิจารณา ผลลัพธ์คือลำดับสัมพัทธ์ของ A และ B ใด ๆ ถูกกำหนดโดยการเปรียบเทียบหนึ่งครั้งและการเปรียบเทียบเพิ่มเติมระหว่าง A และ B นั้นจะไม่ดำเนินการ แต่ตำแหน่งสุดท้ายขององค์ประกอบใด ๆ จะไม่สามารถทราบได้จนกว่าจะมีการพิจารณาองค์ประกอบทั้งหมด

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

(2/3)N1) เมื่อค่าสัมบูรณ์สูงสุดของผลลัพธ์ของตัวเปรียบเทียบเพิ่มขึ้นความน่าจะเป็นสำหรับการเปรียบเทียบใด ๆ ที่จะส่งกลับค่าลบหรือศูนย์ลดลงเป็น 0.5 ทำให้มีโอกาสสิ้นสุดอัลกอริทึมที่มีโอกาสน้อยกว่ามาก (โอกาส 99 เหรียญพลิกหัว Landing ทั้งหมด ซึ่งโดยทั่วไปแล้วสิ่งที่สิ่งนี้ทำให้เกิดขึ้นคือ 1 ใน 1.2 * 10 30 )

แก้ไขเวลาที่ยาวนาน:มี "แปลก ๆ " บางอย่างที่ออกแบบมาโดยเฉพาะเพื่อเป็นตัวอย่างของสิ่งที่ไม่ควรทำซึ่งรวมตัวเปรียบเทียบแบบสุ่ม บางทีชื่อเสียงที่สุดคือ BogoSort "กำหนดรายการถ้าไม่มีรายการตามลำดับให้สลับรายการและตรวจสอบอีกครั้ง" ในทางทฤษฎีมันจะในที่สุดก็ตีในการเปลี่ยนแปลงทางด้านขวาของค่าเช่นเดียวกับ "ไม่เหมาะสมการเรียงลำดับแบบฟอง" ข้างต้น แต่กรณีเฉลี่ยอยู่ที่ปัจจัยเวลา (N! / 2) และเนื่องจากปัญหาวันเกิด (หลังจากพีชคณิตแบบสุ่มพอที่คุณ มีแนวโน้มที่จะพบกับการเรียงสับเปลี่ยนที่ซ้ำกันมากกว่าที่ไม่ซ้ำกัน) มีความเป็นไปได้ที่ไม่ใช่ศูนย์ของอัลกอริทึมที่ไม่เคยทำจนจบอย่างเป็นทางการ


เงื่อนไข 2 จะครอบคลุมการเรียงลำดับอย่างรวดเร็วหรือไม่ หรือมันจะเป็นเงื่อนไขที่สามมากขึ้นเกี่ยวกับการทำซ้ำแต่ละครั้งจะเล็กกว่าครั้งสุดท้าย
edA-qa mort-ora-y

QuickSort จะอยู่ในใจของฉันทั้งสองเงื่อนไข ใน QuickSort ที่มีประสิทธิภาพคุณสามารถเลือกเดือยและเปรียบเทียบแต่ละองค์ประกอบกับมันและสลับองค์ประกอบที่อยู่ใน "ด้าน" ผิดของเดือย เมื่อมีการจัดเรียงองค์ประกอบแล้วฟังก์ชันจะส่งกลับ QuickSort (ซ้าย) + pivot + QuickSort (ขวา) และ pivot จะไม่ถูกส่งผ่านไปยังระดับที่ต่ำกว่า ดังนั้นเงื่อนไขทั้งสองจึงเป็นจริง คุณไม่เคยเปรียบเทียบ a และ b ที่ไม่ซ้ำกันมากกว่าหนึ่งครั้งและคุณได้กำหนดดัชนีของ pivot ตามเวลาที่คุณจัดเรียงองค์ประกอบอื่น ๆ ให้เสร็จ
KeithS

คำตอบที่ดี แต่ฉันไม่เห็นด้วยกับคุณเกี่ยวกับ BubbleSort เมื่อใช้เครื่องมือเปรียบเทียบที่สอดคล้องกันที่ i-th ซ้ำ BubbleSort รู้ว่าองค์ประกอบสุดท้ายของ i-1 อยู่ในตำแหน่งสุดท้ายของพวกเขาและการใช้งานที่เหมาะสมของ BubbleSort จะผ่านองค์ประกอบที่น้อยลงในแต่ละการทำซ้ำดังนั้นมันควรหยุดหลังจาก n ซ้ำ .
Boris Trayvas

หลังจากนั้นฉันก็คิดว่าฉันจะเห็นด้วยกับคุณ หลังจากผ่าน X ค่า X ที่ยิ่งใหญ่ที่สุดอยู่ในตำแหน่งที่เหมาะสมดังนั้นคุณสามารถลดพื้นที่ปัญหาในแต่ละรอบได้ดังนั้นอัลกอริทึมที่มีประสิทธิภาพจะปฏิบัติตามเงื่อนไข 2 ฉันจะแก้ไข
KeithS

คุณต้องระวังการใช้ Quicksort อาจมีข้อสันนิษฐานว่าการค้นหาองค์ประกอบไม่น้อยกว่าเดือยจะสิ้นสุดลงเมื่อเราพบกับเดือยหรือองค์ประกอบที่มากกว่าเดือย ที่จะไม่เป็นกรณีจำเป็น
gnasher729

10

O(n2)

n


แก้ไข: ปัญหาน่าสนใจกว่าที่ฉันคิดไว้ก่อนดังนั้นนี่เป็นความคิดเห็นเพิ่มเติม:

comparecompare(x,y)=true1/2false1/2

insert x [] = [x]
insert x y:ys = if x < y then x:y:ys
                else y:insert x ys

sort_aux l e = match l with
                 [] -> e
                 x:xs -> sort_aux xs (insert x ys)

sort l = sort_aux l []

k=1nf(k)nlf(k)insertk:

compare

i=1ki2ii=1i2i=2

O(2n)O(n2)

มันจะสนุกที่จะคำนวณเวลาทำงานเฉลี่ยสำหรับอัลกอริทึมอื่น ๆ ที่แตกต่างกันเนื่องจากฟังก์ชันการเปรียบเทียบชุดนี้


Quicksort สามารถทำการเปรียบเทียบซ้ำหากองค์ประกอบเดียวกันถูกเลือกเป็น pivot มากกว่าหนึ่งครั้ง (อาจเกิดขึ้นหลายครั้งในรายการ)
กราฟิลส์

2
@ ราฟาเอล: คำที่ฉันเลือกไม่ดี: ฉันหมายถึงการเปรียบเทียบซ้ำระหว่างการเกิดขึ้นขององค์ประกอบซึ่งไม่ได้เกิดขึ้นมากกว่าหนึ่งครั้งใน Quicksort
ดี้

1
@Gilles: ฉันอาจจะผิด แต่ฉันไม่เชื่อว่า transitivity ของการเปรียบเทียบมีความสำคัญต่อรันไทม์ของอัลกอริทึมการเรียงลำดับส่วนใหญ่; ความถูกต้องแน่นอน แต่นั่นไม่ใช่เป้าหมายของคำถาม
ดี้

@Gilles: OP ไม่ได้ถามเกี่ยวกับอัลกอริทึมที่เรียงลำดับจริง เขาถามว่าเกิดอะไรขึ้นกับอัลกอริธึมการจัดเรียงมาตรฐานเมื่อการเปรียบเทียบทั้งหมดถูกแทนที่ด้วยการโยนเหรียญ อัลกอริทึมที่เกิดขึ้นไม่ได้เรียงลำดับ (ยกเว้นความน่าจะเป็นเล็ก ๆ ) แต่อัลกอริทึมยังคงถูกกำหนดไว้อย่างดี
JeffE

@JeffE ฉันเข้าใจแล้วว่าตอนนี้ นั่นไม่ใช่วิธีที่ฉันอ่านคำถามเริ่มแรก แต่ให้ความเห็นของผู้ถามนั่นคือสิ่งที่มีความหมาย
Gilles 'หยุดความชั่วร้าย'

2

การผสานกับตัวเปรียบเทียบแบบสุ่มอย่างยุติธรรมนั้นไม่ยุติธรรม ฉันไม่มีหลักฐาน แต่ฉันมีหลักฐานเชิงประจักษ์ที่แข็งแกร่งมาก (ยุติธรรมหมายถึงการกระจายอย่างสม่ำเสมอ)

module Main where

import Control.Monad
import Data.Map (Map)
import qualified Data.Map as Map
import System.Random (randomIO)

--------------------------------------------------------------------------------

main :: IO ()
main = do
  let xs = [0..9]
  xss <- replicateM 100000 (msortRand xs)
  print $ countFrequencies xss

msortRand :: [a] -> IO [a]
msortRand = msort (\_ _ -> randomIO)

countFrequencies :: (Ord a) => [[a]] -> [Map a Int]
countFrequencies [] = []
countFrequencies xss = foldr (\k m -> Map.insertWith (+) k 1 m) Map.empty ys : countFrequencies wss
  where
    ys = map head xss
    zss = map tail xss
    wss = if head zss == []
      then []
      else zss

--------------------------------------------------------------------------------

msort :: (Monad m) => (a -> a -> m Bool) -> [a] -> m [a]
msort (<) [] = return []
msort (<) [x] = return [x]
msort (<) xs = do
  ys' <- msort (<) ys
  zs' <- msort (<) zs
  merge (<) ys' zs'
  where
    (ys, zs) = split xs

merge :: (Monad m) => (a -> a -> m Bool) -> [a] -> [a] -> m [a]
merge (<) [] ys = return ys
merge (<) xs [] = return xs
merge (<) (x:xs) (y:ys) = do
  bool <- x < y
  if bool
    then liftM (x:) $ merge (<) xs (y:ys)
        else liftM (y:) $ merge (<) (x:xs) ys

split :: [a] -> ([a], [a])
split [] = ([], [])
split [x] = ([x], [])
split (x:y:zs) = (x:xs, y:ys)
  where
    (xs, ys) = split zs

Haskell หรือ Caml อยู่ในแฟชั่นตอนนี้หรือไม่?
Yai0Phah

ฉันไม่รู้. แต่ Haskell เป็นภาษาโปรดของฉันดังนั้นฉันจึงตั้งโปรแกรมไว้ในนั้น การจับคู่รูปแบบทำให้สิ่งนี้ง่ายขึ้น
Thomas Eding

0

คำถามที่เกี่ยวข้องมากมีคำตอบในทุกประเภทของพีชคณิต (Functional Pearl)โดย Christiansen, Danilenko และ Dylus พวกเขาเรียกใช้อัลกอริธึมการเรียงลำดับในรายการ monadซึ่งเป็นการจำลองการไม่กำหนดระดับโดยส่งกลับการเรียงสับเปลี่ยนทั้งหมดของรายการอินพุตที่กำหนด คุณสมบัติที่น่าสนใจคือการสับเปลี่ยนแต่ละครั้งจะถูกส่งคืนเพียงครั้งเดียว

ข้อความจากบทคัดย่อ:

...

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

ยิ่งไปกว่านั้นเรากำหนดและพิสูจน์ทฤษฎีบทที่ระบุว่าไม่ว่าเราจะใช้ฟังก์ชันการเรียงลำดับแบบใดฟังก์ชันการเรียงลำดับที่สอดคล้องกันจะระบุการเรียงลำดับทั้งหมดของรายการอินพุต เราใช้ทฤษฏีอิสระซึ่งมาจากประเภทของฟังก์ชั่นเพียงอย่างเดียวเพื่อพิสูจน์คำพูด

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