Javascript 时间复杂性-错误递归-英国变化组合

Javascript 时间复杂性-错误递归-英国变化组合,javascript,algorithm,recursion,time-complexity,coin-change,Javascript,Algorithm,Recursion,Time Complexity,Coin Change,我最近提出了一个简单(糟糕)的解决英国货币兑换问题的方法(即,多少种硬币组合可以产生一个给定的总数)。我现在有了,但仍然对解决下面两个解决方案的时间和空间复杂性感兴趣 最差解决方案 此解决方案递归地尝试将每个数字与自身和其他数字相结合,从而产生大量重复工作。我相信这是O(n^n)时间,不确定如何度量空间复杂性(但它是巨大的,因为我们正在存储每个结果)。想法 var makeChange = function(total){ // in pence var allSets = new Set(

我最近提出了一个简单(糟糕)的解决英国货币兑换问题的方法(即,多少种硬币组合可以产生一个给定的总数)。我现在有了,但仍然对解决下面两个解决方案的时间和空间复杂性感兴趣

最差解决方案

此解决方案递归地尝试将每个数字与自身和其他数字相结合,从而产生大量重复工作。我相信这是O(n^n)时间,不确定如何度量空间复杂性(但它是巨大的,因为我们正在存储每个结果)。想法

var makeChange = function(total){ // in pence
  var allSets = new Set();
  var coins = [1,2,5,10,20,50,100,200];

  var subroutine = (arr, total) => {
    if(total < 0){ return; }
    if(total === 0){
      allSets.add(''+arr);
    } else {
      // increase each coin amount by one and decrease the recursive total by one
      for(var i = 0; i<coins.length; i++){
        if((total - coins[i]) >= 0){
          subroutine(arr.slice(0,i).concat(arr[i]+1).concat(arr.slice(i+1)), (total - coins[i]))
        }
      }
    }
  };

  var zeros = new Array(coins.length).fill(0);
  subroutine(zeros, total);
  return allSets.size;
};
var makeChange=函数(总计){//以便士为单位
var allset=new Set();
var硬币=[1,2,5,10,20,50100200];
var子例程=(arr,总计)=>{
if(total<0){return;}
如果(总计===0){
所有集合。添加(''+arr);
}否则{
//每增加一个硬币数量,减少一个递归总数
对于(变量i=0;i=0){
子程序(arr.slice(0,i).concat(arr[i]+1).concat(arr.slice(i+1)),(总计-硬币[i]))
}
}
}
};
var zeros=新数组(硬币长度)。填充(0);
子程序(零,总计);
返回allset.size;
};
改进的解决方案

这个解决方案仍然有巨大的空间复杂度,但我相信时间复杂度已经提高到O(n!),因为我们每次都在更小的硬币子集上递归

var makeChange = function(total){ // in pence
  var allSets = new Set();
  var coins = [1,2,5,10,20,50,100,200];

  var subroutine = (arr, total, start) => {
    if(total < 0){ return; }
    if(total === 0){
      console.log(''+arr);
      allSets.add(''+arr);
    } else {
      // only solve for coins above start, since lower coins already solved
      for(var i = start; i<coins.length; i++){
        if((total - coins[i]) >= 0){
          subroutine(arr.slice(0,i).concat(arr[i]+1).concat(arr.slice(i+1)), (total - coins[i]), i);
        }
      }
    }
  };

  var zeros = new Array(coins.length).fill(0);
  for(let i = 0; i<coins.length; i++){
    subroutine(zeros, total, i);
  }

  return allSets.size;
};
var makeChange=函数(总计){//以便士为单位
var allset=new Set();
var硬币=[1,2,5,10,20,50100200];
变量子例程=(arr、总计、开始)=>{
if(total<0){return;}
如果(总计===0){
控制台日志(''+arr);
所有集合。添加(''+arr);
}否则{
//由于较低的硬币已解算,因此只能解算起点以上的硬币
对于(变量i=开始;i=0){
子程序(arr.slice(0,i)。concat(arr[i]+1)。concat(arr.slice(i+1)),(total-coins[i]),i);
}
}
}
};
var zeros=新数组(硬币长度)。填充(0);

对于(设i=0;i第一个算法的复杂度实际上不是O(n^n)。n是表示输入的变量。在本例中,我将引用变量“total”作为输入,因此n基于total。对于O(n^n)的算法,它的递归树必须有一个深度N和一个分支因子N。这里,你的递归深度基于你的硬币数组中最小的变量。你的递归树有一个分支,你每次只需减去这个值,然后递归,直到总数为零。如果这个值是常量,它是安全的表示深度为n。递归树的分支因子也基于coins数组或其中的值数。对于每个函数调用,生成C个其他函数调用,其中C是coins数组的大小。这意味着您的函数实际上是O(n^C)而不是O(n^n)。您的时间和空间复杂性都是基于您的硬币数组的大小以及您的输入号码

函数的空间复杂度为O(n^c*c)。每次调用函数时,也会根据输入向其传递一个大小的数组。我们已经展示了有O(n^c)调用,并且每个调用都包含一个大小为c的数组


请记住,在分析函数的复杂度时要考虑所有输入。

第一个算法的复杂度实际上不是一个O(n^n)。n是一个代表输入的变量。在这种情况下,我将引用变量“total”作为输入,因此n基于total。您的算法是O(n^n),它的递归树必须有一个深度N和一个分支因子N。这里,你的递归深度基于你的硬币数组中最小的变量。你的递归树有一个分支,你每次只需减去这个值,然后递归,直到总数为零。如果这个值是常量,它是安全的表示深度为n。递归树的分支因子也基于coins数组或其中的值数。对于每个函数调用,生成C个其他函数调用,其中C是coins数组的大小。这意味着您的函数实际上是O(n^C)而不是O(n^n)。您的时间和空间复杂性都是基于您的硬币数组的大小以及您的输入号码

函数的空间复杂度为O(n^c*c)。每次调用函数时,也会根据输入向其传递一个大小的数组。我们已经展示了有O(n^c)调用,并且每个调用都包含一个大小为c的数组


请记住,在分析函数的复杂性时要考虑所有输入。

我认为这更适合于CodeReview,这可以在O(n^2)中实现通过使用DP来计算时间…你应该更多地阅读算法来使用DPI来实现这一点。我对改进算法不感兴趣,只是想了解如何计算递归时间复杂度。我认为这更适合CodeReview。这可以在O(n^2)中实现通过使用DP计算时间…您应该阅读更多关于使用DPI实现此功能的算法。我对改进算法不感兴趣,只是想了解如何计算递归时间复杂度。感谢对N深度的解释。对于空间复杂度,数组的大小是C,而不是N。这将是O(C*N^C)?@colorbynumber是的,你说得对。我以为你在传递输出数组是出于某种原因,但它们会进入你的集合。当然,与每次调用创建的数组相比,你的集合的大小微不足道,因此它们是你的空间复杂度的来源。感谢你对N深度的解释。对于空间复杂度,数组是C的常量,而不是N。那么这是O(C*N^C)?@colorbynumber是的,你是对的。我以为你是出于某种原因在传递输出数组,但它们会进入你的集合。当然,你的集合的大小与