数字谜题解

2,790 阅读2分钟
function calc(a, option, b) {
  let res;
  let formula;
  if (option == '+') {
    res = a.value + b.value;
    formula=a.formula+'+'+b.formula;
  }
  else if (option == '-') {
    res = a.value - b.value;
    formula=a.formula+'-'+b.formula;
  }
  else if (option == '*') {
    res = a.value * b.value;
    formula=a.formula+'*'+b.formula;
  }
  else if (option == '/') {
    res =a.value / b.value;
    formula=a.formula+'/'+b.formula;
  }
  else if(option == '.'){
    res=Number([a.value , b.value].join(""));
    formula=a.formula+'.'+b.formula;
  }
  if (res < 0) {
    res= NaN;
  }
  else if (res % 1 !== 0) {
    res=NaN
  }
  return  {
    value:res,
    formula:'('+formula+')',
  };;
};
let cache = {};
function calcSum(nums, options, target) {
  let cha=nums.length-1-options.length;
  if(cha){
    options.push(...'.'.repeat(cha))
  }
  cache={}
  options.sort()
  for(let i of nums){
    cache[i]={
      value:i,
      formula:i,
    }
  }
  calcLoop(nums, options, target)
};
function calcLoop(nums, options, target){
  if (nums.length === 1 &&options.length==0&& cache[nums[0]].value == target) {
    console.log(nums[0]);
    return;
  }
  nums.sort()
  let key=[...nums].join()+'|'+options.join();
  if(cache[key]){
    return
  }
  cache[key]=true
  for (let i of '*+-/.') {
    let index = options.indexOf(i);
    if (index >= 0) {
      let newOptions = [...options];
      newOptions.splice(index, 1);
      let len=nums.length
      for (let j=0;j<len-1;j++) {
        let newNums=[...nums];
        newNums.splice(nums.indexOf(nums[j]), 1);
        for(let k=j+1;k<len;k++){
          let newNums2=[...newNums];
          newNums2.splice(newNums2.indexOf(nums[k]), 1);
          let newNum = calc(cache[nums[j]], i, cache[nums[k]])
          if (isNaN(newNum.value)) {
          }
          else{
            cache[newNum.formula]=newNum;
            let t=[newNum.formula,...newNums2];
            calcLoop(t, newOptions, target);
          }
          if(i!='+'&&i!='*'){
            let newNum = calc(cache[nums[k]], i, cache[nums[j]]);
            if (isNaN(newNum.value)) {
            }
            else{
              cache[newNum.formula]=newNum;
              let t=[newNum.formula,...newNums2];
              calcLoop(t, newOptions, target);
            }
          }
        }
      }
    }
  }
}

第二代使用code calcSum(nums,options,target) 举例

calcSum([1,2,3,4,5],['+','-','*','/'],1)
calcSum([1,1,10,24],['*','*'],1024)

没测过,能用就行

-----------更新---------

