"게으른 정렬"구현


44

숫자 목록을 정렬해야하지만 매우 게으르다. 모든 숫자가 오름차순이 될 때까지 모든 숫자를 바꾸는 방법을 이해하는 것은 정말 어렵 기 때문에 새로운 목록이 정렬되도록 보장하는 자체 알고리즘을 만들었습니다 ¹. 작동 방식은 다음과 같습니다.

크기 N 의 목록을 보려면 N-1 반복 이 필요합니다 . 반복 할 때마다

  • N 번째 숫자가 N + 1 번째 숫자보다 작은 지 확인하십시오 . 그렇다면이 두 숫자는 이미 정렬 된 것이므로이 반복을 건너 뛸 수 있습니다.

  • 그렇지 않은 경우이 두 숫자가 순서가 될 때까지 첫 번째 N 숫자 를 계속 감소시켜야합니다 .

구체적인 예를 들어 봅시다. 입력이

10 5 7 6 1

첫 번째 반복에서는 10과 5를 비교합니다. 10은 5보다 크므로 작을 때까지 줄입니다.

4 5 7 6 1

이제 우리는 5와 7을 비교합니다. 5는 7보다 작으므로이 반복에 대해 아무 것도 할 필요가 없습니다. 그래서 우리는 다음으로 가서 7과 6을 비교합니다. 7은 6보다 큽니다. 그래서 우리는 6보다 작을 때까지 처음 세 숫자를 줄입니다.

2 3 5 6 1

이제 6과 1을 비교합니다. 다시 6은 1보다 큽니다. 따라서 첫 4 개의 숫자가 1보다 작을 때까지 감소하면 다음과 같이됩니다.

-4 -3 -1 0 1

그리고 우리는 끝났습니다! 이제 우리의 목록은 완벽하게 정렬 된 순서입니다. 그리고 일을 더 잘하기 위해 우리는 N-1 번의 목록을 반복 해야 했으므로이 알고리즘은 O (N-1) 시간으로 목록을 정렬하므로 가장 빠른 알고리즘이 확실합니다 .²

오늘 당신의 도전은이 지연 정렬을 구현하는 것입니다. 프로그램이나 함수에는 원하는 표준 형식의 정수 배열이 제공되며이 지연 정렬을 수행하고 새로운 "정렬 된" 목록을 반환해야 합니다. 배열은 절대 비어 있거나 정수가 아닙니다.

여기 몇 가지 예가 있어요.

Input: 10 5 7 6 1
Output: -4 -3 -1 0 1

Input: 3 2 1
Output: -1 0 1

Input: 1 2 3
Output: 1 2 3

Input: 19
Output: 19

Input: 1 1 1 1 1 1 1 1 1 
Output: -7 -6 -5 -4 -3 -2 -1 0 1 

Input: 5 7 11 6 16 2 9 16 6 16
Output: -27 -25 -21 -20 -10 -9 -2 5 6 16

Input: -8 17 9 7
Output: -20 5 6 7

항상 그렇듯이 이것은 이므로 가능한 가장 짧은 프로그램을 작성하십시오!


¹ 이것이 의미하는 것처럼 들리는 것은 아니지만 기술적으로 사실입니다.

² 난 농담이야, 제발 날 미워 하지마


6
나는이 방법으로 그것을 할 경우에 당신이 게으른 생각
요 르그 Hülsermann에게

4
JörgHülsermann @ 아니라 일부 정수는 더 나은에서 바로 위에 물건을 벗고, 너무 무거운 ... 정확히 분위기에 같은 무게를 수행 할 수 있습니다
에릭 Outgolfer

21
<sarcasm>이 정렬 알고리즘은 실제로 O(N^2)목록에서 이전에 액세스 한 항목을 모두 줄여야하기 때문에 시간 복잡도 에 여전히 영향 을 미칩니다. 대신 목록을 거꾸로 살펴보고 필요한 경우 단계 당 하나의 숫자 만 줄이는 것이 좋습니다 . 이것은 당신에게 진정한 O(N)복잡성 을 줄 것입니다 ! </sarcasm>
Value Ink

