123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- const IsNanError = TypeError('resulted in NaN');
- const FailedToConvergeError = Error('failed to converge');
- const InvalidInputsError = Error('invalid inputs');
- export default async function GoalSeek(options) {
- const {
- fn,
- fnParams,
- percentTolerance,
- customToleranceFn,
- maxIterations,
- maxStep,
- goal,
- independentVariableIdx,
- } = options;
- if (typeof customToleranceFn !== 'function') {
- if (!percentTolerance) {
- throw InvalidInputsError;
- }
- }
- let g;
- let y;
- 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;
- // 判断是否满足条件
- if (typeof customToleranceFn !== 'function') {
- if (Math.abs(y) <= Math.abs(absoluteTolerance)) return fnParams[independentVariableIdx];
- } else {
- if (customToleranceFn(res)) return fnParams[independentVariableIdx];
- }
- // 获取要改变的变量
- oldGuess = fnParams[independentVariableIdx];
- // 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);
- }
- } 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
- throw FailedToConvergeError;
- }
- // const fn = (x, y) => x / y;
- // const fnParams = [2037375, 15897178];
- // const customToleranceFn = (x) => {
- // return x < 1;
- // };
- // try {
- // const result = goalSeek({
- // fn,
- // fnParams,
- // customToleranceFn,
- // maxIterations: 1000,
- // maxStep: 0.01,
- // goal: 0.15,
- // independentVariableIdx: 0,
- // });
- // console.log(`result: ${result}`);
- // } catch (e) {
- // console.log("error", e);
- // }
|