Trabajo, trabajo. Mucho tiempo alejado de estos foros. Pero ya vienen más experimentos de física, vehículos derrapando, y cosas por el estilo. Si no surge ningún contratiempo esta semana o la próxima voy a estar posteando el primero (si, genero expectativa porque puedo mwhahaha).
En este caso, una clase que escribí para un posible juego de carreras (obviamente tiene otros usos) luego de ver un libro de física.
Lo que vi fue un gráfico que representaba el torque (ver artículo en la Wikipedia) de un motor en función de las rpm. Algo muy similar a lo que se ve en ésta imagen (una coincidencia, que la primer imagen de Google sea de un Porsche, ya que en el libro el ejemplo era de un vehículo de la misma marca).
Pero el tema automovilístico no nos interesa por el momento (quizá en un futuro?). Lo que importa es ver como la curva de torque está formada por distintos datos experimentales que la convierten en una serie de funciones lineales, que tienen por ecuación y = ax + b. Nuestra clase va a representar una tabla de valores que nos va a permitir calcular el valor (aproximado) de, por ejemplo, el torque a determinadas rpm. Para ello, suponemos que dos puntos contiguos que hemos añadido a la lista (con la función addData), hay una línea recta, y así podremos inferir los valores intermedios. Esto lo podemos ver en éste gráfico, donde a partir de una serie de puntos generamos los segmentos que los unen, y sacamos a partir de ellas los valores de la función:
Código :
package
{
public class MultiPointLinearFunction
{
/*
PRIVATE VARIABLES
*/
private var tempArray:Array;
private var indexArray:Array;
private var valuesArray:Array;
private var lenght:int;
/*
CONSTRUCTOR
*/
public function MultiPointLinearFunction ()
{
tempArray = new Array ();
lenght = 0;
}
/*
METHODS
*/
// Use this function to add new "experimental data"
public function addValue (x:Number, y:Number):void
{
tempArray.push ([x, y]);
lenght++;
}
// This function generates new arrays for faster calculations the process isn't very fast so it has to be used wiselly
// Also it is neccessary to execute the function at least once before getting y coordinates
public function generateTable ():void
{
tempArray.sort (multiSort);
indexArray = new Array ();
valuesArray = new Array ();
for (var i:int = 0; i < lenght; i++)
{
indexArray.push (tempArray[i][0]);
valuesArray.push (tempArray[i][1]);
}
}
public function getY (x:Number):Number
{
var maxIndex:int;
var minIndex:int;
var rx:Number; // The closest x coordinate greater (rightX simplified as rx) than the provided x value
var lx:Number; // The closest x coordinate lesser (leftX simplified as lx) than the provided x value
var ry:Number; // The y value corresponding to the aforementioned rx
var ly:Number; // The y value corresponding to the aforementioned lx
for (var i:int = 0; i < lenght; i++)
{
if (indexArray[i] > x)
{
if (i == 0)
{
maxIndex = 1;
minIndex = 0;
rx = indexArray[maxIndex];
lx = indexArray[minIndex];
ry = valuesArray[maxIndex];
ly = valuesArray[minIndex];
return ((ry - ly) / (rx - lx)) * (x - lx) + ly;
}
else
{
maxIndex = i;
minIndex = i - 1;
rx = indexArray[maxIndex];
lx = indexArray[minIndex];
ry = valuesArray[maxIndex];
ly = valuesArray[minIndex];
return ((ry - ly) / (rx - lx)) * (x - lx) + ly;
}
}
}
maxIndex = lenght - 1;
minIndex = lenght - 2;
rx = indexArray[maxIndex];
lx = indexArray[minIndex];
ry = valuesArray[maxIndex];
ly = valuesArray[minIndex];
return ((ry - ly) / (rx - lx)) * (x - lx) + ly;
}
public function toString ():String
{
var str:String = "[object, MultiPointLinearFunction]\ninitialized = " + (indexArray != null).toString () + "\ntotalValues = " + lenght.toString ();
return str;
}
/*
IMPLEMENTATION
*/
// Sorts the Array according to the first element of the nested Arrays
private function multiSort (a:Array, b:Array):int
{
var xa:Number = a[0];
var xb:Number = b[0];
if (xa > xb)
{
return 1;
}
else if (xa < xb)
{
return -1;
}
else
{
return 0;
}
}
}
}
Código :
package
{
public class MultiPointLinearFunction
{
/*
PRIVATE VARIABLES
*/
private var tempArray:Array;
//
private var indexArray:Array;
private var valuesArray:Array;
private var indexes:int;
private var currentFunction:Function;
// Precalculate this values for faster operations
private var globalM:Number;
private var globalB:Number;
private var minX:Number;
private var maxX:Number;
/*
CONSTRUCTOR
*/
public function MultiPointLinearFunction ()
{
tempArray = new Array ();
indexes = 0;
}
/*
METHODS
*/
// Use this function to add new "experimental data"
public function addValue (x:Number, y:Number):void
{
tempArray.push ([x, y]);
indexes++;
currentFunction = bruteForce;
}
// This function generates new arrays for faster calculations the process isn't very fast so it has to be used wiselly
// Also it is neccessary to execute the function at least once before getting y coordinates
public function generateTable ():void
{
tempArray.sort (multiSort);
indexArray = new Array ();
valuesArray = new Array ();
var x1:Number, y1:Number, x2:Number, y2:Number, m:Number, b:Number;
minX = tempArray[0][0];
maxX = tempArray[indexes - 2][0];
for (var i:int = 0; i < indexes - 1; i++)
{
x1 = tempArray[i][0];
y1 = tempArray[i][1];
x2 = tempArray[i + 1][0];
y2 = tempArray[i + 1][1];
m = (y2 - y1) / (x2 - x1);
b = y1 - x1 * m;
indexArray.push (x1);
valuesArray.push (m, b);
}
if (indexes == 0)
{
globalM = globalB = 0;
}
else
{
globalM = valuesArray[0];
globalB = valuesArray[1];
}
// Choose the ideal algorithm
if (indexes <= 2)
{
currentFunction = fastCalculation;
}
else if (indexes <= 8)
{
currentFunction = bruteForce;
}
else
{
currentFunction = binarySearch;
// currentFunction = bruteForce;
}
}
public function getY (x:Number):Number
{
return currentFunction (x);
}
public function toString ():String
{
var str:String = "[object, MultiPointLinearFunction]\ninitialized = " + (indexArray != null).toString () + "\ntotalValues = " + indexes.toString ();
return str;
}
/*
IMPLEMENTATION
*/
private function fastCalculation (x:Number):Number // Used only with up to two elements (saves time by avoiding array access)
{
return globalM * x + globalB;
}
private function binarySearch (x:Number):Number // The fastest version. Yet, there is some place for optimization, poor performance for small Arrays
{
var m:Number, b:Number;
if (x <= minX)
{
m = valuesArray[0];
b = valuesArray[1];
}
else if (x >= maxX)
{
m = valuesArray[(indexes - 1) * 2 - 2];
b = valuesArray[(indexes - 1) * 2 - 1];
}
else
{
var sampleLenght:Number = indexes;
var sampleLenghtInt:int;
var sum:Number = 0;
var midValue:Number = 0;
while (sampleLenght > 2)
{
sampleLenght *= .5;
sampleLenghtInt = int (sampleLenght); // Caching the interger allows faster Array lookups and bitwise operations
midValue = indexArray[int(sampleLenghtInt + sum)];
if (midValue > x)
{
// if (sum > 0) sum -= sampleLenghtInt;
}
else if (midValue < x)
{
sum += sampleLenght;
}
else
{
// The midValue equals our query!
trace ("lucky");
sum = Math.round (sum);
m = valuesArray[sum * 2];
b = valuesArray[sum * 2 + 1];
return m * x + b;
}
}
sum = int (sum);
trace ("sum (i) = " + sum.toString ())
m = valuesArray[sum * 2];
b = valuesArray[sum * 2 + 1];
}
return m * x + b;
}
private function bruteForce (x:Number):Number // The brute force method, reccommended for any lenght between 2 and 8
{
var m:Number, b:Number;
if (x <= minX)
{
m = valuesArray[0];
b = valuesArray[1];
}
else if (x >= maxX)
{
m = valuesArray[(indexes - 1) * 2 - 2];
b = valuesArray[(indexes - 1) * 2 - 1];
}
else
{
for (var i:int = 0; i < indexes - 1; i++)
{
if (indexArray[i + 1] > x)
{
m = valuesArray[i * 2];
b = valuesArray[i * 2 + 1];
trace ("i = " + i)
i = indexes; // End the loop
}
}
}
return m * x + b;
}
// Sorts the Array according to the first element of the nested Arrays
private function multiSort (a:Array, b:Array):int
{
var xa:Number = a[0];
var xb:Number = b[0];
if (xa > xb)
{
return 1;
}
else if (xa < xb)
{
return -1;
}
else
{
return 0;
}
}
}
}
Código :
midValue = indexArray[sampleLenghtInt + sum];
Código :
midValue = indexArray[int (sampleLenghtInt + sum)];