2012-05-14 15 views
6

Para un sitio web educativo, mi propósito es dejar que los estudiantes se entretengan con las series de valores y su colección. Por ejemplo, los estudiantes pueden entrar en dos matrices para los que se calcula la correlación:Cómo calcular una serie aleatoria de valores (B) que tienen una correlación dada con una serie dada (A)

$array_x = array(5,3,6,7,4,2,9,5); 
$array_y = array(4,3,4,8,3,2,10,5); 

echo Correlation($array_x, $array_y); // 0.93439982209434 

El código para esta aplicación funciona perfectamente y se pueden encontrar en la parte inferior de este post. Sin embargo, ahora estoy enfrentando un desafío. Lo que quiero es la siguiente:

  • estudiante introduce un $ matriz_x (5,3,6,7,4,2,9,5)
  • estudiante introduce una correlación (0,9)
  • entradas del estudiante los límites de $ array_y (por ejemplo, entre 1 y 10 o entre 50 y 80)
  • el script devuelve un conjunto aleatorio (por ejemplo: 4,3,4,8,3,2,10,5) que tiene (aproximadamente) la correlación dada

Entonces, en otras palabras, el código debería funcionar como:

$array_x = array(5,3,6,7,4,2,9,5); 
$boundaries = array(1, 10); 
$correlation = 0.9; 

echo ySeries($array_x, $boundaries, $correlation); // array(4,3,4,8,3,2,10,5) 

En el foro Stackexchange matemáticas, respondió @ilya (inserta como una imagen, ya que no parece formato de látex fomulas hacer para trabajar en stackoverflow):

enter image description here

P. S. El código utilizado para el cálculo de la correlación:

function Correlation($arr1, $arr2) {   
    $correlation = 0; 
    $k = SumProductMeanDeviation($arr1, $arr2); 
    $ssmd1 = SumSquareMeanDeviation($arr1); 
    $ssmd2 = SumSquareMeanDeviation($arr2); 
    $product = $ssmd1 * $ssmd2; 
    $res = sqrt($product); 
    $correlation = $k/$res; 

    return $correlation; 
} 

function SumProductMeanDeviation($arr1, $arr2) { 
    $sum = 0; 
    $num = count($arr1); 
    for($i=0; $i < $num; $i++) { 
    $sum = $sum + ProductMeanDeviation($arr1, $arr2, $i); 
    } 
    return $sum; 
} 

function ProductMeanDeviation($arr1, $arr2, $item) { 
    return (MeanDeviation($arr1, $item) * MeanDeviation($arr2, $item)); 
} 

function SumSquareMeanDeviation($arr) { 
    $sum = 0; 
    $num = count($arr); 
    for($i = 0; $i < $num; $i++) { 
    $sum = $sum + SquareMeanDeviation($arr, $i); 
    } 
    return $sum; 
} 

function SquareMeanDeviation($arr, $item) { 
    return MeanDeviation($arr, $item) * MeanDeviation($arr, $item); 
} 

function SumMeanDeviation($arr) { 
    $sum = 0; 
    $num = count($arr); 
    for($i = 0; $i < $num; $i++) { 
    $sum = $sum + MeanDeviation($arr, $i); 
    } 
    return $sum; 
} 

function MeanDeviation($arr, $item) { 
    $average = Average($arr); 
    return $arr[$item] - $average; 
}  

function Average($arr) { 
    $sum = Sum($arr); 
    $num = count($arr); 
    return $sum/$num; 
} 

function Sum($arr) { 
    return array_sum($arr); 
} 
+0

