2011-12-30 11 views
9

To jest mały, zabawny problem i chciałem sprawdzić u ekspertów, czy istnieje lepszy, funkcjonalny/matematyczny sposób podejścia do rozwiązania problemu niż to, co zrobiłem. Nie jestem zbyt zadowolony z mojego rozwiązania, ponieważ używam dużego IF THEN ELSE, ale nie mogę znaleźć polecenia Mathematica, które można z łatwością użyć (np. Select, Cases, Sow/Reap, Map .. itd ...)Jak zamienić każdy 0 na poprzedni element na liście w sposób idiomatyczny w Mathematica?

Oto problem, biorąc pod uwagę wartości list (liczby lub symbole), ale dla uproszczenia, pozwala założyć listę liczb na razie. Lista może zawierać zera, a celem jest zastąpienie każdego zera elementem widzianym przed nim.

Na końcu, lista nie powinna zawierać zer:.

Oto przykład, biorąc pod uwagę

a = {1, 0, 0, -1, 0, 0, 5, 0}; 

wynik powinien być

a = {1, 1, 1, -1, -1, -1, 5, 5} 

powinno oczywiście być wykonane w najbardziej efektywny sposób.

To co mogłem wymyślić

Scan[(a[[#]] = If[a[[#]] == 0, a[[#-1]], a[[#]]]) &, Range[2, Length[a]]]; 

Chciałem zobaczyć, czy mogę użyć Sow/Reap na ten temat, ale nie wiem jak.

pytanie: czy można to rozwiązać w bardziej funkcjonalny/matematyczny sposób? Im krótszy, tym lepiej oczywiście :)

Update 1 Dzięki wszystkim za odpowiedzi, wszystkie są bardzo dobre do nauki. Jest to wynik testu prędkości o 8,04 V, przy użyciu szyby 7, 4 PL Ram Intel 930 @ 2,8 GHz:

I przetestowaniu wszystkich metod n z 100,000 do 4 million. Metoda ReplaceRepeated nie sprawdza się w przypadku dużych list.

Aktualizacja 2

Usunięto wcześniej wynik, który został przedstawiony powyżej w update1 powodu mojego błędu w kopiowaniu jednego z testów.

Zaktualizowane wyniki znajdują się poniżej. Metoda Leonida jest najszybsza. Gratulacje Leonid. Bardzo szybka metoda.

enter image description here

Program badań jest następujący:

(*version 2.0 *) 
runTests[sizeOfList_?(IntegerQ[#] && Positive[#] &)] := 
Module[{tests, lst, result, nasser, daniel, heike, leonid, andrei, 
    sjoerd, i, names}, 

    nasser[lst_List] := Module[{a = lst}, 
    Scan[(a[[#]] = If[a[[#]] == 0, a[[# - 1]], a[[#]]]) &, 
    Range[2, Length[a]]] 
    ]; 

    daniel[lst_List] := Module[{replaceWithPrior}, 
    replaceWithPrior[ll_, n_: 0] := 
    Module[{prev}, Map[If[# == 0, prev, prev = #] &, ll] 
     ]; 
    replaceWithPrior[lst] 
    ]; 

    heike[lst_List] := Flatten[Accumulate /@ Split[lst, (#2 == 0) &]]; 

    andrei[lst_List] := Module[{x, y, z}, 
    ReplaceRepeated[lst, {x___, y_, 0, z___} :> {x, y, y, z}, 
    MaxIterations -> Infinity] 
    ]; 

    leonid[lst_List] := 
    FoldList[If[#2 == 0, #1, #2] &, [email protected]#, [email protected]#] & @lst; 

    sjoerd[lst_List] := 
    FixedPoint[(1 - Unitize[#]) RotateRight[#] + # &, lst]; 

    lst = RandomChoice[Join[ConstantArray[0, 10], Range[-1, 5]], 
    sizeOfList]; 
    tests = {nasser, daniel, heike, leonid, sjoerd}; 
    names = {"Nasser","Daniel", "Heike", "Leonid", "Sjoerd"}; 

    result = Table[0, {Length[tests]}, {2}]; 

    Do[ 
    result[[i, 1]] = names[[i]]; 

    Block[{j, r = Table[0, {5}]}, 
    Do[ 
    r[[j]] = [email protected][tests[[i]][lst]], {j, 1, 5} 
    ]; 
    result[[i, 2]] = Mean[r] 
    ], 

    {i, 1, Length[tests]} 
    ]; 

    result 
    ] 

Aby uruchomić testy dla długości 1000 komenda brzmi:

Grid[runTests[1000], Frame -> All] 

Dzięki wszystkim za odpowiedzi.

+3

Tylko uwaga, że ​​użycie 'If' jest * nie * nie działa. Parametry warunkowe są zasadniczą częścią programowania funkcjonalnego i nie wymagają efektów ubocznych. Pomyśl o 'If' jako funkcji matematycznej odwzorowującej boolany (zbiór {True, False}) na coś innego. W przeciwnym razie znalazłem to samo rozwiązanie, co Andrei, co moim zdaniem jest najprostsze, ale zdecydowanie nie jest najszybsze (stąd nie jest to najbardziej praktyczne, jeśli przetwarzasz duże dane!) – Szabolcs

+2

replaceWithPrior [ll_, n_: 0]: = Module [{ prev}, Mapa [Jeśli [# == 0, prev, prev = #] &, ll]] W [12]: = replaceWithPrior [a] Out [12] = {1, 1, 1, -1 , -1, -1, 5, 5} –

+4

BTW co powinno się stać, jeśli pierwszy element to 0? – Szabolcs

Odpowiedz

12

dużo (rząd wielkości) szybciej niż inne rozwiązania nieruchomych:

FoldList[If[#2 == 0, #1, #2] &, [email protected]#, [email protected]#] & 

Przyspieszenie jest spowodowane automatycznym kompilowaniem Fold. Nie będzie tak dramatyczny dla niezapakowanych tablic. Benchmarki:

In[594]:= 
a=b=c=RandomChoice[Join[ConstantArray[0,10],Range[-1,5]],150000]; 
(b=Flatten[Accumulate/@Split[b,(#2==0)&]]);//Timing 
Scan[(a[[#]]=If[a[[#]]==0,a[[#-1]],a[[#]]])&,Range[2,Length[a]]]//Timing 
(c=FoldList[If[#2==0,#1,#2]&,[email protected]#,[email protected]#]&@c);//Timing 

SameQ[a,b,c] 

Out[595]= {0.187,Null} 
Out[596]= {0.625,Null} 
Out[597]= {0.016,Null} 
Out[598]= True 
6

Pytanie wygląda dokładnie tak, jak zadanie dla funkcji ReplaceRepeated.Zasadniczo polega na tym, że stosuje on ten sam zestaw reguł do wyrażenia, dopóki nie będą obowiązywać żadne reguły. W twoim przypadku wyrażenie jest listą, a reguła ma zastąpić 0 swoim poprzednikiem, ilekroć pojawi się na liście. Więc tutaj jest rozwiązanie:

a = {1, 0, 0, -1, 0, 0, 5, 0}; 
a //. {x___, y_, 0, z___} -> {x, y, y, z}; 

Wzór dla reguły jest tu następujący:

  • x___ - dowolny symbol, zero lub więcej powtórzeń, początek listy
  • y_ - dokładnie jeden element przed zerem
  • 0 - samo zero, ten element zostanie zastąpiony przez y późniejszy
  • z___ - dowolny symbol, zero lub więcej powtórzeń, koniec listy
+4

Przepraszam, że przyniosłem, ale czuję, że powinienem, ponieważ nikt jeszcze nie skomentował tego. Zasadniczo będzie to bardzo nieefektywne i praktyczne tylko w przypadku bardzo krótkich list. Ogólna rada polega na uniknięciu 'ReplaceRepeated' z wzorami podobnymi do tego, którego użyłeś (włączając podwójne i potrójne spacje). Wspomniałem o tym tutaj: http://stackoverflow.com/questions/4721171/performance-tuning-inmatmatica/4723969#4723969, jako jedną z pułapek wydajności. Inną informacją jest to, że powinieneś użyć 'RuleDelayed' zamiast' Rule', aby poprawnie zlokalizować swoje zmienne wzorcowe. –

+0

Znalazłem również domyślny limit używania ReplaceRepeated. Kiedy uruchomię go na dużej liście, pojawia się błąd z jądra 'ReplaceRepeated :: rrlim: Exiting after .... zeskanowany 65536 razy'. Następnie stwierdziłem, że ten limit można usunąć, używając opcji 'MaxIterations-> Infinity'. – Nasser

+0

@Leonid, wielkie dzięki, nie teraz o słabym występie 'ReplaceRepeated'. – Andrei

8

To wydaje się być czynnikiem 4 szybciej na moim komputerze:

a = Flatten[Accumulate /@ Split[a, (#2 == 0) &]] 

synchronizacją dostaję są

a = b = RandomChoice[Join[ConstantArray[0, 10], Range[-1, 5]], 10000]; 

(b = Flatten[Accumulate /@ Split[b, (#2 == 0) &]]); // Timing 

Scan[(a[[#]] = If[a[[#]] == 0, a[[# - 1]], a[[#]]]) &, 
    Range[2, Length[a]]] // Timing 

SameQ[a, b] 

(* {0.015815, Null} *) 
(* {0.061929, Null} *) 
(* True *) 
+0

Podoba mi się tutaj użycie 'Split'. Zrobiłem coś podobnego, ale jest wolniej: 'Spłaszczaj [Mapa [ConstantArray [First [#], Length [#]] &, Split [a, # 2 == 0 &]]]' –

8
FixedPoint[(1 - Unitize[#]) RotateRight[#] + # &, d] 

wynosi około 10 i 2 razy szybciej niż rozwiązania Heike, ale wolniej niż Leonid użytkownika.

+0

Czy jesteś pewny co do wolniejszy bit? We wszystkich moich dotychczasowych testach twoja metoda była najszybsza. Opublikuję kod testowy (w przypadku, gdy coś źle zrobiłem) i wyniki tabeli wkrótce. Używam v 8.04 na windows 7 – Nasser

+0

@Sjoerd Nice take on it. Wydajność wydaje się jednak zależeć od średniej wielkości zera zer (to jest z superwizji, może się mylę). +1 –

+0

@LeonidShifrin, dzięki za spostrzeżenie. Skopiowałem twój kod z górnej części twojego postu, przeoczyłem dodatkowe @ używane poniżej, gdy go zastosowałeś. Właśnie dlatego opublikowałem swój testowy kod, aby upewnić się, że najpierw został sprawdzony. Ponownie uruchomi wszystkie testy i usunie bieżącą tabelę wyników, a następnie opublikuje nowe wyniki. Szybki test pokazuje, że rzeczywiście jesteś teraz szybszy. Próbuję też sfinalizować inną metodę i dodam ją do tabeli. Ale zacznij biec teraz, zdobądź trochę jedzenia, zanim sklepy się zamkną. – Nasser

Powiązane problemy