【问题标题】:Recursive function building递归函数构建
【发布时间】:2013-01-12 15:50:12
【问题描述】:

我正在尝试编写一个函数来探索所有可能的数字组合,以数组形式给出,希望找到加起来达到一定数量的最小数字组,并将其作为参数传递。

这是我一直在做的事情,这似乎适用于某些但并非所有情况;

我选择一个数字,从总和中减去它,然后将新总和传递给函数,数组的限制保持不变,这让我可以选择重新选择数字,
在第二次调用中,我传递了新的总和,即总和减去当前选择的数字,但我将数组缩小了 1,这意味着我不会再次选择相同的数字。

但是,我意识到我并没有涵盖所有选择,因为无论如何我假设任何数字对于解决方案都是至关重要的,但我不知道要传递哪些参数才能涵盖第三个选项,即不选择数字,这意味着我不会从总和中减去,并且会缩小数组的大小。

您的帮助将不胜感激,顺便说一句,我正在用 C 语言编写。

    int howManyCoins(int*coins,int size,int sum)
{
    return howManyCoins_aux(coins,size,sum,size-1);
}

int howManyCoins_aux(int*coins,int size, int sum,int chosen)
{
    if (sum==0)return 1;
    if (sum<0)return 0;
    if (chosen==0) return 0;
    if (coins[chosen]>sum) return 0;
    int res1=0,res2=0,best_solution=0;
    for (int i=chosen;i>=0;i--)
    {
        res1+=howManyCoins_aux(coins,size,sum-coins[i],chosen);
        res2+=howManyCoins_aux(coins,size,sum-coins[i],chosen-1);
        if(!(res1+res2)) best_solution=0;
        else if (res1==0) best_solution=res2;
        else if (res2==0) best_solution=res1;
        else best_solution=res2>res1?res1:res2;
    }
    return best_solution;
}

【问题讨论】:

  • 如果您发布一些代码或伪代码会有所帮助。
  • 目前有任何代码吗?该算法看起来几乎合理。
  • 数字也可以是负数,对吧?
  • 一分钟后贴出代码,@cnicuter居然没有,数组都是正数,从最小值到最大值排序。
  • 您以最模糊、最不可读、最无效的方式执行此操作是否有原因?一个简单的 for 循环可以很容易地替换这个乱七八糟的函数。

标签: c recursion tail-recursion backtracking


【解决方案1】:

这听起来像是子集和问题的变体。由于您只考虑正数,因此如果您保留另一个数据结构来跟踪与每个可能值相加的数字集,那么像 here 描述的动态编程方法可能会起作用。

【讨论】:

    【解决方案2】:

    您可以将选择简化为:选择硬币并能够重新选择硬币或不选择硬币(递归将涵盖所有选项)

    替换:

    res1+=howManyCoins_aux(coins,size,sum-coins[i],chosen);
    res2+=howManyCoins_aux(coins,size,sum-coins[i],chosen-1);
    

    res1 = howManyCoins_aux(coins, size, sum-coins[i], chosen);
    res2 = howManyCoins_aux(coins, size, sum, chosen-1);
    

    如果每个硬币只能选 1 个:(即change-making problem

    res1 = howManyCoins_aux(coins, size, sum-coins[i], chosen-1);
    res2 = howManyCoins_aux(coins, size, sum, chosen-1);
    

    您的算法有些不清楚,我认为有很多重复。您可以摆脱 for 循环,并将您的功能更改为:(未经测试)

    int howManyCoins_aux(int *coins, int size, int sum, int chosen, int pos)
    {
      if (sum == 0) return chosen;
      if (sum < 0 || pos == size) return 9999999;
      int res1 = 9999999;
      if (coins[pos] >= sum)
        res1 = howManyCoins_aux(coins, size, sum-coins[pos], chosen+1, pos);
      int res2 = howManyCoins_aux(coins, size, sum, chosen, pos+1);
      return min(res1, res2);
    }
    

    话虽如此,递归可能不是要走的路(即使在小型数据集上也需要很长时间)。听起来像integer programming。链接中有几个解决方案。

    【讨论】:

    • 是的,我有点绝望,开始尝试各种东西 xD
    • 其实我有义务使用递归(这是作业)。我听从了您的建议,并进行了一些测试,并使用调用树跟踪了该函数,它正在做它应该做的事情,但是,似乎我在函数末尾的返回值错误,我的当前代码返回真或假(0或1)而不是实际的组大小:/,我应该只返回两个结果的总和吗?但这不是最小的解决方案吗?
    • 要求是: - 硬币的数量应该是达到总和所需的最小数量。 - 任何硬币都可以被多次选择。附带说明:我不确定我是否理解参数 pos 的目的并在您的代码中选择
    • 好的,为了完整起见,我只是包含了该部分。 pos 是数组中的位置。 chosen 是选择的硬币数量。
    • 非常感谢您的帮助,我开始思考整个概念,最后一件事,我的逻辑有什么问题:(第三个答案中的代码)
    【解决方案3】:

    如果要检查所有可能的值,可以使用位数组。如果您给定的数组足够短,您可以使用整数(或长整数)作为位数组。 现在,让 a 成为您的数组,然后对于每个索引 n: 如果且仅设置了位数组的位 n,则 a[n] 在组合中。

    【讨论】:

    • 但是我的函数调用会是什么样子? ,我很难涵盖第三个选项
    • 根据我的建议,您可以放弃递归。如果您的位数组是整数,请使用 ++ 移动到下一个组合。要检查位 n 是否设置为整数 b,请使用 'if(b & (1
    • 实际上我是为作业而写的,我有义务使用递归:),但我感谢您的意见,谢谢。
    猜你喜欢
    • 2013-08-12
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 2011-11-25
    • 2016-03-13
    • 1970-01-01
    • 2020-02-27
    • 2012-06-12
    相关资源
    最近更新 更多