Konwergencja procesu Markowa

10

Wyzwanie

Biorąc pod uwagę macierz stochastyczną lewą lub prawą, gdzie granica, gdy x zbliża się do nieskończoności macierzy, do potęgi x zbliża się do macierzy ze wszystkimi wartościami skończonymi, zwróć macierz, do której się zbiega. Zasadniczo, chcesz ciągle mnożyć macierz, aż wynik się nie zmieni.

Przypadki testowe

[[7/10, 4/10], [3/10, 6/10]] -> [[4/7, 4/7], [3/7, 3/7]]
[[2/5, 4/5], [3/5, 1/5]] -> [[4/7, 4/7], [3/7, 3/7]]
[[1/2, 1/2], [1/2, 1/2]] -> [[1/2, 1/2], [1/2, 1/2]]
[[1/3, 2/3], [2/3, 1/3]] -> [[1/2, 1/2], [1/2, 1/2]]
[[1/10, 2/10, 3/10], [4/10, 5/10, 6/10], [5/10, 3/10, 1/10]] -> [[27/130, 27/130, 27/130], [66/130, 66/130, 66/130], [37/130, 37/130, 37/130]]
[[1/7, 2/7, 4/7], [2/7, 4/7, 1/7], [4/7, 1/7, 2/7]] -> [[1/3, 1/3, 1/3], [1/3, 1/3, 1/3], [1/3, 1/3, 1/3]]

Zasady

  • Obowiązują standardowe luki
  • Możesz wybrać, czy chcesz matrycę stochastyczną prawą czy lewą
  • Możesz użyć dowolnego rozsądnego typu liczby, takiego jak zmiennoprzecinkowe, wymierne, dziesiętne nieskończone precyzyjne itp
  • Jest to , więc najkrótsze przesłanie w bajtach dla każdego języka zostaje ogłoszone zwycięzcą dla jego języka. Żadna odpowiedź nie zostanie zaakceptowana
HyperNeutrino
źródło
@FryAmTheEggman wygląda na to, że niektóre wcześniejsze komentarze zostały usunięte, więc może to być zbędne, ale macierze redukcyjne i okresowe są już wykluczone przez „Biorąc pod uwagę macierz stochastyczną lewą lub prawą, gdzie granica zbliżona do x nieskończoności macierzy do mocy x zbliża się do macierzy ze wszystkimi skończonymi wartościami ”, co czytam jako powiedzenie, że dane wejściowe z pewnością zbiegną się w unikalne rozwiązanie. (tzn. macierz wejściowa jest gwarantowana jako ergodyczna.)
Nathaniel,
@Nanielski To nie do końca prawda, ponieważ jeśli łańcuch jest redukowalny, możesz uzyskać wynik (jak w przypadku matrycy tożsamości), który odpowiada temu, co powiedziałeś, ale łańcuch, który opisuje, nie jest nieredukowalny i dlatego nie można zagwarantować, że dane wejściowe bądź ergodyczny (ponieważ nie będzie to pozytywny cykliczny). Gwarantowanie ergodyczności jest tym, czego chce OP, i myślę, że mają to teraz, dzięki dodatkowemu ograniczeniu wszystkich wartości wierszy, które są identyczne. Jeśli znasz lepszy sposób ograniczenia danych wejściowych bez konieczności dodawania wyjaśnienia łańcuchów Markowa, jestem pewien, że HyperNeutrino to doceni! :)
FryAmTheEggman
1
@FryAmTheEggman ah, masz rację, przepraszam. Myślałem raczej o iteracji mocy niż o podniesieniu macierzy do potęgi. (Tak więc przez „unikalne rozwiązanie” miałem na myśli „takie, które jest niezależne od punktu początkowego procesu iteracji”, ale nie ma to znaczenia tutaj.) Zgadzam się, że warunek „wszystkie wiersze są identyczne” spełnia swoje zadanie. Przypuszczam, że OP mógłby po prostu powiedzieć „łańcuch Markowa ma gwarancję ergodyczności”, co zadowoliłoby ludzi takich jak my, którzy prawdopodobnie będą się tym martwić!
Nathaniel,
W rzeczywistości, jeśli B jest rozwiązaniem dla BA = B , to również cB dla dowolnej stałej skalarnej c . Zatem niezerowe rozwiązanie nie może być ściśle unikalne, chyba że w jakiś sposób naprawisz skalę. (Wymagałoby to, aby B był stochastyczny). Oczywiście, to czy rzędy, czy kolumny B będą równe, będzie zależeć od tego, czy A jest lewy, czy prawy.
Ilmari Karonen
2
Dla każdego, kto nigdy nie nauczył się o macierzach podczas lekcji matematyki w szkole średniej i jak je pomnożyć: mathsisfun.com/algebra/matrix-multiplying.html . Musiałem to sprawdzić, aby zrozumieć, o co pytano. Może to powszechna wiedza gdzie indziej na Ziemi ..: S
Kevin Cruijssen

