무작위 비교기를 허용하는 정렬 알고리즘


22

일반 정렬 알고리즘은 일반적으로 정렬 할 데이터 세트와 두 개의 개별 요소를 비교할 수있는 비교기 함수를 사용합니다. 비교기가 순서 관계 ¹이면 알고리즘의 출력은 정렬 된 목록 / 배열입니다.

어떤 정렬 알고리즘이 실제로 주문 관계가 아닌 비교기 (특히 각 비교에서 임의의 결과를 반환하는 비교기)와 함께 작동 하는지 궁금 합니다. "작업"이란 여기서는 입력의 순열을 계속 반환하고 일반적으로 인용 된 시간 복잡성에서 실행한다는 것을 의미합니다 (가장 최악의 시나리오로 항상 저하되거나 무한 루프로 빠지거나 요소가 없음). 그러나 결과의 순서는 정의되지 않습니다. 더 좋은 것은 비교기가 코인 플립 일 때 결과적인 순서는 균일 한 분포입니다.

내 거친 정신 계산에서 병합 정렬이 이것으로 잘 작동하고 동일한 런타임 비용을 유지하고 공정한 임의 순서를 생성하는 것으로 보입니다. 나는 빠른 종류와 같은 것이 퇴화되고, 마무리되지 않을 것이며, 공정하지 않을 것이라고 생각합니다.

임의 비교기와 함께 설명 된대로 다른 정렬 알고리즘 (병합 정렬 제외)이 작동합니까?


  1. 참고로, 비교기는 적절한 함수 (결정적)이고 주문 관계의 공리를 충족시키는 경우 주문 관계입니다.

    • : 그것은 결정적 compare(a,b)특정을 위해 a그리고 b항상 같은 결과를 반환합니다.
    • 전 이적입니다. compare(a,b) and compare(b,c) implies compare( a,c )
    • 비대칭이다 compare(a,b) and compare(b,a) implies a == b

(모든 입력 요소가 고유하다고 가정하므로 반사성은 문제가되지 않습니다.)

무작위 비교기는 이러한 규칙을 모두 위반합니다. 그러나 순서 관계는 아니지만 무작위가 아닌 비교기가 있습니다 (예를 들어 하나의 규칙 만 위반하고 세트의 특정 요소에 대해서만 위반할 수 있음).


(1) 비교 기능이 안정적이라는 것은 무엇을 의미합니까? (2)“안정하지 않은”과“무작위”는 동의어인가?
Ito Tsuyoshi

"가장 까다로운 시나리오와 비교할 때 일반적으로 인용 된 시간 복잡성에서 실행"-일반적으로 인용 된 시간 복잡성 최악의 경우입니다! "주문은 공정한 무작위 주문일 것입니다." 비교기 역시 균일하다고 가정하십니까?
Raphael

아마도 공식적인 이론이 아니라 실제로 (프로그래밍 언어) 많은 것들이 상각 된 시간에 인용됩니다. 예를 들어, quicksort는 종종 되지만 실제로는 O ( n 2 ) 입니다. O(logn)O(n2)
edA-qa mort-ora-y

4
(1) 평균 : EDA-qamort-ORA-Y @ 이 아닌 O ( 로그 N ) . (2) " 할부 상환 시간 "이 의미하는 것은 아닙니다 . " 예상 시간 "또는 덜 공식적으로 "일반적인 시간"을 의미합니다. O(nlogn)O(logn)
JeffE

1
아무도 위의 제기 된 더 흥미로운 질문을 해결하지 못했습니다 : 비교 알고리즘이 동전 뒤집기이면 어떤 정렬 알고리즘 (있는 경우)이 속성을 가지며 그 결과는 균일 한 순열입니다.
Joe

답변:


13

따라서 기본적으로 다음과 비슷한 비교 함수가 제공되면 평균 경우에서 저하되지 않는 정렬 알고리즘이 있는지 알고 싶습니다.

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

... 여기서 Random.Next ()는 지정된 포괄적 인 하한과 상한 사이에서 무작위로 생성 된 정수를 생성하는 메소드입니다.

실제로 대부분의 기본 정렬 알고리즘은 다음 두 가지 조건 중 하나 이상을 준수하기 때문에 평균 사례에 따라 수행됩니다.

  1. 두 가지 고유 한 요소 간의 비교는 결코 두 번이나 이루어지지 않습니다.
  2. 정렬의 각 반복에서 적어도 하나의 요소의 올바른 위치가 결정되어 요소가 다시 비교되지 않습니다.