Lo que es interesante acerca de este problema es que está buscando una forma de proporcionar a los estudiantes una herramienta que, si estoy leyendo la pregunta correctamente, no tiene una solución matemática predefinida (que es digamos, no hay una ecuación o función que algún tipo griego o francés haya inventado hace más de 500 años). Si bien el objetivo de querer llevarlo a cabo es noble (y voy a pensar en ello), básicamente estás proporcionando una herramienta/función que el estudiante no podría lograr en su estado (en otras palabras, pueden ' t conecta los números para confirmar que ya has hecho una lista propia para confirmar). – Anthony

+0

Si entiendo tu comentario correctamente, el objetivo de esta herramienta sería, por ejemplo, demostrar que si la serie A está entre 300 y 500, podría correlacionarse con una serie B si los límites de B son de 1 a 10 o mucho más , digamos de 700 a 1000. Estudié economía yo mismo, y muchos cálculos se entendieron mejor al tener herramientas que te permiten tocar algunas propiedades para ver el resultado en el otro. Ese sería el propósito de esta herramienta, en lugar de probar el teorema de algunos griegos o franceses :-) – Pr0no

+0

Definitivamente entiendo el beneficio de jugar con un teorema desde todos los ángulos. Definitivamente me ha ayudado a captar mejor la geometría avanzada (re: basic trig), entre otros modelos/sistemas. Me pregunto si tu pregunta no es tanto un problema de programación como matemático.En algunas ocasiones me di cuenta de mis propios dilemas y descubrí que preguntar en [math] (math.stackexchange.com) me ayudó a construir una mejor base para construir el código. Como dije, creo que la pregunta es válida y el objetivo es noble, pero la respuesta es probablemente más fundamental que la codificación. – Anthony

Respuesta

4

Por lo tanto, aquí está la aplicación PHP de su algoritmo que utiliza comadreja Dawkins para reducir el error gradualmente hasta que el umbral deseado .

<?php 
function sqrMeanDeviation($array, $avg) 
{ 
    $sqrMeanDeviation = 0; 
    for($i=0; $i<count($array); $i++) 
    { 
     $dev = $array[$i] - $avg; 
     $sqrMeanDeviation += $dev * $dev; 
    } 

    return $sqrMeanDeviation; 
} 

// z values are non-0 an can value between [-abs_z_bound, abs_z_bound] 
function random_z_element($abs_z_bound = 1) 
{ 
    $a = (mt_rand() % (2*$abs_z_bound)) - ($abs_z_bound-1); 
    if($a <= 0) 
     $a--; 
    return $a; 
} 

// change z a little 
function copy_z_weasel($old_array_z, $error_probability = 20 /*error possible is 1 in error_probability*/, $abs_z_bound = 1) 
{ 
    $new_z = array(); 

    for($i = 0; $i < count($old_array_z); $i++) 
     if(mt_rand() % $error_probability == 0) 
      $new_z[$i] = random_z_element($abs_z_bound); 
     else 
      $new_z[$i] = $old_array_z[$i]; 

    return $new_z; 
} 

function correlation_error($array_y, $array_x, $avg_x, $sqrMeanDeviation_x, $correlation) 
{ 
    // checking correlation 
    $avg_y = array_sum($array_y)/count($array_y); 

    $sqrMeanDeviation_y = 0; 
    $covariance_xy = 0; 

    for($i=0; $i<count($array_x); $i++) 
    { 
     $dev_y = $array_y[$i] - $avg_y; 
     $sqrMeanDeviation_y += $dev_y * $dev_y; 

     $dev_x = $array_x[$i] - $avg_x; 
     $covariance_xy += $dev_y * $dev_x; 
    } 
    $correlation_xy = $covariance_xy/sqrt($sqrMeanDeviation_x*$sqrMeanDeviation_y); 
    return abs($correlation_xy - $correlation); 
} 