Odpowiedzi:

7

R ,  44  43 bajtów

function(m){X=m
while(any(X-(X=X%*%m)))0
X}

Wypróbuj online!

Po prostu mnoży, aż znajdzie stałą macierz. Najwyraźniej X!=(X=X%*%m)porównuje, a następnie zmienia przypisanie X, więc to jest miłe.

Dzięki @Vlo za odcięcie bajtu; mimo że przekreślone 44 jest nadal regularne 44.

Giuseppe
źródło
1
Zastanawiam się, dlaczego function(m){ while(any(m!=(m=m%*%m)))0 m}nie działa. Niedokładności numeryczne uniemożliwiające uruchomienie warunku zakończenia?
CodesInChaos
@CodesInChaos najprawdopodobniej to brak precyzji. Przejście na bibliotekę o dowolnej precyzji też nie pomaga - albo przekroczą limit czasu (Rmpfr), albo zawiodą (gmp) w ten sam sposób, chociaż prawdopodobnie robię coś złego.
Giuseppe,
@Giuseppe Wydaje mi się, że sugerowane podejście jest powtarzane do kwadratu, dopóki nie ulegnie zmianie? (Nie mogę odczytać R)
202729
@ user202729 tak to jest. R używa 64-bitowych liczb zmiennoprzecinkowych i wiem, że błędy propagują się dość szybko.
Giuseppe
Myślę, że ten algorytm jest niestabilny. Galaretka ma również ten sam problem. (
DO
5

Oktawa ,45 42 35 bajtów

@(A)([v,~]=eigs(A,1))/sum(v)*any(A)

Wypróbuj online!

Zaoszczędź 3 bajty dzięki Giuseppe i 7 dodatkowych dzięki Luisowi Mendo!

Wykorzystuje to, że wektor własny odpowiadający wartości własnej 1 (również maksymalnej wartości własnej) jest wektorem kolumny powtarzanym dla każdej wartości macierzy granicznej. Musimy znormalizować wektor, aby suma 1 była stochastyczna, po prostu powtarzamy go, aby wypełnić macierz. Nie jestem zbyt dobrze zaznajomiony z golfem Octave, ale nie byłem w stanie znaleźć funkcjonalnego sposobu powtarzania mnożenia, a pełny program wydaje się, że zawsze będzie dłuższy.

Możemy użyć, any(A)ponieważ z ograniczeń wiemy, że macierz musi opisywać nieredukowalny łańcuch Markowa, a więc każdy stan musi być osiągalny z innych stanów. Dlatego przynajmniej jedna wartość w każdej kolumnie musi być niezerowa.

FryAmTheEggman
źródło
Jak eigszawsze zwraca odpowiadający mu wektor własny 1? Moja pamięć łańcuchów Markowa jest trochę rozmyta.
Giuseppe,
42 bajty
Giuseppe
@Giuseppe Ponieważ matryca jest stochastyczna i kilka innych rzeczy, jej maksymalna wartość własna wynosi 1 i eigszwraca wartość zaczynając od największej wartości własnej. Dziękujemy również za golfa!
FryAmTheEggman
Ah, tak. Łańcuchy Markowa są na moim następnym egzaminie, ale ponieważ dotyczy to aktuariuszy, niektórych szczegółów całkowicie brakuje.
Giuseppe,
3

APL (Dyalog) , 18 6 bajtów

12 bajtów zapisanych dzięki @ H.PWiz

+.×⍨⍣≡

Wypróbuj online!

Uriel
źródło
+.×⍨⍣≡dla 6 bajtów. To znaczy, kwadrat, dopóki nic się nie zmieni
H.PWiz
@ H.PWiz Wierzę, że tak. Nie mam pojęcia, dlaczego tego nie zrobiłam. Dzięki!
Uriel
3

k / q, 10 bajtów

k / q, ponieważ program jest identyczny w obu językach. Kod jest tak naprawdę idiomatyczny k / q.

{$[x]/[x]}

Wyjaśnienie

{..}jest poza składnią lambda, z xparametrem niejawnym

$[x] ma $ jako operator mnożenia macierzy binarnej, pod warunkiem, że tylko jeden parametr tworzy jednoargumentowy operator, który pozostawił mnożenie przez macierz Markowa

/[x] stosuje lewe mnożenie aż do zbieżności, zaczynając od samego x.

ulucs
źródło
3

C (gcc) , 207 192 190 181 176 bajtów + 2 bajty flagi-lm

  • Zapisano piętnaście siedemnaście dwadzieścia dwa bajty dzięki ceilingcat .
  • Zapisano dziewięć bajtów; usunięcie return A;.
float*f(A,l,k,j)float*A;{for(float B[l*l],S,M=0,m=1;fabs(m-M)>1e-7;wmemcpy(A,B,l*l))for(M=m,m=0,k=l*l;k--;)for(S=j=0;j<l;)m=fmax(m,fdim(A[k],B[k]=S+=A[k/l*l+j]*A[k%l+j++*l]));}

Wypróbuj online!

Jonathan Frech
źródło
@ceilingcat Zliczając bajty flagi kompilatora, wynikiem jest 192. Uwzględniłem jednak twoją sugestię.
Jonathan Frech,
@ceilingcat Dziękuję.
Jonathan Frech,
2

Python 3 , 75 61 bajtów

f=lambda n:n if allclose(n@n,n)else f(n@n)
from numpy import*

Wypróbuj online!

W przypadkach testowych występują niedokładności typu float, więc wartości mogą się nieco różnić od dokładnych ułamków.

PS. numpy.allclose()jest używany, ponieważ numpy.array_equal()jest dłuższy i podatny na pływanie nieścisłości.

-14 bajtów Dzięki HyperNeutrino;) O tak, zapomniałem operatora @; P

