Thực hiện việc sắp xếp lười biếng


44

Tôi phải sắp xếp một danh sách các số, nhưng tôi siêu lười. Thật sự rất khó để tìm ra cách hoán đổi tất cả các số xung quanh cho đến khi tất cả chúng theo thứ tự tăng dần, vì vậy tôi đã đưa ra thuật toán của riêng mình để đảm bảo rằng danh sách mới được sắp xếp¹. Đây là cách nó hoạt động:

Để có danh sách kích thước N , chúng ta sẽ cần các lần lặp N-1 . Trên mỗi lần lặp lại,

  • Kiểm tra xem số thứ N có nhỏ hơn số thứ N + 1 không . Nếu có, thì hai số này đã được sắp xếp và chúng ta có thể bỏ qua phép lặp này.

  • Nếu chúng không phải, thì bạn cần liên tục giảm các số N đầu tiên cho đến khi hai số này theo thứ tự.

Hãy lấy một ví dụ cụ thể. Giả sử đầu vào là

10 5 7 6 1

Ở lần lặp đầu tiên, chúng ta sẽ so sánh 10 và 5. 10 lớn hơn 5, vì vậy chúng tôi giảm dần cho đến khi nó nhỏ hơn:

4 5 7 6 1

Bây giờ chúng ta so sánh 5 và 7. 5 nhỏ hơn 7, vì vậy chúng ta không cần phải làm gì với phép lặp này. Vì vậy, chúng tôi đi đến phần tiếp theo và so sánh 7 và 6. 7 lớn hơn 6, vì vậy chúng tôi giảm ba số đầu tiên cho đến khi nó nhỏ hơn 6 và chúng tôi nhận được điều này:

2 3 5 6 1

Bây giờ chúng tôi so sánh 6 và 1. Một lần nữa, 6 lớn hơn 1, vì vậy chúng tôi giảm bốn số đầu tiên cho đến khi nó nhỏ hơn 1 và chúng tôi nhận được điều này:

-4 -3 -1 0 1

Và chúng ta đã hoàn thành! Bây giờ danh sách của chúng tôi là theo thứ tự hoàn hảo sắp xếp. Và, để làm cho mọi thứ trở nên tốt hơn, chúng tôi chỉ phải lặp qua danh sách N-1 lần, vì vậy thuật toán này sắp xếp danh sách theo thời gian O (N-1) , mà tôi khá chắc chắn là thuật toán nhanh nhất có .²

Thách thức của bạn cho ngày hôm nay là thực hiện Lazy Sort này. Chương trình hoặc chức năng của bạn sẽ được cung cấp một loạt các số nguyên ở bất kỳ định dạng chuẩn nào bạn thích và bạn phải thực hiện loại sắp xếp lười biếng này và trả về danh sách "đã sắp xếp" mới . Mảng sẽ không bao giờ trống hoặc chứa các số nguyên.

Dưới đây là một số ví dụ:

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

Như mọi khi, đây là , vì vậy hãy viết chương trình ngắn nhất bạn có thể!


¹ này không có nghĩa là những gì có vẻ như nó có nghĩa, nhưng nó là về mặt kỹ thuật đúng

² Tôi hoàn toàn đùa, xin đừng ghét tôi


6
Tôi nghĩ bạn không lười biếng nếu bạn làm theo cách này
Jörg Hülsermann

4
@ JörgHülsermann cũng có một số nguyên quá nặng ... không chính xác trong tâm trạng mang trọng lượng như vậy, tốt hơn là chỉ cởi bỏ những thứ hàng đầu
Erik the Outgolfer

21
<sarcasm>Thuật toán sắp xếp này thực sự vẫn đồng hồ ở mức O(N^2)độ phức tạp vì bạn phải đi qua tất cả các mục được truy cập trước đó trong danh sách để giảm chúng. Tôi khuyên bạn nên đi qua danh sách ngược thay vào đó và chỉ giảm một số cho mỗi bước khi cần thiết. Điều này sẽ cung cấp cho bạn sự O(N)phức tạp thực sự! </sarcasm>
Mực giá trị

1
@ValueInk O(n^2)về quyền truy cập bộ nhớ, nhưng không phải là O(n)để so sánh?
Cole Johnson

7
@ColeJohnson về mặt kỹ thuật có, nhưng độ phức tạp thời gian cần phải xem xét tất cả các bước của thuật toán. Bạn vẫn phải lặp qua tất cả các chỉ số trước đó trên mỗi lần lặp, vì vậy nó vẫn xuất hiện O(N^2).
Mực giá trị

Câu trả lời:


12

Thạch ,  14 12 11  9 byte

-2 byte nhờ vào ETHproductions (sử dụng dyad tối thiểu, «)

I’«0Ṛ+\Ṛ+

Một liên kết đơn lấy và trả về danh sách các số nguyên.

Hãy thử trực tuyến! hoặc xem bộ thử nghiệm .

Tôi thực sự không nghĩ rằng đây là Lazy ™ đủ!

Làm sao?

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

JavaScript (ES6), 61 byte

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

Các trường hợp thử nghiệm


7

Thạch , 12 byte

I»1U
0ị;Ç_\U

Hãy thử trực tuyến!

Làm thế nào nó hoạt động

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.

Ý tưởng cơ bản lúc chơi là thế này: Nếu bạn đảo ngược các mảng đầu vào và đầu ra, đầu ra chỉ đơn giản là đầu vào với mỗi delta bằng 0 hoặc lớn hơn được thay thế bằng -1. Ví dụ:

