Впровадити "Ледачий сорт"


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, тому ми зменшуємо перші чотири числа, поки воно не стане меншим за 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
Я думаю, ви не лінуєтесь, якщо зробите це таким чином
Jörg Hülsermann

4
@ JörgHülsermann добре, що цілі числа є занадто важкими ... не зовсім в настрої переносити таку вагу, краще зняти лише верхню штуку
Ерік Атгольфер

21
<sarcasm>Цей алгоритм сортування насправді залишається O(N^2)складним у часі, оскільки вам потрібно пройти всі раніше доступні елементи у списку, щоб зменшити їх. Я рекомендую йти за списком в зворотному напрямку , а й зменшуємо тільки один номер на етапі по мірі необхідності. Це додасть вам справжньої O(N)складності! </sarcasm>
Значення чорнила

1
@ValueInk O(n^2)з точки зору доступу до пам'яті, але хіба це не O(n)для порівняння?
Коул Джонсон

7
@ColeJohnson технічно так, але часова складність потребує врахування всіх кроків алгоритму. Ви все ще повинні повторити всі попередні індекси для кожної ітерації, тому вона все одно виходить O(N^2).
Значення чорнила

Відповіді:


12

Желе ,  14 12 11  9 байт

-2 байти завдяки ETHproductions (використовуйте мінімальний діад, «)

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

JavaScript (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

Haskell, 56 байт

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

Спробуйте в Інтернеті!

Збережіть першу частину списку в параметрі a. На кожному кроці додайте наступний елемент xдо кінця aта збільшуйте всі елементи 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

JavaScript (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

Мозок-Флак , 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

JavaScript (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

JavaScript (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-Prolog, 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, якщо вони обидва порожні списки, або якщо A можна отримати, перевернувши B, викликаючи функцію помічника, щоб пройти список і зробити віднімання з акумулятором відсуваючи кожне значення нижче попереднього і повертаючи результат назад на правильний шлях.
  • fПомічник відповідає двом спискам, значенням попереднього числа у списку та накопичувачем різниці кочення, і вирішує, що нове значення поточної позиції списку є початковим значенням за вирахуванням акумулятора різниці, необов'язково мінус нове значення, необхідне для примушування цього значення нижче попереднього числа у списку, і fповинно вирішуватись для хвоста списку рекурсивно із збільшеною різницею акумулятора.

Скріншот тестових випадків на Swish:

зображення, що показує тестові випадки на Swish


0

JavaScript (ES6), 61 байт

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

Не найкоротше рішення, але я не міг передати можливість використовувати reduceRight.


0

C # (.NET Core) , 89 88 86 79 байт

  • Збережено лише 1 байт із дещо іншим підходом.
  • Збережено ще 2 байти зі спрощенням fors.
  • Збережено 7 байт завдяки дивовижним навичкам гольфу 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;}

Спробуйте в Інтернеті!

Спочатку він forпроходить через масив, потім обчислює декремент і, нарешті, другий forдекрементує елементи, якщо потрібно, до iпозиції th.

Чи дійсно просто змінювати початковий масив, а не повертати новий (все ще звикаючи до правил)?


Так, зміна оригінального масиву є ідеальною. :)
DJMcMayhem

4
@DJMcMayhem спасибі, мені стало лінь створити новий. :)
Чарлі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.