【问题标题】:Algorithm to calculate the number of combinations to form 100计算组合数以形成 100 的算法
【发布时间】:2010-08-18 09:20:03
【问题描述】:

我遇到了一个棘手的情况,我需要根据不同的因素计算组合的数量以形成 100。

这些是

  • 组合数
  • 倍增系数
  • 距离

示例输入 1:(2-10-20)

意思是

  • 列出有效的 2 路组合以形成 100。
  • 组合之间的距离应小于或等于 20。
  • 所有合成的组合必须能被给定的乘法因子 10 整除

输出将是

[40,60]

[50,50]

[60,40]

这里[30,70],[20,60]无效,因为距离在20以上。

示例输入 2:[2-5-20]

[40,60]

[45,55]

[50,50]

[55,45]

[60,40]

如果您能指引我正确的方向,我将不胜感激。

干杯。

【问题讨论】:

  • 据我了解,定义输入的方法 sig 是: int[][] ReturnFactors(int countOfFactorsToAdd, int numFactorsAreMultiplesOf, int distanceFactorsMayBeFromEachother) ?另一个问题,如果是 3-10-20,这是否意味着 3 个因素之间的距离可能不超过 20,或者 3 个因素中最大和最小的可能不超过 20?
  • @Jimmy,你是对的签名.. 3-10-20 意味着 3 个因素之间的距离必须不超过 20... [20-40-40],[30, 30,20],[40,30,30] 有效。 [10-40-50] & [10,10,80],[20,70,10] 无效

标签: c# algorithm scala


【解决方案1】:

我希望这不是作业问题!

    def combinations(n: Int, step: Int, distance: Int, sum: Int = 100): List[List[Int]] =
      if (n == 1) 
        List(List(sum))
      else 
        for {
          first <- (step until sum by step).toList
          rest <- combinations(n - 1, step, distance, sum - first)
          if rest forall (x => (first - x).abs <= distance)
        } yield first :: rest

【讨论】:

  • 这不是功课..我只是简化了我遇到的业务案例...您的解决方案按预期工作正常....非常感谢... :)
  • 如果我正确理解代码,这将是一个非常缓慢的解决方案,即使对于不是太大的 step 值。似乎您从 0 循环到 sum(随 Step 递增)。这意味着如果您的输入参数是 5-10-100,并且您从 10 作为第一个值开始,您仍将调查至少 2^4 种可能性(如果不立即考虑距离,甚至可能有 10^4 种可能性) .这仅适用于 5 个值和 10 个步长。值越多或步长越小,性能影响将是巨大的。查看我的答案,以获得比 0 和 100 更好的最小/最大值。
【解决方案2】:

如果您需要将 100 除以 2,最大距离为 N,则组合中的最小值为

100 / 2 - N / 2

如果您需要将 100 除以 3 个最大距离为 N 的值,这将变得更加棘手。 3个值的平均值将是100/3,但是如果其中一个值远低于这个平均值,那么另一个值只能比这个平均值稍大,这意味着最小值不是平均值减去最大距离除以两个,但可能

100 / 3 - 2N / 3

一般有 M 值,这就变成了

100 / M - (M-1)N / M

可以简化为

(100 - (M-1)N) / M

同样我们可以计算出可能的最高值:

(100 + (M-1)N) / M

这为您提供了组合的第一个值的范围。

要确定第二个值的范围,您必须考虑以下约束:

  • 第一个值的距离(不应高于您的最大距离)
  • 我们还能达到总和 (100)

第一个约束不是问题。第二个是。

假设我们使用 10 的倍数将 100 除以 3,最大距离为 30 如前所述,最小值为:

(100 - (3-1)30) / 3 --> 13 --> 舍入到 10 的下一个倍数 --> 20

最大值为

(100 + (3-1)30) / 3 --> 53 --> 四舍五入到前一个 10 的倍数 --> 50

所以对于第一个值,我们应该迭代 20、30、40 和 50。

假设我们选择 20。剩下 80 用于其他 2 个值。 同样,我们可以将 80 分布在 2 个值上,最大距离为 30,这给出:

最小值:(80 - (2-1)30) / 2 --> 25 --> 四舍五入 --> 30

最大值:(80 + (2-1)30) / 2 --> 55 --> 四舍五入 --> 50

第二个约束是我们不希望与第一个值相比距离大于 30。这给出了最小值 -10 和最大值 50。

现在取两个域之间的交集 --> 30 到 50,第二个值迭代 30、40、50。

然后对下一个值重复此操作。

编辑: 我在伪代码中添加了算法以使其更清晰:

calculateRange (vector, remainingsum, nofremainingvalues, multiple, maxdistance)
{
if (remaingsum==0)
   {
   // at this moment the nofremainingvalues should be zero as well
   // found a solution
   print vector
   return;
   }

minvalueaccordingdistribution = (remainingsum-(nofremainingvalues-1)*maxdistance)/nofremaingvalues;
maxvalueaccordingdistribution = (remainingsum+(nofremainingvalues-1)*maxdistance)/nofremaingvalues;

minvalueaccordingdistance = max(values in vector) - maxdistance;
maxvalueaccordingdistance = min(values in vector) + maxdistance;

minvalue = min (minvalueaccordingdistribution, minvalueaccordingdistance);
maxvalue = max (minvalueaccordingdistribution, minvalueaccordingdistance);

for (value=minvalue;value<=maxvalue;value+=multiple)
   {
   calculaterange (vector + value, remainingsum - value, nofremainingvalues-1, multiple, maxdistance);
   }
}

