|  | @@ -2,16 +2,17 @@ const IsNanError = TypeError('resulted in NaN');
 | 
	
		
			
				|  |  |  const FailedToConvergeError = Error('failed to converge');
 | 
	
		
			
				|  |  |  const InvalidInputsError = Error('invalid inputs');
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -export default async function GoalSeek({
 | 
	
		
			
				|  |  | -  fn,
 | 
	
		
			
				|  |  | -  fnParams,
 | 
	
		
			
				|  |  | -  percentTolerance,
 | 
	
		
			
				|  |  | -  customToleranceFn,
 | 
	
		
			
				|  |  | -  maxIterations,
 | 
	
		
			
				|  |  | -  maxStep,
 | 
	
		
			
				|  |  | -  goal,
 | 
	
		
			
				|  |  | -  independentVariableIdx,
 | 
	
		
			
				|  |  | -}) {
 | 
	
		
			
				|  |  | +export default async function GoalSeek(options) {
 | 
	
		
			
				|  |  | +  const {
 | 
	
		
			
				|  |  | +    fn,
 | 
	
		
			
				|  |  | +    fnParams,
 | 
	
		
			
				|  |  | +    percentTolerance,
 | 
	
		
			
				|  |  | +    customToleranceFn,
 | 
	
		
			
				|  |  | +    maxIterations,
 | 
	
		
			
				|  |  | +    maxStep,
 | 
	
		
			
				|  |  | +    goal,
 | 
	
		
			
				|  |  | +    independentVariableIdx,
 | 
	
		
			
				|  |  | +  } = options;
 | 
	
		
			
				|  |  |    if (typeof customToleranceFn !== 'function') {
 | 
	
		
			
				|  |  |      if (!percentTolerance) {
 | 
	
		
			
				|  |  |        throw InvalidInputsError;
 | 
	
	
		
			
				|  | @@ -19,49 +20,78 @@ export default async function GoalSeek({
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    let g;
 | 
	
		
			
				|  |  |    let y;
 | 
	
		
			
				|  |  | -  let y1;
 | 
	
		
			
				|  |  | -  let oldGuess;
 | 
	
		
			
				|  |  | +  let oldY = 0;
 | 
	
		
			
				|  |  | +  let oldGuess = fnParams[independentVariableIdx];
 | 
	
		
			
				|  |  |    let newGuess;
 | 
	
		
			
				|  |  |    let res;
 | 
	
		
			
				|  |  | +  let max;
 | 
	
		
			
				|  |  | +  let min;
 | 
	
		
			
				|  |  |    const absoluteTolerance = ((percentTolerance || 0) / 100) * goal;
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  async function getMin(minGuest) {
 | 
	
		
			
				|  |  | +    let copyParams = [...fnParams]
 | 
	
		
			
				|  |  | +    copyParams[independentVariableIdx] = minGuest
 | 
	
		
			
				|  |  | +    res = await fn(...copyParams);
 | 
	
		
			
				|  |  | +    if (res > goal) {
 | 
	
		
			
				|  |  | +      minGuest -= oldGuess / 2;
 | 
	
		
			
				|  |  | +      await getMin(minGuest);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return minGuest;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  async function getMax(maxGuest) {
 | 
	
		
			
				|  |  | +    let copyParams = [...fnParams]
 | 
	
		
			
				|  |  | +    copyParams[independentVariableIdx] = maxGuest
 | 
	
		
			
				|  |  | +    res = await fn(...copyParams);
 | 
	
		
			
				|  |  | +    if (res < goal) {
 | 
	
		
			
				|  |  | +      maxGuest += oldGuess / 2;
 | 
	
		
			
				|  |  | +      await getMax(maxGuest);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return maxGuest;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    // iterate through the guesses
 | 
	
		
			
				|  |  |    for (let i = 0; i < maxIterations; i++) {
 | 
	
		
			
				|  |  |      // define the root of the function as the error
 | 
	
		
			
				|  |  |      res = await fn(...fnParams);
 | 
	
		
			
				|  |  |      y = res - goal;
 | 
	
		
			
				|  |  |      if (isNaN(y)) throw IsNanError;
 | 
	
		
			
				|  |  | -    // was our initial guess a good one?
 | 
	
		
			
				|  |  | +    // 判断是否满足条件
 | 
	
		
			
				|  |  |      if (typeof customToleranceFn !== 'function') {
 | 
	
		
			
				|  |  |        if (Math.abs(y) <= Math.abs(absoluteTolerance)) return fnParams[independentVariableIdx];
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  |        if (customToleranceFn(res)) return fnParams[independentVariableIdx];
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    // set the new guess, correcting for maxStep
 | 
	
		
			
				|  |  | +    // 获取要改变的变量
 | 
	
		
			
				|  |  |      oldGuess = fnParams[independentVariableIdx];
 | 
	
		
			
				|  |  | -    newGuess = oldGuess + y;
 | 
	
		
			
				|  |  | -    if (Math.abs(newGuess - oldGuess) > maxStep) {
 | 
	
		
			
				|  |  | -      if (newGuess > oldGuess) {
 | 
	
		
			
				|  |  | -        newGuess = oldGuess + maxStep;
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        newGuess = oldGuess - maxStep;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // newGuess = oldGuess + y;
 | 
	
		
			
				|  |  | +    // if (Math.abs(newGuess - oldGuess) > maxStep) {
 | 
	
		
			
				|  |  | +    //   if (newGuess > oldGuess) {
 | 
	
		
			
				|  |  | +    //     newGuess = oldGuess + maxStep;
 | 
	
		
			
				|  |  | +    //   } else {
 | 
	
		
			
				|  |  | +    //     newGuess = oldGuess - maxStep;
 | 
	
		
			
				|  |  | +    //   }
 | 
	
		
			
				|  |  | +    // }
 | 
	
		
			
				|  |  | +    // 获取最大值和最小值
 | 
	
		
			
				|  |  | +    if (y > 0) {
 | 
	
		
			
				|  |  | +      max = oldGuess;
 | 
	
		
			
				|  |  | +      if (!min) {
 | 
	
		
			
				|  |  | +        min = await getMin(oldGuess / 2);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    fnParams[independentVariableIdx] = newGuess;
 | 
	
		
			
				|  |  | -    // re-run the fn with the new guess
 | 
	
		
			
				|  |  | -    y1 = (await fn(...fnParams)) - goal;
 | 
	
		
			
				|  |  | -    if (isNaN(y1)) throw IsNanError;
 | 
	
		
			
				|  |  | -    // calculate the error
 | 
	
		
			
				|  |  | -    g = (y1 - y) / y;
 | 
	
		
			
				|  |  | -    if (g === 0) g = 0.0001;
 | 
	
		
			
				|  |  | -    // set the new guess based on the error, correcting for maxStep
 | 
	
		
			
				|  |  | -    newGuess = oldGuess - y / g;
 | 
	
		
			
				|  |  | -    if (maxStep && Math.abs(newGuess - oldGuess) > maxStep) {
 | 
	
		
			
				|  |  | -      if (newGuess > oldGuess) {
 | 
	
		
			
				|  |  | -        newGuess = oldGuess + maxStep;
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        newGuess = oldGuess - maxStep;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      min = oldGuess;
 | 
	
		
			
				|  |  | +      if (!max) {
 | 
	
		
			
				|  |  | +        max = await getMax(oldGuess * 2);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    // if(oldY) {
 | 
	
		
			
				|  |  | +    //   if(y > oldY) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    //   }
 | 
	
		
			
				|  |  | +    // }
 | 
	
		
			
				|  |  | +    // oldY = y;
 | 
	
		
			
				|  |  | +    // 利用二分法查询
 | 
	
		
			
				|  |  | +    newGuess = (min + max) / 2;
 | 
	
		
			
				|  |  |      fnParams[independentVariableIdx] = newGuess;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    // done with iterations, and we failed to converge
 |