class DigitalPuzzleSolution {
  constructor( needNum = 5,uid = 0) {
    this.targetNeedNum = needNum
    if(!uid){
      this.getUserInfo()
    }
    else{
      this.uid = uid
    }
    this.cache = null
    if (!this.isNative(console.log)) {
      var _frame = document.createElement('iframe');
      document.body.appendChild(_frame);
      this.console = _frame.contentWindow.console
    }
    else {
      this.console = console
    }
    this.LasVegasCalcNum=1000000
    this.codeMap=null
    window.calcSum=this.calcSum
    window.calcSum2=this.calcSum2
    window.getDigitalPuzzle=this.getDigitalPuzzle
    this.optionTypes=''
    this.suitOptionTypes=''
    this.typeMax=2
  }
  isNative(method) {
    return !!method && (/{\s*\[native code\]\s*}/.test(method + ""))
  }
  setNeedNum=(needNum = 5)=>{
    this.targetNeedNum = needNum
  }
  setLasVegasCalcNum=(LasVegasCalcNum=1000000)=>{
    this.LasVegasCalcNum=LasVegasCalcNum
  }
  setOptionTypesOrder=(optionTypes="")=>{
    this.optionTypes=optionTypes
  }
  post=(url,done)=>{
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('POST', url, true);
    httpRequest.setRequestHeader("Content-type","application/json; charset=utf-8");
    httpRequest.setRequestHeader("authorization","Bearer "+this.authorization);
    httpRequest.send();
    let that=this
    httpRequest.onreadystatechange = function () {
      if (httpRequest.readyState == 4 && httpRequest.status == 200) {
        var json = httpRequest.responseText;
        done(JSON.parse(json))
        
      }
    };
  }
  get=(url,done)=>{
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', url, true);
    httpRequest.send();
    let that=this
    httpRequest.onreadystatechange = function () {
      if (httpRequest.readyState == 4 && httpRequest.status == 200) {
        var json = httpRequest.responseText;
        done&&done(JSON.parse(json))
      }
    };
  }
  getUserInfo=()=>{
    let that=this
    this.get('/user_api/v1/user/get',function(res){
      if(res.err_no===0){
        that.uid=res.data.user_id
      }
      else{
        console.log('id获取失败')
      }
    })
  }
  getAuth=(done)=>{
    let that=this
    this.get('/get/token',function(res){
      that.authorization=res.data
      done()
    })
  }
  getDigitalPuzzle=(type=0)=>{
    if(type>=0&&type<this.typeMax){
      this.type=type
    }
    if(!this.uid){
      this.console.log('请先设置用户id 如:solution.setUid(12345678)')
      return
    }
    let that=this
    this.getAuth(function(){
      that.post('https://juejin-game.bytedance.com/game/num-puzz/ugc/start?uid='+that.uid+'&time='+Date.now(),that.run)
    })
    
  }
  run=(res)=>{
    if(res.code===0){
      let arg=this.getArgument(res.data)
      if(arg){
        this.console.log('数字:'+arg.nums+'\n符号:'+arg.options+'\n目标:'+arg.target+'\n所需'+this.targetNeedNum+'个解(如需要更多解请使用setNeedNum)\n解:')
        if(this.type==0){
          this.calcSum(arg.nums,arg.options,arg.target)
        }
        else if(this.type==1){
          this.calcSum2(arg.nums,arg.options,arg.target)
        }
        else if(this.type==2){
          this.calcSum3(arg.nums,arg.options,arg.target)
        }
        this.console.log('结束!')
        return
      }
    }
    this.console.log('获取参数错误')
  }
  getArgument(data){
    let target=data.target
    let nums=[]
    let options=[]
    let optionMap=['+','-','*','/']
    for(let i of data.map){
      for(let j of i){
        if(j>=0){
          if(j%1===0){
            nums.push(j)
          }
          else{
            let n=j*10-3
            if(n>=0&&n<4){
              options.push(optionMap[n])
            }   
          }
        }
      }
    }
    return {target,nums,options}
  }
  setUid=(uid)=>{
    this.uid = uid
  }
  calc(a, option, b){
    let res;
    let formula;
    switch(option){
      case '+':
        res = a.value + b.value;break;
      case '-':
        res = a.value - b.value; break;
      case '*':
        res = a.value * b.value;break;
      case '/':
        res = a.value / b.value;break;
      case '.':
        res = Number(a.value +''+b.value);break;
        default : ;
    }
    formula = a.formula + option + b.formula;
    if (res < 0) {
      res = NaN;
    }
    else if(res ===Infinity) {
      res = NaN;
    }
    else if (res % 1 !== 0) {
      res = NaN;
    }
    return {
      value: res,
      formula: '(' + formula + ')',
    }
  }
  calcSum=(nums, options, target)=>{
    let cha = nums.length - 1 - options.length;
    this.cache = {};
    if (cha) {
      options.push(...'.'.repeat(cha))
    }
    this.nowNum = 0
    options.sort()
    let len = nums.length
    for (let i = 0; i < len; i++) {
      let num = nums[i]
      this.cache[num] = {
        value: num,
        formula: '' + num,
      }
      nums[i] = '' + num;
    }
    nums.sort()
    this.getSuiteOptionTypes(options)
    this.calcLoop(nums, options, target)
    this.cache = null;
  };
  calcLoop=(nums, options, target)=>{
    let cache = this.cache
    if (nums.length === 1 && options.length == 0 && cache[nums[0]].value == target) {
      this.console.log(nums[0]);
      this.nowNum++;
      if (this.nowNum >= this.targetNeedNum) {
        return true
      }
      return// nums[0]
    }
    nums=[...nums]
    nums.sort()
    let key = [...nums].join() + '|' + options.join();
    if (cache[key]) {
      return
    }
    cache[key] = true
    for (let i of this.suitOptionTypes) {
      let index = options.indexOf(i);
      if (index >= 0) {
        let newOptions = [...options];
        newOptions.splice(index, 1);
        let len = nums.length
        for (let j = 0; j < len - 1; j++) {
          let newNums = [...nums];
          newNums.splice(j, 1);
          for (let k = j + 1; k < len; k++) {
            let newNums2 = [...newNums];
            //newNums2.splice(newNums2.indexOf(nums[k]), 1);
            let newNum = this.calc(cache[nums[j]], i, cache[nums[k]])
            if (isNaN(newNum.value)) {
            }
            else {
              cache[newNum.formula] = newNum;
              newNums2[k-1]=newNum.formula//[newNum.formula, ...newNums2]
              if (this.calcLoop(newNums2, newOptions, target)) return true;
            }
            if (i != '+' && i != '*') {
              let newNum = this.calc(cache[nums[k]], i, cache[nums[j]]);
              if (isNaN(newNum.value)) {
              }
              else {
                cache[newNum.formula] = newNum;
                newNums2[k-1]=newNum.formula
                if (this.calcLoop(newNums2, newOptions, target)) return true;
              }
            }
          }
        }
      }
    }
  }
  calcSum2=(nums, options, target)=>{
    let cha = nums.length - 1 - options.length;
    this.cache = {};
    if (cha) {
      options.push(...'.'.repeat(cha))
    }
    this.nowNum = 0
    options.sort()
    let len = nums.length
    nums.sort()
    for (let i = 0; i < len; i++) {
      let num = nums[i]
      nums[i] = {
        value: num,
        formula: '' + num,
      }
    }
    this.getSuiteOptionTypes(options)
    this.calcLoop2(nums, options, target)
    this.cache = null;
  };

