예제 #1
0
        public static double Avg(Tuple <double, double>[] weightedValues)
        {
            if (weightedValues == null || weightedValues.Length == 0)
            {
                return(0);
            }

            double totalWeight = weightedValues.Sum(o => o.Item2);

            if (Math1D.IsNearZero(totalWeight))
            {
                return(weightedValues.Average(o => o.Item1));
            }

            double sum = weightedValues.Sum(o => o.Item1 * o.Item2);

            return(sum / totalWeight);
        }
예제 #2
0
        /// <summary>
        /// This will try various inputs and come up with an input that produces the desired output
        /// NOTE: This method will only try positive inputs
        /// NOTE: This method assumes an increase in input causes the output to increase
        /// </summary>
        /// <remarks>
        /// NOTE: This first attempt doesn't try to guess the power of the equation (linear, square, sqrt, etc).
        /// It starts at 1, then either keeps multiplying or dividing by 10 until a high and low are found
        /// Then it does a binary search between high and low
        ///
        /// TODO: Take a parameter: Approximate function
        ///     this is the definition of a function that could be run in reverse to approximate a good starting point, and help with coming up with
        ///     decent input attempts.  This should be refined within this method and returned by this method.  Use Math.NET's Fit.Polynomial?
        /// http://stackoverflow.com/questions/20786756/use-math-nets-fit-polynomial-method-on-functions-of-multiple-parameters
        /// http://www.mathdotnet.com/
        ///
        /// TODO: Think about how to make this method more robust.  It could be a helper for a controller (thrust controller)
        /// </remarks>
        public static double GetInputForDesiredOutput_PosInput_PosCorrelation(double desiredOutput, double allowableError, Func <double, double> getOutput, int?maxIterations = 5000)
        {
            if (allowableError <= 0)
            {
                throw new ArgumentException("allowableError must be positive: " + allowableError.ToString());
            }

            // Start with an input of 1
            Tuple <double, double> current = Tuple.Create(1d, getOutput(1d));

            if (IsInRange(current.Item2, desiredOutput, allowableError))
            {
                return(current.Item1);       // lucky guess
            }

            // See if it's above or below the desired output
            Tuple <double, double> low  = null;
            Tuple <double, double> high = null;

            if (current.Item2 < desiredOutput)
            {
                low = current;
            }
            else
            {
                high = current;
            }

            int count = 0;

            while (maxIterations == null || count < maxIterations.Value)
            {
                double nextInput;

                if (low == null)
                {
                    #region too high

                    // Floor hasn't been found.  Try something smaller

                    nextInput = high.Item1 / 10d;

                    current = Tuple.Create(nextInput, getOutput(nextInput));

                    if (current.Item2 < desiredOutput)
                    {
                        low = current;      // floor and ceiling are now known
                    }
                    else if (current.Item2 < high.Item2)
                    {
                        high = current;     // floor still isn't known, but this is closer than the previous ceiling
                    }

                    #endregion
                }
                else if (high == null)
                {
                    #region too low

                    // Ceiling hasn't been found.  Try something larger

                    nextInput = low.Item1 * 10d;

                    current = Tuple.Create(nextInput, getOutput(nextInput));

                    if (current.Item2 > desiredOutput)
                    {
                        high = current;     // floor and ceiling are now known
                    }
                    else if (current.Item2 > low.Item2)
                    {
                        low = current;      // ceiling still isn't known, but this is closer than the previous floor
                    }

                    #endregion
                }
                else
                {
                    #region straddle

                    // Floor and ceiling are known.  Try an input that is between them

                    nextInput = Math1D.Avg(low.Item1, high.Item1);

                    current = Tuple.Create(nextInput, getOutput(nextInput));

                    if (current.Item2 < desiredOutput && current.Item2 > low.Item2)
                    {
                        low = current;      // increase the floor
                    }
                    else if (current.Item2 > desiredOutput && current.Item2 < high.Item2)
                    {
                        high = current;     // decrease the ceiling
                    }

                    #endregion
                }

                if (IsInRange(current.Item2, desiredOutput, allowableError))
                {
                    return(current.Item1);
                }

                count++;
            }

            //TODO: Take in a param whether to throw an exception or return the best guess
            throw new ApplicationException("Couldn't find a solution");
        }
예제 #3
0
 public static bool IsInvalid(this double item)
 {
     return(Math1D.IsInvalid(item));
 }