[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 byte

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

Hãy thử trực tuyến.

Giải trình:

{                  } /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

Haskell, 56 byte

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

Hãy thử trực tuyến!

Giữ phần đầu tiên của danh sách trong tham số a. Ở mỗi bước, thêm phần tử tiếp theo xvào cuối avà tăng tất cả các phần tử của a theo mức tối thiểu (y-x-1)0.


4

Python , 54 byte

f=lambda a,*r:r and[f(*r)[0]-max(r[0]-a,1)]+f(*r)or[a]

Hãy thử trực tuyến!

Mất đầu vào như thế f(1,2,3). Xuất ra một danh sách. Sử dụng thời gian theo cấp số nhân.


3

C #, 76 byte

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;}

Điều này sửa đổi danh sách tại chỗ. Nó đi qua danh sách ngược và giữ tổng số delta để áp dụng cho mỗi số.


2

JavaScript (ES6), 59 byte

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

Ồ Tôi đã định viết một giải pháp JS, nhưng sau đó tôi thấy điều này. Tôi đã không nghĩ sử dụng toán tử lây lan như thế trong các tham số
andrewarchi

Bạn có thể bỏ qua f=các câu trả lời của JS để lưu hai byte
andrewarchi

@andrewarchi Cảm ơn, nhưng hàm đặc biệt này cần gọi chính nó ( f(a)) để nó vẫn yêu cầu tên.
Sản phẩm ETH

Tôi quên nó là đệ quy
andrewarchi

2

Brain-Flak , 153 byte

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

Hãy thử trực tuyến!

Điều này bao gồm +1cho -rcờ.

#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 byte

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


1
Cách sử dụng tốt diff, tôi đã cố gắng tìm ra cách để nó hoạt động ... Nhân tiện, bạn có thể thoát khỏi các dấu ngoặc xung quanh thân hàm cho -2 byte, nhưng tốt hơn nữa, bạn có thể sử dụng s=scan()thay vì hàm định nghĩa để tiết kiệm thêm một vài byte. Sẽ thật tuyệt nếu bạn bao gồm một liên kết đến Dùng thử trực tuyến để người khác có thể xác minh rằng mã này hoạt động cho tất cả các trường hợp thử nghiệm.
Giuseppe

Đừng lo lắng! tất cả chúng ta bắt đầu ở đâu đó :)
Giuseppe

1

JavaScript (ES6), 68 byte

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

Đầu vào và đầu ra là một mảng các số nguyên.

Kiểm tra đoạn trích

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

JavaScript (ES6), 50 byte

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

Giải trình:

Đây là một giải pháp đệ quy, đầu tiên nhân bản mảng, sau đó giảm tất cả các giá trị cho đến khi một phần tử lớn hơn hoặc bằng phần tử tiếp theo trong mảng.

Hàm gọi chính nó miễn là bất kỳ phần tử nào bị lỗi. Khi các yếu tố cuối cùng được sắp xếp, bản sao được trả về. (Chúng ta không thể trả về chính mảng đó, vì some()phương thức sẽ làm giảm tất cả các phần tử của nó, làm cho tất cả chúng bị giảm đi -1.)

Các trường hợp thử nghiệm:

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-Prolog, 194 byte

:-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]).

Có thể dùng thử trực tuyến tại đây: http://swish.swi-prolog.org/p/LazySort.pl

Bạn hỏi câu l(L, [10,5,7,6,1])."giải quyết cho L, trong đó L là phiên bản được sắp xếp lười biếng của danh sách này".

Hai chức năng là:

  • lazysort (A, B) - tuyên bố rằng A là phiên bản được phân loại của B, nếu cả hai đều là danh sách trống hoặc nếu A có thể lấy được bằng cách đảo ngược B, gọi hàm trợ giúp để đi qua danh sách và thực hiện phép trừ với bộ tích lũy đẩy từng giá trị thấp hơn giá trị trước đó và đảo ngược kết quả của giá trị đó trở lại đúng hướng xung quanh.
  • fTrình trợ giúp khớp với hai danh sách, giá trị của số trước đó trong danh sách và bộ tích lũy chênh lệch và giải quyết giá trị mới của vị trí danh sách hiện tại là giá trị ban đầu trừ đi bộ tích lũy chênh lệch, tùy ý trừ đi một giá trị mới cần thiết để buộc giá trị dưới số trước đó trong danh sách và fphải giải quyết phần đuôi của danh sách theo cách đệ quy với bộ tích lũy chênh lệch tăng bây giờ.

Ảnh chụp màn hình các trường hợp thử nghiệm trên Swish:

hình ảnh hiển thị các trường hợp thử nghiệm đang chạy trên Swish


0

JavaScript (ES6), 61 byte

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

Không phải là giải pháp ngắn nhất nhưng tôi không thể bỏ qua cơ hội sử dụng reduceRight.


0

C # (.NET Core) , 89 88 86 79 byte

  • Chỉ lưu 1 byte với cách tiếp cận hơi khác.
  • Đã lưu thêm 2 byte với sự đơn giản hóa của fors.
  • Lưu được 7 byte nhờ các kỹ năng chơi golf tuyệt vời của VisualMelon.
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;}

Hãy thử trực tuyến!

Đầu tiên forlặp qua mảng, sau đó nó tính toán giảm dần và cuối cùng là lần thứ hai forgiảm các phần tử nếu cần thiết cho đến ivị trí thứ.

Có hợp lệ không khi chỉ sửa đổi mảng ban đầu thay vì trả về một mảng mới (vẫn quen với các quy tắc)?


Có, Sửa đổi mảng ban đầu là hoàn toàn tốt. :)
DJMcMayhem

4
@DJMcMayhem cảm ơn, tôi cảm thấy quá lười biếng để tạo một cái mới. :)
Charlie
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.