zoukankan      html  css  js  c++  java
  • js/ts 获取通过子项数字组合能组合成的最优组合

    例如:有一组数字,[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],需要从这一组数字中寻找最优的数字组合,让数字组合的总和最接近但不超过这个某个值,比如14,且组合的数字数量尽量多。

    > [4, 10]有2个数字,可构成等于14,最接近14的数字组合。

    > [1, 2, 3, 4]有4个数字,可构成等于10,较接近14的数字组合。

    以上2个组合,第2个组合才是更优,是否还有更优的数字组合呢?

    通过以下封装的方法即可求出是否还有更优的数字组合,使用如下:

    // 示例1,目标值14
    this.getOptimalGroup(14, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    // 更优组合,[5, 2, 3, 4] 总和: 14
    
    // 示例2,目标值26,数字可以无序排列
    this.getOptimalGroup(26, [1.5, 16, 1.6, 220, 190, 48, 8, 20, 25, 140, , 14, 3])
    // 最优组合,[1.5, 1.6, 14, 8] 总和: 25.1

    [推荐] 方式一:

    /**
     * 获取通过子项数字组合能组合成的最优组合
     * @param target 目标数字
     * @param arr 子项数字数组
     */
    private getOptimalGroup(target: number, arr: Array<number>): number {
      console.log('%c
    原始子项数组:', 'color: blue;', arr)
      // 将子项数字数组从小到大排序
      arr.sort((a, b) => a - b)
    
      // 理论最优数字组合
      let theoryOptimalArr: Array<number> = [],
        // 理论最接近目标的数字
        theoryOptimalTarget = 0,
        // 目标数字副本
        targetCopy = target
    
      //#region 计算理论最优
      console.group('计算理论最优')
      for (let i = 0; i < arr.length; i++) {
        const num = arr[i]
        if (num > targetCopy) break
    
        // 消耗数字
        targetCopy -= num
        // 加入到理论最优数字组合
        theoryOptimalArr.push(num)
      }
      // 计算理论最优目标数字
      theoryOptimalTarget = theoryOptimalArr.reduce((total, item) => total += item, 0)
      console.log('%c理论最接近目标的数字:', 'color: orange;', theoryOptimalTarget)
      console.log('%c理论最优数字组合:', 'color: orange;', theoryOptimalArr)
      console.groupEnd()
      //#endregion
    
      //#region 寻找更优组合
      console.group('寻找更优组合')
      // 当前最优目标数字,默认初始等于理论最优目标数字
      let currOptimalTarget = theoryOptimalTarget,
        // 理论最优数字组合数组副本
        theoryOptimalArrCopy = [...theoryOptimalArr],
        // 当前最优数字组合
        currOptimalArr: Array<number> = [],
        // 当前数字组合累计值
        currTotal = 0,
        // 是否匹配
        match = false
      const otherNums = arr.filter(x => !theoryOptimalArr.includes(x) && x < target)
      otherNums.length && console.log('%c可供用于替换的数字(按从小到大的顺序排列):', 'color: gray;', otherNums)
      for (let i = 0; i < theoryOptimalArr.length; i++) {
        // 重置理论最优数字组合数组副本为原始值,重新开始下一轮替换
        theoryOptimalArrCopy = [...theoryOptimalArr]
        console.group(`[第 ${i + 1} 轮替换] 替换理论最优数字组合数组的第 ${i + 1} 个数字:`, theoryOptimalArr[i])
        for (let j = 0; j < otherNums.length; j++) {
          const replaceNum = otherNums[j]
          theoryOptimalArrCopy[i] = replaceNum
          currTotal = common.parseRMB(theoryOptimalArrCopy.reduce((total, item) => total += item, 0))
          match = currTotal > theoryOptimalTarget && currTotal <= target && currTotal > currOptimalTarget
          const followOtherNums = otherNums.filter(x => x > replaceNum)
          if (!match) {
            if (currTotal > target) {
              console.log(`%c[超出范围] 当前数字 ${replaceNum} 替换数字 ${theoryOptimalArr[i]} 后,总和 ${currTotal} 超出目标数字 ${target} -> ${common.parseRMB(currTotal - target)},${followOtherNums.length ? `后续其他数字 [${followOtherNums.join(', ')}] 不再继续替换` : '后续无其他数字替换'}`, 'color: red;')
              break
            } else if (currTotal < currOptimalTarget) {
              console.log(`%c[不能作为更优] 当前数字 ${replaceNum} 替换数字 ${theoryOptimalArr[i]} 后,总和 ${currTotal} 未超出目标数字 ${target},但没有比当前最优目标数字 ${currOptimalTarget} 更优,${followOtherNums.length ? `后续其他数字 [${followOtherNums.join(', ')}] 继续替换` : '后续无其他数字替换'}`, 'color: red;')
            }
          }
    
          if (currTotal > currOptimalTarget) {
            currOptimalTarget = currTotal
            currOptimalArr = [...theoryOptimalArrCopy]
            console.log(`%c[找到更优] 当前数字 ${replaceNum} 替换数字 ${theoryOptimalArr[i]} 后,总和 ${currTotal} 未超出目标数字 ${target},可组合成更优、更接近目标数字的组合:`, 'color: blue;', currOptimalArr, '总和:', currOptimalTarget, `${followOtherNums.length ? `后续其他数字 [${followOtherNums.join(', ')}] 继续替换` : '后续无其他数字替换'}`)
          }
        }
        console.groupEnd()
      }
    
      console.log('
    ')
      if (currOptimalArr.length) {
        console.log('%c最优的数字组合:', 'color: green;', `${currOptimalArr.map(x => `${x}`).join(' + ')} = ${common.parseRMB(currOptimalArr.reduce((total, item) => total += item, 0))}`)
      } else {
        console.log('%c没有更优的数字组合,理论最优数字组合可作为最优', 'color: red;', `${theoryOptimalArr.map(x => `${x}`).join(' + ')} = ${common.parseRMB(theoryOptimalArr.reduce((total, item) => total += item, 0))}`)
      }
      console.log('%c最接近目标的数字:', 'color: green;', `${currOptimalTarget}元`)
      console.groupEnd()
      //#endregion
    
      return currOptimalTarget
    }

    方式二:

    /**
     * 获取通过子项数字组合能组合成的最优组合
     * @param total 目标数字
     * @param arr 子项数字数组
     */
    private GetOptimalGroup(total: number, arr: Array<number>) {
      arr.sort((a, b) => { return a - b });
      let sum = 0;
      //原始数组
      let first = [];
      let firstsum = 0
      for (let i = 0; i < arr.length; i++) {
        sum += arr[i];
        if (sum > total) break;
        first.push(arr[i]);
        firstsum += arr[i];
      }
      console.log('原始最大个数:' + first.length + ',最大和:' + firstsum + ',组合:' + JSON.stringify(first));
      //可以替换的所有数字
      let canchange = [];
      for (let i = 0; i < arr.length; i++) {
        const arr_item = arr[i];
        for (let j = 0; j < first.length; j++) {
          const first_item = first[j];
          if (arr_item > first[0] && first.lastIndexOf(arr_item) < 0 && arr_item - first_item < (total - firstsum) && arr_item - first_item > 0) {
            canchange.push(arr_item);
            break;
          }
        }
      }
      console.log('可替换数字:' + JSON.stringify(canchange));
    
      let tmpsum = firstsum;
      let rightpair = [];
      //和最大的新组合
      let newArr = [];
      //逐个替换
      for (let i = 0; i < first.length; i++) {
        const first_item = first[i];
        for (let j = 0; j < canchange.length; j++) {
          const canchange_item = canchange[j];
          if (firstsum + canchange_item - first_item > tmpsum && firstsum + canchange_item - first_item < total) {
            tmpsum = firstsum + canchange_item - first_item;
            newArr = first.concat();
            newArr[i] = canchange_item;
            console.log('【有效替换】【' + canchange_item + '】替换【' + first_item + '】;最大个数:' + newArr.length + ',最大和:' + tmpsum + ',组合:' + JSON.stringify(newArr));
            rightpair = [newArr];
    
          } else if (firstsum + canchange_item - first_item == tmpsum) { //另外答案
            newArr = first.concat();
            newArr[i] = canchange_item;
            console.log('【有效替换】【' + canchange_item + '】替换【' + first_item + '】;最大个数:' + newArr.length + ',最大和:' + tmpsum + ',组合:' + JSON.stringify(newArr));
            rightpair.push(newArr)
          } else {
            console.log('【无效替换】【' + canchange_item + '】替换【' + first_item + '】;最大和:' + (firstsum + canchange_item - first_item));
          }
        }
      }
      console.log('最终最大个数:' + newArr.length + ',最大和:' + tmpsum + ',组合:' + JSON.stringify(rightpair));
    }

    附方式一 javascript 方式实现代码

    /**
     * 展开数组
     */
    var __spreadArray = (this && this.__spreadArray) || function (to, from) {
      for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
        to[j] = from[i];
      return to;
    };
    /**
     * 获取通过子项数字组合能组合成的最优组合
     * @param target 目标数字
     * @param arr 子项数字数组
     */
    function getOptimalGroup (target, arr) {
      console.log('%c
    原始子项数组:', 'color: blue;', arr);
      // 将子项数字数组从小到大排序
      arr.sort(function (a, b) { return a - b; });
      // 理论最优数字组合
      var theoryOptimalArr = [],
        // 理论最接近目标的数字
        theoryOptimalTarget = 0,
        // 目标数字副本
        targetCopy = target;
      //#region 计算理论最优
      console.group('计算理论最优');
      for (var i = 0; i < arr.length; i++) {
        var num = arr[i];
        if (num > targetCopy)
          break;
        // 消耗数字
        targetCopy -= num;
        // 加入到理论最优数字组合
        theoryOptimalArr.push(num);
      }
      // 计算理论最优目标数字
      theoryOptimalTarget = theoryOptimalArr.reduce(function (total, item) { return total += item; }, 0);
      console.log('%c理论最接近目标的数字:', 'color: orange;', theoryOptimalTarget);
      console.log('%c理论最优数字组合:', 'color: orange;', theoryOptimalArr);
      console.groupEnd();
      //#endregion
      //#region 寻找更优组合
      console.group('寻找更优组合');
      // 当前最优目标数字,默认初始等于理论最优目标数字
      var currOptimalTarget = theoryOptimalTarget,
        // 理论最优数字组合数组副本
        theoryOptimalArrCopy = __spreadArray([], theoryOptimalArr),
        // 当前最优数字组合
        currOptimalArr = [],
        // 当前数字组合累计值
        currTotal = 0,
        // 是否匹配
        match = false;
      var otherNums = arr.filter(function (x) { return !theoryOptimalArr.includes(x) && x < target; });
      otherNums.length && console.log('%c可供用于替换的数字(按从小到大的顺序排列):', 'color: gray;', otherNums);
      for (let i = 0; i < theoryOptimalArr.length; i++) {
        // 重置理论最优数字组合数组副本为原始值,重新开始下一轮替换
        theoryOptimalArrCopy = [...theoryOptimalArr]
        console.group(`[第 ${i + 1} 轮替换] 替换理论最优数字组合数组的第 ${i + 1} 个数字:`, theoryOptimalArr[i])
        for (let j = 0; j < otherNums.length; j++) {
          const replaceNum = otherNums[j]
          theoryOptimalArrCopy[i] = replaceNum
          currTotal = parseRMB(theoryOptimalArrCopy.reduce((total, item) => total += item, 0))
          match = currTotal > theoryOptimalTarget && currTotal <= target && currTotal > currOptimalTarget
          const followOtherNums = otherNums.filter(x => x > replaceNum)
          if (!match) {
            if (currTotal > target) {
              console.log(`%c[超出范围] 当前数字 ${replaceNum} 替换数字 ${theoryOptimalArr[i]} 后,总和 ${currTotal} 超出目标数字 ${target} -> ${parseRMB(currTotal - target)},${followOtherNums.length ? `后续其他数字 [${followOtherNums.join(', ')}] 不再继续替换` : '后续无其他数字替换'}`, 'color: red;')
              break
            } else if (currTotal < currOptimalTarget) {
              console.log(`%c[不能作为更优] 当前数字 ${replaceNum} 替换数字 ${theoryOptimalArr[i]} 后,总和 ${currTotal} 未超出目标数字 ${target},但没有比当前最优目标数字 ${currOptimalTarget} 更优,${followOtherNums.length ? `后续其他数字 [${followOtherNums.join(', ')}] 继续替换` : '后续无其他数字替换'}`, 'color: red;')
            }
          }
    
          if (currTotal > currOptimalTarget) {
            currOptimalTarget = currTotal
            currOptimalArr = [...theoryOptimalArrCopy]
            console.log(`%c[找到更优] 当前数字 ${replaceNum} 替换数字 ${theoryOptimalArr[i]} 后,总和 ${currTotal} 未超出目标数字 ${target},可组合成更优、更接近目标数字的组合:`, 'color: blue;', currOptimalArr, '总和:', currOptimalTarget, `${followOtherNums.length ? `后续其他数字 [${followOtherNums.join(', ')}] 继续替换` : '后续无其他数字替换'}`)
          }
        }
        console.groupEnd()
      }
    
      console.log('
    ');
      if (currOptimalArr.length) {
        console.log('%c最优的数字组合:', 'color: green;', currOptimalArr.map(function (x) { return "" + x; }).join(' + ') + " = " + parseRMB(currOptimalArr.reduce(function (total, item) { return total += item; }, 0)));
      }
      else {
        console.log('%c没有更优的数字组合,理论最优数字组合可作为最优', 'color: red;', theoryOptimalArr.map(function (x) { return "" + x; }).join(' + ') + " = " + parseRMB(theoryOptimalArr.reduce(function (total, item) { return total += item; }, 0)));
      }
      console.log('%c最接近目标的数字:', 'color: green;', currOptimalTarget + "u5143");
      console.groupEnd();
      //#endregion
      return currOptimalTarget;
    }
    
    /**
     * 转换为人民币数值
     * @param value 值
     * @returns 人民币数值
     */
    function parseRMB (value) {
      var result = Math.floor(parseFloat((value * 100).toString())) / 100;
      result = Number(result.toFixed(1));
      return result;
    }
    // 调用
    getOptimalGroup(24, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
    嘴角上扬,记得微笑
  • 相关阅读:
    java坏境内存不够用 大量占用swap 临时加swap
    磁盘分区
    简述raid0,raid1,raid5,raid10 的工作原理及特点
    给用户提权
    用户的环境变量被删除了
    定时任务
    linux权限
    kafka部署
    数据仓库
    kylin
  • 原文地址:https://www.cnblogs.com/jardeng/p/15250200.html
Copyright © 2011-2022 走看看