Erste Zeichenänderungen in einer Liste

Oct 07 2020

Ich habe vorher eine ähnliche Frage gestellt (https://bit.ly/3llhmFE). Dieser ist ähnlich, aber anders.

Ich habe eine Liste mit einer Folge von Ziffern 1, 0 und -1, zum Beispiel:

l = {0, 0, 1, 1, 1, 1, -1, -1, -1, 0 , 1, 1}

Ich möchte, dass eine Funktion das Ergebnis zurückgibt:

    {0, 0, 1, 0, 0, 0, -1, 0 , 0,  0, 1, 0}

Im Wesentlichen möchte ich die Reihenfolge der ersten Vorzeichenwechsel.

Diese Sequenzen können Zehntausende oder Hunderttausende lang sein, daher muss ich eine effiziente Lösung finden, die keine Schleifen beinhaltet - vielleicht mithilfe von Foldlist.

Antworten

7 ciao Oct 07 2020 at 20:19

Ich denke, Sie werden das ziemlich bissig finden:

fn = Sign[Prepend[Differences[#], 0] Abs[#]] &;

Die Frage nach der Anordnung des ersten Ergebniselements ist nicht eindeutig. Sollte es Null sein (es gibt keinen ersten Vorzeichenwechsel von "nichts vorher") oder das Vorzeichen des ersten Zielelements.

In letzterem Fall erfolgt dies ohne Leistungskosten:

fn2 = Sign[Prepend[Differences[#], #[[1]]] Abs[#]] &;
5 kglr Oct 08 2020 at 00:45

Etwas schnellere Versionen von ciao fnund fn2:

f0 = # Unitize @ Prepend[Differences @ #, 0]  &;

f0 @ l
{0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 1, 0}
f1 = # Unitize @ Prepend[Differences @ #, #[[1]]] &;

f1 @ l
{0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 1, 0}
4 SjoerdSmit Oct 07 2020 at 19:14

Sie könnten dies mit einem FoldPairListoder einem Gefolterten tun FoldList, aber der einfachste Weg, eine schnelle Funktion zu erhalten, ist wahrscheinlich nur Compileein Tisch:

cf = Compile[{
   {list, _Integer, 1}
   },
  Module[{prev = 0},
   Table[
    Which[
     i == 0, prev = i; 0,
     i > prev, prev = i; 1,
     i < prev, prev = i; -1,
     True, 0
     ],
    {i, list}
    ]
   ]
 ]

Zeitliche Koordinierung:

l = RandomInteger[{-1, 1}, 10^7];
cf[l]; // AbsoluteTiming

{0,822968, Null}

4 C.E. Oct 07 2020 at 19:44
findFirstZeroes[l_] := Unitize[Prepend[Rest[l] - Most[l], 0] + 1]

findFirstInSequence[l_] := Module[{a, b},
  a = Unitize[l - 1];
  b = Unitize[l + 1];
  Abs[findFirstZeroes[a] - 1] + (findFirstZeroes[b] - 1)
  ]

l = RandomInteger[{-1, 1}, 10^7];
findFirstInSequence[l]; // AbsoluteTiming

{0.225284, Null}

Sjoerds Lösung benötigt auf meinem Computer ungefähr 0,36.

2 JonathanKinlay Oct 07 2020 at 19:16

Ich kann mit folgendem Code dorthin gelangen:

op=Foldlist[If[#2==0,@1,#2]&]; 
op@l;
Join[{0},Sign@Differences[%]]

welches produziert:

{0, 0, 1, 0, 0, 0, -1, 0, 1, -1, 0}  

nach Bedarf.

Aber ich kann nicht herausfinden, wie man alles in einer einzigen, eleganten Funktion zusammenfügt.

Die folgende Funktion ist ungefähr so ​​schnell wie die kompilierte Schleifenprozedur:

foo[l_]:=Module[{t},
t=FoldList[If[#2==0, #1, #2]&]@l;
Join[{0},Sign@Differences[t]]]

l = RandomInteger[{-1,1}, 10^7};
foo[l];//AbsoluteTiming
{0.803403, Null}