1
O(n^2)메모리 액세스 측면에서 @ValueInk 이지만 O(n)비교 용이 아닙니까?
Cole Johnson

7
@ColeJohnson은 기술적으로는 맞지만 시간 복잡성은 알고리즘의 모든 단계를 고려해야합니다. 모든 반복에서 이전의 모든 색인을 반복해야하므로 여전히 나옵니다 O(N^2).
Value Ink

답변:


12

젤리 ,  14 12 11  9 바이트

ETH 프로덕션 덕분에 -2 바이트 (최소 dyad 사용 «)

I’«0Ṛ+\Ṛ+

정수 목록을 가져오고 리턴하는 모나드 링크.

온라인으로 사용해보십시오! 또는 테스트 스위트를 참조하십시오.

나는 이것이 이것이 Lazy ™라고 생각하지 않습니다!

어떻게?

I’«0Ṛ+\Ṛ+ - Link: list of integers, a              e.g. [ 8, 3, 3, 4, 6, 2]
I         - increments between consecutive items of a   [-5, 0, 1, 2,-4 ]
 ’        - decrement (vectorises)                      [-6,-1, 0, 1,-5 ]
   0      - literal 0
  «       - minimum of decremented increments and zero  [-6,-1, 0, 0,-5 ]
    Ṛ     - reverse                                     [-5, 0, 0,-1,-6 ]
      \   - cumulative reduce with:
     +    -   addition                                  [-5,-5,-5,-6,-12]
       Ṛ  - reverse                                     [-12,-6,-5,-5,-5]
        + - addition (with a)                           [-4,-3,-2,-1, 1, 2]


8

자바 스크립트 (ES6), 61 바이트

a=>a.map((b,i)=>a=(b-=a[i+1])>0?a.map(c=>i--<0?c:c-b-1):a)&&a

테스트 사례


7

젤리 , 12 바이트

I»1U
0ị;Ç_\U

온라인으로 사용해보십시오!

작동 원리

I»1U  Helper link. Argument: l (list of integers)
I     Compute the increments (difference between items) of l.
 »1   For each item n, take the maximum of n and 1.
   U  Reverse.

0ị;Ç_\U  Main link. Argument: l (list of integers)
   Ç     Call the helper link with argument l.
  ;      Concatenate this with