예를 들어, SelectionSort는 정렬되지 않은 요소의 하위 목록을 반복하고 "가장 작은"및 / 또는 "가장 큰"요소를 찾은 다음 (각 요소를 지금까지 가장 큰 요소와 비교하여) 올바른 위치에 놓고 반복합니다. 결과적으로 결정적이지 않은 비교기조차도 모든 반복이 끝날 때마다 알고리즘은 최소 또는 최대라고 생각하는 값을 찾은 다음 결정하려고하는 위치의 요소와 교체하며 결코 고려하지 않습니다. 그러나이 프로세스 동안 A와 B를 여러 번 비교할 수 있습니다 (가장 극단적 인 예로, 역순으로 정렬 된 배열에서 SelectionSort의 여러 패스를 고려하여 조건 1을 위반 함). .

MergeSort는 조건 1에 따르지만 2에는 맞지 않습니다. 하위 배열이 병합 될 때 동일한 하위 배열 (왼쪽 또는 오른쪽)의 요소는 배열의 해당 요소가 순서대로 정렬 된 것으로 이미 결정 되었기 때문에 서로 비교되지 않습니다. 이 알고리즘은 각 하위 배열의 병합되지 않은 요소 중 가장 적은 요소 만 다른 요소와 비교하여 병합 된 목록에서 다음 중 더 적은 요소를 결정합니다. 즉, 두 개의 고유 한 객체 A와 B가 서로 최대 한 번 비교되지만 전체 컬렉션에서 지정된 요소의 "최종"인덱스는 알고리즘이 완료 될 때까지 알 수 없습니다.

InsertionSort는 전반적인 전략과 복잡성이 SelectionSort와 비슷해 보이지만 Condition 1 만 준수합니다. 정렬되지 않은 각 요소는 검사중인 요소보다 작은 요소가 발견 될 때까지 가장 먼저 정렬 된 요소와 비교됩니다. 해당 시점에 요소가 삽입 된 후 다음 요소가 고려됩니다. 결과적으로 A와 B의 상대 순서는 한 번의 비교로 결정되며 A와 B 사이의 추가 비교는 수행되지 않지만 모든 요소가 고려 될 때까지 요소의 최종 위치를 알 수 없습니다.

QuickSort는 두 가지 모두를 준수 합니다정황. 각각의 레벨에서, "왼쪽"측이 피봇보다 작은 요소를 포함하고 "오른쪽"측이 피봇보다 큰 요소를 포함하도록 피봇이 선택되고 배열된다. 해당 레벨의 결과는 QuickSort (왼쪽) + 피벗 + QuickSort (오른쪽)이며 기본적으로 피벗 요소의 위치가 알려져 있음을 의미합니다 (왼쪽의 길이보다 하나의 색인이 더 크다). 피벗은 다른 요소와 비교되지 않습니다. 피벗으로 선택된 후 (이전 피벗 요소와 비교되었을 수 있지만 해당 요소는 알려진 것으로 하위 배열에 포함되지 않음) 피벗의 반대쪽에있는 A 및 B는 절대로 비교했다. 순수한 QuickSort의 대부분의 구현에서 기본 사례는 하나의 요소이며,이 시점에서 현재 색인은 최종 색인이며 더 이상 비교되지 않습니다.

내가 생각할 수있는 유일한 비교 종류는 최적화되지 않은 BubbleSort입니다. 정렬이 X 패스를 실행 한 후 X 가장 큰 요소가 올바른 위치에 있음을 승인하지 않거나 "더블 체크"패스를 사용하여 목록이 정렬되었는지 확인하는 경우 정렬은 "완료"로 간주됩니다. 임의의 비교가 더 교환이 수행되지 않음 따라서 모든 인접한 두 패스 동안 목록의 요소 (진정한 랜덤 경우, 확률로 발생할 수있는 이벤트를 리턴 -1 또는 0했다 , 비교적 대해 25 개 요소의 작은 목록 (2000 개 확률 중 하나), 100 개 요소의 경우 확률은 3.7 * 10 -18(2/3)N1). 비교기 결과의 최대 절대 값이 증가함에 따라 하나의 비교에서 음수 또는 0을 반환 할 확률이 0.5로 감소하여 알고리즘을 종료 할 가능성이 훨씬 낮아집니다 (99 코인이 모든 착륙 헤드를 뒤집을 가능성) 어느는이 귀결 무엇 기본적 1 1.2 * 10 30 )