function ySeries($array_x, $low_bound, $high_bound, $correlation, $threshold) 
{ 
    $array_y = array(); 

    $avg_x = array_sum($array_x)/count($array_x); 
    $sqrMeanDeviation_x = sqrMeanDeviation($array_x, $avg_x); 

    // pre-compute beta 
    $beta_x_sQMz = $sqrMeanDeviation_x * sqrt(1/($correlation*$correlation) - 1); 

    $best_array_z = array(); 
    $n = 0; 
    $error = $threshold + 1; 

    while($error > $threshold) 
    { 
     ++$n; 

     // generate z 
     $array_z = array(); 
     if(count($best_array_z) == 0) 
      for($i=0; $i<count($array_x); $i++) 
       $array_z[$i] = random_z_element(); 
     else 
      $array_z = copy_z_weasel($best_array_z); 

     $sqm_z = sqrMeanDeviation($array_z, array_sum($array_z)/count($array_z)); 
     // this being 0 implies that for every beta correlation(x,y) = 1 so just give it any random beta 
     if($sqm_z) 
      $beta = $beta_x_sQMz/$sqm_z; 
     else 
      $beta = 10; 
     // and now we have y 
     for($i=0; $i<count($array_x); $i++) 
      $array_y[$i] = $array_x[$i] + ($array_z[$i] * $beta); 

     // now, change bounds (we could do this afterwards but we want precision and y to be integers) 
     // rounding 
     $min_y = $array_y[0]; 
     $max_y = $array_y[0]; 
     for($i=1; $i<count($array_x); $i++) 
     { 
      if($array_y[$i] < $min_y) 
       $min_y = $array_y[$i]; 
      if($array_y[$i] > $max_y) 
       $max_y = $array_y[$i]; 
     } 

     $range = ($high_bound - $low_bound)/($max_y - $min_y); 
     $shift = $low_bound - $min_y; 
     for($i=0; $i<count($array_x); $i++) 
      $array_y[$i] = round($array_y[$i] * $range + $shift); 

     // get the error 
     $new_error = correlation_error($array_y, $array_x, $avg_x, $sqrMeanDeviation_x, $correlation); 

     if($new_error < $error) 
     { 
      $best_array_z = $array_z; 
      $error = $new_error; 
     } 

    } 
    echo "Correlation ", $correlation, " approched within " , $new_error, " in ", $n ," iterations.\n"; 

    return $array_y; 
} 

?> 
+0

¡Gracias! Pero lo que no entiendo ... hablas de una diferencia promedio de 0.001 ... Obtuve muchas diferencias entre -0.1 y 0.8 en una correlación solicitada de 0.7. ¿Cómo es posible esa diferencia con tus resultados? Estoy usando tu código con '$ array_x = array (5,3,6,7,4,2,9,5); $ low_bound = 100; $ high_bound = 130; $ correlación = 0.7; '¿Alguna idea? ¿Es posible mejorar la precisión de los algoritmos o dejar que la función se repita si la correlación tiene una diferencia por encima de un cierto umbral, digamos 0.05? – Pr0no

+0

Estoy sorprendido por la varianza del error (el valor abs ($ correlation_xy - $ correlation)) pero hice solo una prueba rápida, con un gran número de valores de entrada aleatorios (array_x y correlación) y el error promedio fue bajo. Es por eso que sugerí que agregue un bucle para controlar el error máximo. Pero editaré para incluir eso ya que me gusta la idea de Alex. – Cimbali

3

un enfoque simple, aunque muy ineficiente, sería comenzar con un número aleatorio en el intervalo dado y tratar de añadir más números, siempre y cuando no violen la correlación demasiado:

function ySeries(array_x, boundaries, correlation) { 
    array_y = [random(boundaries)] 
    while (len(array_y) < len(array_x)) { 
    do {  
     y = random(boundaries) 
    } while (Correlation(array_x, array_y + [y]) > correlation + epsilon) 

    array_y.push(y) 
    } 
} 

podría funcionar bien, siempre y cuando los números son pequeños

+0

como una variante: inicializar array_y = array_x y alterar los valores aleatoriamente de la misma manera que anteriormente, controlando la correlación. – Pavel

+0

¿Podrías por favor elaborar? ¿De dónde obtienes el épsilon? – Pr0no

+0

es una desviación de la correlación dada que está dispuesto a tolerar, una constante elegida por usted mismo. debería ser algo así como ... while | Correlación (..) - correlación | > epsilon – Pavel

Cuestiones relacionadas