Convergencia de un proceso de Markov

10

Desafío

Dada una matriz estocástica izquierda o derecha donde el límite a medida que x se aproxima al infinito de la matriz a la potencia de x se aproxima a una matriz con todos los valores finitos, devuelve la matriz a la que converge la matriz. Básicamente, desea seguir multiplicando la matriz por sí misma hasta que el resultado ya no cambie.

Casos de prueba

[[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]]

Reglas

  • Se aplican lagunas estándar
  • Puede elegir si desea una matriz estocástica derecha o izquierda
  • Puede usar cualquier tipo de número razonable, como flotantes, racionales, decimales de precisión infinita, etc.
  • Este es el , por lo que el envío más corto en bytes para cada idioma se declara ganador de su idioma. No se aceptarán respuestas.
Hiperneutrino
fuente
@FryAmTheEggman parece que se han eliminado algunos comentarios anteriores, por lo que esto podría ser redundante, pero las matrices reducibles y periódicas ya están excluidas por "Dada una matriz estocástica izquierda o derecha donde el límite a medida que x se aproxima al infinito de la matriz a la potencia of x se acerca a una matriz con todos los valores finitos ", que leí diciendo que la entrada está garantizada para converger en una solución única. (es decir, se garantiza que la matriz de entrada sea ergódica).
Nathaniel
@Nathaniel Eso no es del todo cierto, ya que si la cadena es reducible, puede tener un resultado (como para la matriz de identidad) que cumple con lo que dijo pero la cadena que describe no es irreducible y, por lo tanto, no se garantizará la entrada ser ergódico (ya que no será positivo recurrente). Garantizar la ergodicidad es lo que quiere el OP, y creo que lo tienen ahora, gracias a la restricción adicional de que todos los valores de las filas son idénticos. Si conoce una mejor manera de restringir la entrada sin necesidad de agregar una explicación de las cadenas de Markov, ¡estoy seguro de que HyperNeutrino lo agradecería! :)
FryAmTheEggman
1
@FryAmTheEggman ah, tienes razón, lo siento. Estaba pensando en la iteración de potencia en lugar de elevar la matriz a una potencia. (Entonces, por "solución única" quise decir "uno que es independiente del punto de partida del proceso de iteración", pero eso no es relevante aquí.) Estoy de acuerdo en que la condición 'todas las filas son idénticas' hace el trabajo. Supongo que el OP podría simplemente decir "se garantiza que la cadena de Markov será ergódica", ¡lo que satisfaría a personas como nosotros que probablemente se preocuparán por ello!
Nathaniel
En realidad, si B es una solución para BA = B , entonces también lo es cB para cualquier constante escalar c . Por lo tanto, una solución distinta de cero no puede ser estrictamente única, a menos que de alguna manera arregle la escala. (Requerir que B sea ​​estocástico lo haría.) Además, obviamente, si las filas o las columnas de B son iguales dependerá de si A es estocástico izquierdo o derecho.
Ilmari Karonen
2
Para cualquier otra persona que nunca aprendió sobre matrices durante la clase de matemáticas en la escuela secundaria y cómo multiplicarlas: mathsisfun.com/algebra/matrix-multiplying.html . Tuve que buscarlo para entender lo que se preguntaba ... Tal vez es de conocimiento común en otros lugares de la Tierra ...: S
Kevin Cruijssen

Respuestas:

7

R ,  44  43 bytes

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

Pruébalo en línea!

Simplemente sigue multiplicándose hasta que encuentra una matriz fija. Aparentemente X!=(X=X%*%m)hace la comparación, luego reasigna X, así que eso está bien.

Gracias a @Vlo por eliminar un byte; a pesar de que tachado 44 sigue siendo regular 44.

Giuseppe
fuente
1
Me pregunto por qué function(m){ while(any(m!=(m=m%*%m)))0 m}no funciona. ¿Imprecisiones numéricas que impiden que se active la condición de terminación?
CodesInChaos
@CodesInChaos probablemente sea una falta de precisión. Cambiar a una biblioteca de precisión arbitraria tampoco ayuda, ya sea que caducan (Rmpfr) o fallan (gmp) de la misma manera, aunque probablemente estoy haciendo algo mal.
Giuseppe
@Giuseppe ¿Supongo que el enfoque sugerido es la cuadratura repetida hasta que ya no cambie? (No puedo leer R)
user202729
@ user202729 sí, lo es. R usa números de coma flotante de 64 bits y sé que los errores se propagan con bastante rapidez.
Giuseppe
Creo que ese algoritmo es inestable. Jelly también tiene el mismo problema. (TODO prueba esto y encuentra una alternativa)
user202729
5

Octava ,45 42 35 bytes

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

Pruébalo en línea!