Shieru Asakoto
źródło
Użyj dotzamiast matmul: D
HyperNeutrino
Właściwie weź numpy tablic jako dane wejściowe i wykonaj x=n@n: P tio.run/…
HyperNeutrino
59 bajtów
HyperNeutrino
Dodano tył z f=przodu, ponieważ jest rekurencyjnie nazywany;)
Shieru Asakoto
O tak, masz rację :) dobry telefon!
HyperNeutrino,
2

Java 8, 356 339 bajtów

import java.math.*;m->{BigDecimal t[][],q;RoundingMode x=null;for(int l=m.length,f=1,i,k;f>0;m=t.clone()){for(t=new BigDecimal[l][l],i=l*l;i-->0;)for(f=k=0;k<l;t[i/l][i%l]=(q!=null?q:q.ZERO).add(m[i/l][k].multiply(m[k++][i%l])))q=t[i/l][i%l];for(;++i<l*l;)f=t[i/l][i%l].setScale(9,x.UP).equals(m[i/l][i%l].setScale(9,x.UP))?f:1;}return m;}

-17 bajtów dzięki @ceilingcat .

Zdecydowanie niewłaściwy język .. Cholerna precyzja zmiennoprzecinkowa ..

Wyjaśnienie:

Wypróbuj online.

import java.math.*;     // Required import for BigDecimal and RoundingMode
m->{                    // Method with BigDecimal-matrix as both parameter and return-type
  BigDecimal t[][],q;   //  Temp BigDecimal-matrix
  RoundingMode x=null;  //  Static RoundingMode value to reduce bytes
  for(int l=m.length,   //  The size of the input-matrix
          f=1,          //  Flag-integer, starting at 1
          i,k;          //  Index-integers
      f>0;              //  Loop as long as the flag-integer is still 1
      m=t.clone()){     //    After every iteration: replace matrix `m` with `t`
    for(t=new BigDecimal[l][l],
                        //   Reset matrix `t`
        i=l*l;i-->0;)   //   Inner loop over the rows and columns
      for(f=k=0;        //    Set the flag-integer to 0
          k<l           //    Inner loop to multiply
          ;             //      After every iteration:
           t[i/l][i%l]=(q!=null?q:q.ZERO).add(
                        //       Sum the value at the current cell in matrix `t` with:
             m[i/l][k]  //        the same row, but column `k` of matrix `m`,
             .multiply(m[k++][i%l])))
                        //        multiplied with the same column, but row `k` of matrix `m`
        q=t[i/l][i%l];  //     Set temp `q` to the value of the current cell of `t`
    for(;++i<l*l;)      //   Loop over the rows and columns again
      f=t[i/l][i%l].setScale(9,x.UP).equals(m[i/l][i%l].setScale(9,x.UP))?
                        //    If any value in matrices `t` and `m` are the same:
         f              //     Leave the flag-integer unchanged
        :               //    Else (they aren't the same):
         1;}            //     Set the flag-integer to 1 again
  return m;}            //  Return the modified input-matrix `m` as our result
Kevin Cruijssen
źródło
Dlaczego główna funkcja jest tak pełna?
user202729,
@ user202729 Ponieważ float/ doublenie ma właściwej precyzji zmiennoprzecinkowej, java.math.BigDecimalnależy zamiast niej użyć. I zamiast po prostu +-*/, BigDecimals użyć .add(...), .subtract(...), .multiply(...), .divide(...). Więc coś tak prostego, jak się 7/10staje new BigDecimal(7).divide(new BigDecimal(10)). Ponadto, ,scale,RoundingModew dividesą wymagane dla wartości z „nieskończonymi” wartościami dziesiętnymi (jak 1/3istnienie 0.333...). Główną metodą można oczywiście grać w golfa, ale nie zawracałem sobie głowy wyszukiwaniem i zamianą, aby przekonwertować zmiennoprzecinkowe na BigDecimals.
Kevin Cruijssen
@ceilingcat Thanks!
Kevin Cruijssen