오랜 시간 편집 : 무작위 비교기를 포함하지 말아야 할 일의 예로 특별히 설계된 몇 가지 "정렬"이 있습니다. 아마도 가장 유명한 것은 BogoSort 일 것입니다. "목록이 제공되면 목록이 순서가 맞지 않으면 목록을 섞고 다시 확인하십시오." 이론적으로는됩니다 결국 바로 위의 "최적화되지 않은 거품 정렬"같은 값의 오른쪽 순열에 맞았지만 평균의 경우는 충분히 무작위 순열 후 (N! / 2), 때문에 생일 문제의 계승 시간 (당신 고유 한 것보다 중복 순열이 발생할 가능성이 높아집니다.


조건 2도 빠른 정렬을 포함합니까? 또는 각 반복이 마지막보다 작다는 세 번째 조건 일 수 있습니다.
edA-qa mort-ora-y

QuickSort는 제 생각에 두 가지 조건을 모두 다룰 것입니다. 효율적인 QuickSort에서는 피벗을 선택한 다음 각 요소를 비교하고 피벗의 "측면"에있는 요소를 교체합니다. 요소가 정렬되면 함수는 QuickSort (왼쪽) + 피벗 + QuickSort (오른쪽)를 반환하며 피벗은 하위 수준으로 전달되지 않습니다. 따라서 두 조건이 모두 맞습니다. 고유 한 a와 b를 두 번 이상 비교하지 않으며 다른 요소 정렬을 완료 할 때까지 피벗의 인덱스를 결정했습니다.
KeithS 2016 년

좋은 답변이지만 BubbleSort에 대해서는 동의하지 않습니다. 일관된 비교기를 사용할 때 i 번째 반복에서 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)

이 균일 한 비교 함수가 주어지면 다른 알고리즘에 대한 평균 실행 시간을 계산하는 것이 재미있을 것입니다.


동일한 요소가 피벗으로 두 번 이상 선택된 경우 빠른 정렬이 비교를 반복 할 수 있습니다 (목록에서 여러 번 발생할 수 있음).
Raphael

2
@Raphael : 내가 선택한 단어가 잘못 되었습니다. Quicksort에서 두 번 이상 발생하지 않는 요소 발생 간에 반복 비교를 의미 했습니다.
코디

1
@Gilles : 틀릴 수도 있지만, 비교의 전이 가 대부분의 정렬 알고리즘 의 런타임 에 중요하다고 생각하지 않습니다 . 정확성은 확실하지만 문제의 대상은 아닙니다.
코디

@Gilles : OP는 실제로 정렬되는 알고리즘에 대해 묻지 않습니다. 그는 모든 비교가 코인 플립으로 대체 될 때 표준 정렬 알고리즘이 어떻게되는지 묻고 있습니다. 결과 알고리즘은 정렬되지 않지만 (작은 확률을 제외하고) 여전히 잘 정의 된 알고리즘입니다.
JeffE

@JeffE 나는 그것을 이해한다. 그것이 내가 처음에 질문을 읽은 방법이 아니지만, 질문자의 의견을 감안할 때, 그 의미였습니다.
Gilles 'SO- 악마 그만해'

2

공정한 무작위 비교기를 가진 Mergesort는 공정하지 않습니다. 나는 증거가 없지만, 매우 강한 경험적 증거를 가지고 있습니다. (공정함은 균일하게 분포됨을 의미합니다.)

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은 제가 가장 좋아하는 언어이므로이 언어를 프로그래밍했습니다. 패턴 매칭이 더 쉬워졌습니다.
토마스 에딩

0

Christiansen, Danilenko 및 Dylus의 모든 종류의 순열 (함수 진주) 에서 매우 많은 관련 질문에 대한 답변이 제공됩니다. 그들은 리스트 모나드 에서 정렬 알고리즘을 실행하는데 , 이것은 비결정론 을 본질적으로 시뮬레이션하여 주어진 입력리스트의 모든 순열을 반환합니다. 흥미로운 속성은 각 순열이 정확히 한 번만 반환된다는 것입니다.

초록에서 인용 :

...

본 논문에서는 비결정론과 다른 관점에서의 정렬의 조합을 살펴 본다 : 분류 함수가 주어지면, 비결정론 술어에 적용하여 입력 목록의 순열을 열거하는 함수를 얻는다. 우리는 정렬 알고리즘과 술어의 필수 속성의 맨 아래에 도달하고 모델링 된 비결정론의 변형에 대해 논의합니다.

또한, 어떤 정렬 함수를 사용하든 해당 순열 함수는 입력 목록의 모든 순열을 열거한다는 이론을 공식화하고 증명합니다. 우리는 선언문을 증명하기 위해 함수 유형에서만 파생 된 자유 정리를 사용합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.