일반 정렬 알고리즘은 일반적으로 정렬 할 데이터 세트와 두 개의 개별 요소를 비교할 수있는 비교기 함수를 사용합니다. 비교기가 순서 관계 ¹이면 알고리즘의 출력은 정렬 된 목록 / 배열입니다.
어떤 정렬 알고리즘이 실제로 주문 관계가 아닌 비교기 (특히 각 비교에서 임의의 결과를 반환하는 비교기)와 함께 작동 하는지 궁금 합니다. “작업”이란 여기서는 입력의 순열을 계속 반환하고 일반적으로 인용 된 시간 복잡성에서 실행한다는 것을 의미합니다 (가장 최악의 시나리오로 항상 저하되거나 무한 루프로 빠지거나 요소가 없음). 그러나 결과의 순서는 정의되지 않습니다. 더 좋은 것은 비교기가 코인 플립 일 때 결과적인 순서는 균일 한 분포입니다.
내 거친 정신 계산에서 병합 정렬이 이것으로 잘 작동하고 동일한 런타임 비용을 유지하고 공정한 임의 순서를 생성하는 것으로 보입니다. 나는 빠른 종류와 같은 것이 퇴화되고, 마무리되지 않을 것이며, 공정하지 않을 것이라고 생각합니다.
임의 비교기와 함께 설명 된대로 다른 정렬 알고리즘 (병합 정렬 제외)이 작동합니까?
-
참고로, 비교기는 적절한 함수 (결정적)이고 주문 관계의 공리를 충족시키는 경우 주문 관계입니다.
- : 그것은 결정적
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
- : 그것은 결정적
(모든 입력 요소가 고유하다고 가정하므로 반사성은 문제가되지 않습니다.)
무작위 비교기는 이러한 규칙을 모두 위반합니다. 그러나 순서 관계는 아니지만 무작위가 아닌 비교기가 있습니다 (예를 들어 하나의 규칙 만 위반하고 세트의 특정 요소에 대해서만 위반할 수 있음).
답변
따라서 기본적으로 다음과 비슷한 비교 함수가 제공되면 평균 경우에서 저하되지 않는 정렬 알고리즘이 있는지 알고 싶습니다.
int Compare(object a, object b) { return Random.Next(-1,1); }
… 여기서 Random.Next ()는 지정된 포괄적 인 하한과 상한 사이에서 무작위로 생성 된 정수를 생성하는 메소드입니다.
실제로 대부분의 기본 정렬 알고리즘은 다음 두 가지 조건 중 하나 이상을 준수하기 때문에 평균 사례에 따라 수행됩니다.
- 두 가지 고유 한 요소 간의 비교는 결코 두 번이나 이루어지지 않습니다.
- 정렬의 각 반복에서 적어도 하나의 요소의 올바른 위치가 결정되어 요소가 다시 비교되지 않습니다.
예를 들어, 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
). 비교기 결과의 최대 절대 값이 증가함에 따라 하나의 비교에서 음수 또는 0을 반환 할 확률이 0.5로 감소하여 알고리즘을 종료 할 가능성이 훨씬 낮아집니다 (99 코인이 모든 착륙 헤드를 뒤집을 가능성) 어느는이 귀결 무엇 기본적 1 1.2 * 10 30 )
오랜 시간 편집 : 무작위 비교기를 포함하지 말아야 할 일의 예로 특별히 설계된 몇 가지 “정렬”이 있습니다. 아마도 가장 유명한 것은 BogoSort 일 것입니다. “목록이 제공되면 목록이 순서가 맞지 않으면 목록을 섞고 다시 확인하십시오.” 이론적으로는됩니다 결국 바로 위의 “최적화되지 않은 거품 정렬”같은 값의 오른쪽 순열에 맞았지만 평균의 경우는 충분히 무작위 순열 후 (N! / 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 []
이 균일 한 비교 함수가 주어지면 다른 알고리즘에 대한 평균 실행 시간을 계산하는 것이 재미있을 것입니다.
답변
공정한 무작위 비교기를 가진 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
답변
Christiansen, Danilenko 및 Dylus의 모든 종류의 순열 (함수 진주) 에서 매우 많은 관련 질문에 대한 답변이 제공됩니다. 그들은 리스트 모나드 에서 정렬 알고리즘을 실행하는데 , 이것은 비결정론 을 본질적으로 시뮬레이션하여 주어진 입력리스트의 모든 순열을 반환합니다. 흥미로운 속성은 각 순열이 정확히 한 번만 반환된다는 것입니다.
초록에서 인용 :
…
본 논문에서는 비결정론과 다른 관점에서의 정렬의 조합을 살펴 본다 : 분류 함수가 주어지면, 비결정론 술어에 적용하여 입력 목록의 순열을 열거하는 함수를 얻는다. 우리는 정렬 알고리즘과 술어의 필수 속성의 맨 아래에 도달하고 모델링 된 비결정론의 변형에 대해 논의합니다.
또한, 어떤 정렬 함수를 사용하든 해당 순열 함수는 입력 목록의 모든 순열을 열거한다는 이론을 공식화하고 증명합니다. 우리는 선언문을 증명하기 위해 함수 유형에서만 파생 된 자유 정리를 사용합니다.