「レイジーソート」を実装する


44

私は数字のリストをソートすることになっていますが、私はとても怠け者です。すべての数字が昇順になるまですべての数字を入れ替える方法を考えるのは本当に難しいので、新しいリストがソートされることを保証する独自のアルゴリズムを思いついた¹。仕組みは次のとおりです。

サイズNのリストの場合、N-1回の反復が必要です。各反復で、

  • N番目の数値がN + 1番目の数値よりも小さいかどうかを確認します。そうである場合、これらの2つの数値は既にソートされているため、この反復をスキップできます。

  • そうでない場合は、最初のN個の数字を順番にデクリメントして、これら2つの数字が順番どおりになるまで続ける必要があります。

具体例を見てみましょう。入力があったとしましょう

10 5 7 6 1

最初の反復で、10と5を比較します。10は5よりも大きいため、小さくなるまでデクリメントします。

4 5 7 6 1

5と7を比較します。5は7より小さいので、この反復では何もする必要はありません。次のステップに進み、7と6を比較します。7は6より大きいため、最初の3つの数値を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)、リスト上の以前にアクセスしたすべてのアイテムをデクリメントする必要があるため、実際には時間の複雑さを記録しています。代わりにリストを逆方向にたどって、必要に応じてステップごとに1つの数値だけを減らすことをお勧めします。これにより、真のO(N)複雑さが得られます!</sarcasm>
バリューインク

1
O(n^2)メモリアクセスの観点から@ValueInk O(n)ですが、比較用ではありませんか?
コールジョンソン

7
@ColeJohnsonは技術的にはそうですが、時間の複雑さはアルゴリズムのすべてのステップを考慮する必要があります。繰り返しごとに以前のすべてのインデックスを繰り返し処理する必要があるため、まだ出ていO(N^2)ます。
バリューインク

回答:


12

ゼリー 14 12 11  9 バイト

ETHproductionsのおかげで-2バイト(最小ダイアドを使用、«

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を末尾に追加し、aaのすべての要素を(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=2バイトを節約するために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()に、関数の代わりに使うことができますさらに数バイトを節約するための定義。他の人がすべてのテストケースでこのコードが機能することを確認できるように、Try it onlineへのリンクを含めると便利です。
ジュゼッペ

心配ない!私たちはすべてどこかで始まります:)
ジュゼッペ

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はこのリストの遅延ソートバージョンです」と尋ねます。

2つの機能は次のとおりです。

  • lazysorted(A、B)-Aが両方とも空のリストである場合、またはAがBを逆にしてリストをウォークしてアキュムレーターで減算を行うヘルパー関数を呼び出すことで取得できる場合、AはBのレイジーソートバージョンであることを示します各値を前の値よりも低くし、その結果を元に戻します。
  • fヘルパーは、リスト内の前の数値の値とローリング差分アキュムレータの2つのリストを照合し、現在のリスト位置の新しい値が元の値から差分アキュムレータを引いたもの、オプションでこれを強制するために必要な新しい値を引いたものを解決しますリスト内の前の数値よりも小さい値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バイトだけを保存しました。
  • 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必要に応じて2番目の要素がith位置まで減少します。

新しい配列を返すのではなく、元の配列を変更するだけで有効ですか?


はい、元のアレイを修正することはまったく問題ありません。:)
DJMcMayhem

4
@DJMcMayhemありがとう、私は新しいものを作成するのが面倒だと感じました。:)
チャーリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.