  calcLoop2=(nums, options, target)=>{
    let cache = this.cache
    if (nums.length === 1 && options.length == 0 && nums[0].value == target) {
      this.console.log(nums[0].formula);
      this.nowNum++;
      if (this.nowNum >= this.targetNeedNum) {
        return true
      }
      return// nums[0]
    }

    let nums2= nums.map((v)=>v.value)
    nums2.sort()
    let key = nums2.join() + '|' + options.join();
    if (cache[key]) {
      return
    }
    cache[key] = true
    for (let i of this.suitOptionTypes) {
      let index = options.indexOf(i);
      if (index >= 0) {
        let newOptions = [...options];
        newOptions.splice(index, 1);
        let len = nums.length
        for (let j = 0; j < len - 1; j++) {
          let newNums = [...nums];
          newNums.splice(j, 1);
          for (let k = j + 1; k < len; k++) {
            let newNums2 = [...newNums];
            //newNums2.splice(newNums2.indexOf(nums[k]), 1);
            let newNum = this.calc(nums[j], i, nums[k])
            if (isNaN(newNum.value)) {
            }
            else {
              newNums2[k-1]=newNum//[newNum.formula, ...newNums2]
              if (this.calcLoop2(newNums2, newOptions, target)) return true;
            }
            if (i != '+' && i != '*') {
              let newNum = this.calc(nums[k], i, nums[j]);
              if (isNaN(newNum.value)) {
              }
              else {
                newNums2[k-1]=newNum
                if (this.calcLoop2(newNums2, newOptions, target)) return true;
              }
            }
          }
        }
      }
    }
  }
  getSuiteOptionTypes=(options)=>{
    if(this.optionTypes){
      this.suitOptionTypes=this.optionTypes
      return
    }
    let obj={}
    for(let i of options){
      if(obj[i]){
        obj[i]++
      }
      else{
        obj[i]=1
      }
    }
    let arr=[]
    for(let i in obj){
      arr.push({option:i,num:obj[i]})
    }
    arr.sort((a,b)=>b.num-a.num)
    let type=''
    for(let i of arr){
      type+=i.option
    }
    this.suitOptionTypes=type
  }
}

第3代封装为类,可以直接在游戏控制台使用,增加输出解数量功能(减少运算),自动获取数据(需在游戏界面)使用

//一定要创建对象
let solution=new DigitalPuzzleSolution(needNum,uid)//needNum所需解的数量(默认5条),uid ID,其实可以不填,不填会自动获取,如果不是在游戏界面,则填一个不是0的数,或自己的uid
//下面看自己情况使用
solution.calcSum([1,1,10,24],['*','*'],1024)
calcSum([1,1,10,24],['*','*'],1024)
solution.getDigitalPuzzle()
getDigitalPuzzle()

--------------------------更新--------------------------- 3.1代增加了calcSum2使用了更加狠的剪枝,去除重复策略,相比于calcSum全遍历,结果并不是很全面但是找到解的速度有了较大的提升

solution.calcSum2([2,2,3,4,5,6,15,26,62],['-','/','/','*','*','+','+','-'],2610)
calcSum2([2,2,3,4,5,6,15,26,62],['-','/','/','*','*','+','+','-'],2610)
solution.getDigitalPuzzle(1)
getDigitalPuzzle(1)

--------------------------更新--------------------------- 其实很早就做好了,还是放上来,免得以后丢了,观察到运算速度和符号顺序有很大关系所以增加了调整符号顺序的函数setOptionTypesOrder(),现在的默认顺序是按照符号数量从多到少排列

solution.setOptionTypesOrder('*+-/.')//5个都要