main()
{
calculaterange (emptyvector, 100, 2, 20);
}

【讨论】:

  • 非常感谢您的想法...正在研究该方法...将尽快更新您... :)
【解决方案3】:

为什么不能使用几乎没有优化的蛮力方法?例如,说 N - 组合数 M - 倍数 D - 最大可能距离

因此组合中的可能值可以是 M、2M、3M 等。您需要生成此集合,然后从集合中的第一个元素开始,并尝试通过从同一集合中选择值来找出接下来的两个(前提是它们应该小于第一个/第二个值的 D)。 所以 i/p 为 3-10-30 会

  1. 创建一组 10、20、30、40、50、60、70、80、90 作为可能值
  2. 从 10 开始,第二个值的选择必须是 20、30、40、50 (D
  3. 现在从 20、30、40、50 的集合中选择第二个值,然后尝试获取下一个值,依此类推

如果您使用递归,那么解决方案会变得更加简单。

  1. 你必须从一个 MIN & 中的可能值列表 最大指数。
  2. 所以尝试第一个值 MIN 索引(到 MAX 索引)。说我们 已选择 X 索引处的值。
  3. 对于每个第一个值,尝试查找 从列表中取出 N-1 个值,其中 MIN = X + 1 和 MAX。

当 M = 1 且 N 足够大时,性能最差。

【讨论】:

  • 问题是你可以从一个没有意义的第一个值开始,只有在你添加了所有的组合之后才意识到这一点。例如。对于 3-10-30,从 10 开始不会给出解决方案,您只有在添加所有值后才意识到这一点,这对于较大的 N 值将是致命的。
  • Patrick - 我完全同意。该解决方案采用蛮力方法,尽管可以优化,但效率并不高。但从好的方面来说,它很容易理解。想法是从这里开始,然后让它变得更好。此外,在业务情况下,N 的值很可能会被限制为小于 5,并且此解决方案会很好地工作。
【解决方案4】:

所有个加法因素之间的距离,还是每个个因素之间的距离?例如,对于 3-10-20,[20-40-60] 是有效答案吗?我假设是后者,但下面的解决方案可以非常简单地修改以适用于前者。

无论如何,要走的路是从你可以管理的最极端的答案(一种)开始,然后沿着答案走下去,直到你到达另一个最极端的答案。

让我们尝试将数字放置得尽可能低,除了最后一个,它会尽可能高(假设其他数字都低)。设公约数为d,除以100,得到S = 100/d。这很好地量化了我们的问题。现在我们有我们的约束,即间距最多为s,除了我们将其转换为多个量化步长n = s/d。现在假设我们有M 样本、i1...iM 并编写约束:

i1 + i2 + i3 + ... + iM = S
0 <= i1 <= n
0 <= i2 <= n
. . .
0 <= iM <= n
i1 <= i2
i2 <= i3
. . .
i(M-1) <= iM

我们可以求解第一个方程得到iM 给定其他方程。

现在,如果我们让一切都尽可能相似:

i1 = i2 = ... = iM = I
M*I = S
I = S/M

很好——我们找到了起点! (如果I 是一个分数,那么前几个I 和其余的I+1。)现在我们只是尝试依次遍历每个变量:

for (i1 = I-1 by -1 until criteria fails)
  sum needs to add to S-i1
  i2 >= i1
  i2 <= i1 +n
  solve the same problem for M-1 numbers adding to S-i1
  (while obeying the above constraint on i2)

好吧,看这里——我们有一个递归算法!我们只是走过并读出答案。

当然,我们可以向上而不是向下走i1。如果您需要打印答案,不妨这样做。如果您只需要数它们,请注意向上计数是对称的,因此只需将您从倒数中得到的答案加倍。 (如果不是所有的值都以相同的开头,你也会有一个校正因子——如果有些是I,有些是I+1,你需要考虑到这一点,我不会在这里做。)


编辑:如果范围是每个值都必须适合的范围,而不是所有的

0 <= i1 <= n

条件,你有

max(i1,i2,...,iM) - min(i1,i2,...,iM) <= n

但这给出了相同的递归条件,除了我们传递我们已经选择放入混合中的那些项目的最大值和最小值,而不是在 i2 (或任何其他变量的轮到它)上添加约束是)。

【讨论】:

  • 嗨,雷克斯。 [20-40-60] 不是一个有效的...距离是一组中所有或任何项目之间的距离,不一定是后续项目。这里“20-60 = 40”违反了规则...
  • 啊,好的。相应地更新了答案。基本上还是一样的答案。
【解决方案5】:

输入: (2-10-20)

  1. 将数字除以参数 1

(50,50)

2 检查差异规则是否允许这种组合。如果违反规则,则停止,如果允许,则将其及其组合添加到结果列表中

例如:abs(50-50)

3 第一个值增加参数 2,第二个值减少参数 2

  1. 去2.点

【讨论】:

  • “将第一个值增加参数 2,将第二个值减少参数 2”你到底是什么意思??
  • 第一个组合是 50,50。下一个是 50-10,50+10
猜你喜欢
  • 2016-07-22
  • 1970-01-01
  • 2019-11-25
  • 1970-01-01
  • 2017-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多