0ị       the 0th last item of the (1-indexed) l. (Can't use Ṫ because it modifies l)
    _\   Cumulatively reduce the result by subtraction.
      U  Reverse.

입력과 출력 배열을 반대로 바꾸면 출력은 단순히 각 델타가 0 이상인 입력이 -1로 대체 된 입력입니다. 예를 들면 다음과 같습니다.

[10,  5,  7,  6,  1]   input
[ 1,  6,  7,  5, 10]   reverse
[   5,  1, -2,  5  ]   deltas
[  -1, -1, -2, -1  ]   min(deltas, -1)
[ 1, -1, -2, -1, -1]   reverse and concat the last item of the original
[ 1,  0, -2, -3, -4]   re-apply deltas
[-4, -3, -2,  0,  1]   reverse

5

k, 20 바이트

{x-|+\0,1_0|1+-':|x}

온라인으로 사용해보십시오.

설명:

{                  } /function, x is input
                 |x  /reverse x
              -':    /difference between every element
            1+       /add one to each difference
          0|         /make minimum difference be 0
      0,1_           /swap first difference with a 0
    +\               /cumulative sum
   |                 /reverse again
 x-                  /subtract from x

4

하스켈, 56 바이트

a#(x:y:z)=map(+min(y-x-1)0)(a++[x])#(y:z)
a#x=a++x
([]#)

온라인으로 사용해보십시오!

목록의 첫 부분을 parameter에 유지하십시오 a. 각 단계에서, 다음의 요소를 추가 x의 끝 a과의 최소하여 모든 요소가 증가 (y-x-1)하고 0.



3

C #, 76 바이트

a=>{for(int d=0,i=a.Length-1;i>0;a[--i]-=d)d=a[i-1]-d<a[i]?d:a[i-1]-a[i]+1;}

목록이 수정됩니다. 목록을 거꾸로 진행하여 델타의 누적 합계를 유지하여 각 숫자에 적용합니다.


2

자바 스크립트 (ES6), 59 바이트

f=([n,...a],p=a[0]-n)=>a+a?[(a=f(a))[0]-(p>1?p:1),...a]:[n]

와. JS 솔루션을 작성하려고했지만 이것을 보았습니다. 나는 매개 변수에 같은 확산 연산자를 사용하여 생각하지 않았다
andrewarchi

f=두 개의 바이트를 절약하기 위해 JS 응답을 떠날 수 있습니다.
andrewarchi

@andrewarchi 감사합니다. 그러나이 특정 함수는 자체적으로 호출 ( f(a))해야하므로 여전히 이름이 필요합니다.
ETHproductions

나는 그것이 재귀했다 잊고
andrewarchi

2

Brain-Flak , 153 바이트

{(({})<>[({})])(({}({}))[({}[{}])])<>(([{}]({})))([({}<(())>)](<>)){({}())<>}{}{((<{}>))<>{}}{}<>{}{{}({}<>{}())((<>))}{}{}}{}<>{}([]){{}({}<>)<>([])}<>

온라인으로 사용해보십시오!

여기에는 +1에 대한 -r플래그.

#While True
{

    #Push the last value left in the array minus the counter onto the alternate stack
    (({})<>[({})])

    #Put the counter back on top of the alternate stack
    (({}({}))[({}[{}])])

    #Toggle
    <>

    #Find the difference between the last two inputs left on the array
    (([{}]({})))

    #Greater than or equal to 0?
    ([({}<(())>)](<>)){({}())<>}{}{((<{}>))<>{}}{}<>{}

    #If So:
    {

      #Pop the truthy/falsy value
      {}

      #Increment the counter by the difference between elements +1
      ({}<>{}())

      #Push two falsys
      ((<>))

    #Endwhile
    }

    #Pop the two falsys
    {}{}

#Endwhile
}

#Pop the falsy

{}

#Toggle back
<>

#Pop the counter

#Reverse the stack
{}
([]){{}({}<>)<>([])}<>

2

R, 56 바이트

function(s){s-c(rev(cumsum(rev(pmax(0,-diff(s)+1)))),0)}


1
의 좋은 사용 diff, 나는 그것을 작동시키는 방법을 알아 내려고 노력 중 ... 방법으로, 함수 본문 주위의 중괄호를 -2 바이트 동안 제거 할 수 있지만 더 나은 방법 s=scan()은 함수 대신 사용할 수 있습니다 몇 바이트를 더 절약 할 수 있습니다. 다른 사람이이 코드가 모든 테스트 사례에 대해 작동하는지 확인할 수 있도록 온라인 으로 시도 링크를 포함하면 좋을 것 입니다.
주세페

걱정 마! 우리 모두 어딘가에서 시작 :)
주세페

1

자바 스크립트 (ES6), 68 바이트

a=>a.map((v,i)=>(d=v-o[i+1]+1)>1?o=o.map((v,j)=>j>i?v:v-d):0,o=a)&&o

입력 및 출력은 정수 배열입니다.

테스트 스 니펫

f=
a=>a.map((v,i)=>(d=v-o[i+1]+1)>1?o=o.map((v,j)=>j>i?v:v-d):0,o=a)&&o
<input id=I oninput="O.value=f(this.value.split` `.map(x=>+x)).join` `">
<input id=O disabled>


1

자바 스크립트 (ES6), 50 바이트

f=a=>(b=[...a]).some((_,i)=>a[i]-->=a[i+1])?f(a):b

설명:

이것은 재귀 솔루션으로, 먼저 배열을 복제 한 다음 요소가 배열의 다음 요소보다 크거나 같아 질 때까지 모든 값을 줄입니다.

어떤 요소가 고장난 한 함수는 스스로를 호출합니다. 요소가 최종 정렬되면 클론이 반환됩니다. ( some()메소드가 모든 요소를 ​​감소 시켜서 -1 씩 줄 이므로 배열 자체를 반환 할 수 없습니다 .)

테스트 사례 :

f=a=>(b=[...a]).some((_,i)=>a[i]-->=a[i+1])?f(a):b

console.log(f([10,5,7,6,1])+'');
console.log(f([1,1,1,1,1,1,1,1,1])+'');
console.log(f([5,7,11,6,16,2,9,16,6,16])+'');
console.log(f([19])+'');
console.log(f([-8,17,9,7])+'');
console.log(f([1,2,3,4,5,6,7])+'');


1

SWI- 프롤로그, 194 바이트

:-use_module(library(clpfd)).
f([],[],_,_).
f([A|B],[M|N],P,D):-A#=M-D-E,A#<P,abs(M,S),T#=S+1,E in 0..T,label([E]),f(B,N,A,D+E).
l([],[]).
l(A,B):-reverse(Z,B),f([X|Y],Z,X+1,0),reverse(A,[X|Y]).

http://swish.swi-prolog.org/p/LazySort.pl에서 온라인으로 시도해 볼 수 있습니다.

l(L, [10,5,7,6,1])."L에 대한 해결, 여기서 L은이 목록의 게으른 정렬 버전입니다"라고 묻습니다 .

두 가지 기능은 다음과 같습니다.

  • lazysorted (A, B)-A가 B의 lazysorted 버전임을 나타냅니다. 둘 다 비어있는 목록이거나 B를 역순으로하여 A를 얻을 수 있거나 목록을 걸어서 누산기로 빼기를 수행하는 도우미 함수 호출 각 값을 이전 값보다 낮게 밀고 그 결과를 올바른 방향으로 되돌립니다.
  • f도우미는 목록의 이전 숫자 값과 롤링 차이 누산기의 두 목록을 일치시키고 현재 목록 위치의 새 값에서 원래 값에서 차이 누산기를 뺀 값을 선택적으로 풀고, 선택적으로 빼기 위해 필요한 새 값을 뺍니다. 값이 목록의 이전 숫자 아래로 f떨어지고 이제 증가 된 차이 누산기를 사용하여 목록의 꼬리를 재귀 적으로 해결해야합니다.

Swish의 테스트 사례 스크린 샷 :

Swish에서 실행되는 테스트 케이스를 보여주는 이미지


0

자바 스크립트 (ES6), 61 바이트

a=>a.reduceRight((r,e)=>[e-(d=(c=e-r[0]+1)>d?c:d),...r],d=[])

가장 짧은 해결책은 아니지만 사용할 기회를 전달할 수 없었습니다 reduceRight.


0

C # (. NET 코어) , 89 88 86 79 바이트

  • 약간 다른 접근 방식으로 1 바이트 만 절약했습니다.
  • fors를 단순화하여 다른 2 바이트를 절약했습니다 .
  • VisualMelon의 놀라운 골프 기술 덕분에 7 바이트가 절약되었습니다.
a=>{for(int i=0,j,k;++i<a.Length;)for(k=a[i-1]-a[j=i]+1;--j>=0;)a[j]-=k>0?k:0;}

온라인으로 사용해보십시오!

먼저 for배열을 반복 한 다음 감소를 계산하고 마지막으로 두 번째 위치 for까지 요소를 감소시킵니다 i.

새로운 배열을 반환하는 대신 원래 배열을 수정하는 것이 유효합니까 (여전히 규칙에 익숙해 졌습니까)?


예, 원래 배열을 수정하는 것은 완벽합니다. :)
DJMcMayhem

4
@DJMcMayhem 감사합니다, 나는 새로운 것을 만들기에는 너무 게으른 느낌이 들었습니다. :)
Charlie
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.