Erste Zeichenänderungen in einer Liste
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
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[#]] &;
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}
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}
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.
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}