¡Ahorré 3 bytes gracias a Giuseppe y 7 más gracias a Luis Mendo!

Esto utiliza que el vector propio correspondiente al valor propio 1 (también el valor propio máximo) es el vector de columna que se repite para cada valor de la matriz limitante. Tenemos que normalizar el vector para que tenga la suma 1 para que sea estocástico, luego simplemente lo repetimos para completar la matriz. No estoy muy versado en el golf Octave, pero no he podido encontrar una manera funcional de hacer multiplicaciones repetidas, y un programa completo parece que siempre será más largo que esto.

Podemos usarlo any(A)ya que, a partir de las restricciones, sabemos que la matriz debe describir una cadena de Markov irreducible, por lo que cada estado debe ser accesible desde los otros estados. Por lo tanto, al menos un valor en cada columna debe ser distinto de cero.

FryAmTheEggman
fuente
¿Cómo eigssiempre devuelve el vector propio correspondiente 1? Mi recuerdo de las cadenas de Markov es un poco borroso.
Giuseppe
42 bytes
Giuseppe
@Giuseppe Debido a que la matriz es estocástica, y un par de cosas más, su valor propio máximo es 1, y eigsregresa a partir del valor propio más grande. Además, gracias por el golf!
FryAmTheEggman
Ah bien. Las cadenas de Markov están en mi próximo examen, pero como es para actuarios, faltan algunos detalles.
Giuseppe
3

APL (Dyalog) , 18 6 bytes

12 bytes guardados gracias a @ H.PWiz

+.×⍨⍣≡

Pruébalo en línea!

Uriel
fuente
+.×⍨⍣≡por 6 bytes. Es decir, cuadrado hasta que nada cambie
H.PWiz
@ H.PWiz, creo que sí. No tengo idea de por qué no lo había hecho en primer lugar. ¡Gracias!
Uriel
3

k / q, 10 bytes

k / q porque el programa es idéntico en ambos idiomas. El código es realmente idiomático k / q.

{$[x]/[x]}

Explicación

{..}está fuera de la sintaxis lambda, con xcomo parámetro implícito

$[x] tiene $ como operador de multiplicación de matriz binaria, proporcionar solo un parámetro crea un operador unario que deja multiplicados por la matriz de Markov

/[x] aplica la multiplicación izquierda hasta la convergencia, comenzando con la propia x.

ulucs
fuente
3

C (gcc) , 207 192 190 181 176 bytes + 2 bytes de bandera-lm

  • Salvó quince diecisiete veintidós bytes gracias a ceilingcat .
  • Guardado nueve bytes; la eliminación 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]));}

Pruébalo en línea!

Jonathan Frech
fuente
@ceilingcat Contando los bytes del indicador del compilador, esto da como resultado 192 nuevamente. Incluyó su sugerencia, no obstante.
Jonathan Frech
@ceilingcat Gracias.
Jonathan Frech
2

Python 3 , 75 61 bytes

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

Pruébalo en línea!

En los casos de prueba hay imprecisiones de flotación, por lo que los valores pueden diferir un poco de las fracciones exactas.

PD. numpy.allclose()se usa porque numpy.array_equal()es más largo y propenso a imprecisiones de flotación.

-14 bytes Gracias HyperNeutrino;) Oh sí, olvidé el operador @; P

Shieru Asakoto
fuente
Usar en dotlugar de matmul: D
HyperNeutrino
En realidad, tome matrices numpy como entrada y haga x=n@n: P tio.run/…
HyperNeutrino
59 bytes
HyperNeutrino
Se agregó nuevamente f=al frente porque se llama recursivamente;)
Shieru Asakoto
Oh sí, tienes razón :) buena llamada!
HyperNeutrino
2

Java 8, 356339 bytes

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 bytes gracias a @ceilingcat .

Definitivamente no es el lenguaje correcto ... Maldita precisión de coma flotante ...

Explicación:

Pruébalo en línea.

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
fuente
¿Por qué la función principal es tan detallada?
user202729
@ user202729 Debido a que float/ doubleno tiene la precisión de coma flotante adecuada, java.math.BigDecimaldebería usarse en su lugar. Y en lugar de simplemente +-*/, BigDecimals utilizan .add(...), .subtract(...), .multiply(...), .divide(...). Entonces algo tan simple como se 7/10vuelve new BigDecimal(7).divide(new BigDecimal(10)). Además, el ,scale,RoundingModeen el dividese requieren para valores con 'infinito' decimal valores (como 1/3ser 0.333...). Por supuesto, el método principal se puede jugar al golf, pero no me molesté cuando hice una búsqueda y reemplazo para convertir los flotadores a BigDecimals.
Kevin Cruijssen
@ceilingcat Gracias!
Kevin Cruijssen