Внедрить «Ленивую сортировку»


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
Я думаю, что вам не лень, если вы делаете это таким образом
Йорг Хюльсерманн

4
@ JörgHülsermann ну, некоторые целые числа слишком тяжелые ... не совсем в настроении, чтобы нести такой вес, лучше снимать только самые лучшие вещи
Эрик Игрок в гольф

21
<sarcasm>Этот алгоритм сортировки на самом деле все еще имеет O(N^2)временную сложность, потому что вы должны пройти через все ранее доступные элементы в списке, чтобы уменьшить их. Вместо этого я рекомендую просмотреть список в обратном порядке и при необходимости уменьшать только одно число на шаг. Это даст вам истинную O(N)сложность! </sarcasm>
Value Ink

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

7
@ColeJohnson технически да, но сложность времени должна учитывать все этапы алгоритма. Вам по-прежнему приходится перебирать все предыдущие индексы на каждой итерации, поэтому она все еще выходит O(N^2).
Value Ink

Ответы:


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

к, 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и увеличивайте все элементы до минимума (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

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

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-Пролог, 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 является lazysorted версией 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.