算法查找给定范围内给定数字的整数数

时间:2012-02-28 01:15:28

标签: algorithm

如果我以列表list的形式给出完整的数字集,并且我想知道它们可以在给定范围[A, B]内形成多少(有效)整数,那么算法可以我用它来有效地做到这一点?

例如,给定一个数字列表(包含重复和零)list={5, 3, 3, 2, 0, 0},我想知道在[A, B]=[20, 400]范围内可以形成多少个整数。例如,在这种情况下,20, 23, 25, 30, 32, 33, 35, 50, 52, 53, 200, 203, 205, 230, 233, 235, 250, 253, 300, 302, 303, 305, 320, 323, 325, 330, 332, 335, 350, 352, 353都有效。

3 个答案:

答案 0 :(得分:2)

Step 1: Find the number of digits your answers are likely to fall in. In your 
        example it is 2 or 3.

Step 2: For a given number size (number of digits)

    Step 2a: Pick the possibilities for the first (most significant digit). 
    Find the min and max number starting with that digit (ascend or descending
    order of rest of the digits). If both of them fall into the range:
        step 2ai: Count the number of digits starting with that first digit and
        update that count
    Step 2b: Else if both max and min are out of range, ignore. 
    Step 2c: Otherwise, add each possible digit as second most significant digit
    and repeat the same step 

以案例为例解决:

数字大小为2,即__:

0_ : Ignore since it starts with 0
2_ : Minimum=20, Max=25. Both are in range. So update count by 3 (second digit might be 0,3,5)
3_ : Minimum=30, Max=35. Both are in range. So update count by 4 (second digit might be 0,2,3,5)
5_ : Minimum=50, Max=53. Both are in range. So update count by 3 (second digit might be 0,2,3)

尺寸3:

0__ : Ignore since it starts with 0
2__ : Minimum=200, max=253. Both are in range. Find the number of ways you can choose 2 numbers from a set of {0,0,3,3,5}, and update the count.
3__ : Minimum=300, max=353. Both are in range. Find the number of ways you can choose 2 numbers from a set of {0,0,2,3,5}, and update the count.
5__ : Minimum=500, max=532. Both are out of range. Ignore.

更有趣的情况是最大限制为522(而不是400):

5__ : Minimum=500, max=532. Max out of range.
    50_: Minimum=500, Max=503. Both in range. Add number of ways you can choose one digit from {0,2,3,5}
    52_: Minimum=520, Max=523. Max out of range.
        520: In range. Add 1 to count.
        522: In range. Add 1 to count.
        523: Out of range. Ignore.
    53_: Minimum=530, Max=532. Both are out of range. Ignore.



def countComb(currentVal, digSize, maxVal, minVal, remSet):
    minPosVal, maxPosVal = calculateMinMax( currentVal, digSize, remSet)
    if maxVal>= minPosVal >= minVal and maxVal>= maxPosVal >= minVal
        return numberPermutations(remSet,digSize, currentVal)
    elif minPosVal< minVal and maxPosVal < minVal or minPosVal> maxVal and maxPosVal > maxVal:
        return 0
    else:
        count=0
        for k in unique(remSet):
            tmpRemSet = [i for i in remSet]
            tmpRemSet.remove(k)
            count+= countComb(currentVal+k, digSize, maxVal, minVal, tmpRemSet)
        return count

在您的情况下:countComb('',2,400,20,['0','0','2','3','3','5']) + countComb('',3,400,20,['0','0','2','3','3','5'])会给出答案。

def calculateMinMax( currentVal, digSize, remSet):
    numRemain = digSize - len(currentVal)
    minPosVal = int( sorted(remSet)[:numRemain] )
    maxPosVal = int( sorted(remSet,reverse=True)[:numRemain] )
    return minPosVal,maxPosVal

numberPermutations(remSet,digSize, currentVal): Basically number of ways 
you can choose (digSize-len(currentVal)) values from remSet. See permutations
with repeats.

答案 1 :(得分:0)

如果范围很小但是列表很大,那么简单的解决方案就是在范围内循环并检查是否可以从列表中生成每个数字。通过使用散列表或数组可以快速进行检查,该数组可以计算列表中每个数字仍然可以使用多少次。

答案 2 :(得分:0)

对于 n 数字的列表,其中 z 为零,下限为 l ,上限为 u ...

第1步:简易事项

考虑一种情况,你有一个2位数的下限和4位数的上限。虽然确定有多少2位和4位数字在界限范围内可能很棘手,但我们至少知道所有3位数字都是。如果界限是2位数字和5位数字,你知道所有3位和4位数字都是公平的游戏。

因此,让我们将其概括为 a 数字的下限和 b 数字的上限。对于 a b 之间的每个 k (不包括 a b ,他们自己),所有 k - 数字都在该范围内。

有多少这样的数字?考虑一下如何选择它们:第一个数字必须是 n 数字中的一个非零(因此( n - z )数字),其余的是从尚未完成的列表中选取的,即( n -1)选择第二个数字,( n -2)第三,等等。所以这看起来像一个阶乘,但有一个奇怪的第一个任期。挑选了多少个 n ?为什么, k ,这意味着我们必须除以( n - k )!确保我们只选择 k 位数。所以每个k的等式看起来像:( n - z )( n - 1)!/( n - k )!插入范围内的每个 k a b ),您的数量为( a + 1) - to( b -1) - 数字可能,所有数字都必须有效。

第2步:边缘案例

当您考虑 a - 和 b -digit数字时,事情会有点棘手。我认为你不能避免在所有可能的数字组合中开始深度优先搜索,但如果它超出边界,你至少可以在整个分支上中止。

例如,如果您的列表包含{7,5,2,3,0}且上限为520,则搜索可能如下所示:

Pick the 7: does 7 work in the hundreds place? No, because 700 > 520;
  abort this branch entirely (i.e. don't consider 752, 753, 750, 725, etc.)
Pick the 5: does 5 work in the hundreds place? Yes, because 500 <= 520.
    Pick the 7: does 7 work in the tens place? No, because 570 > 520.
      Abort this branch (i.e. don't consider 573, 570, etc.)
    Pick the 2: does 2 work in the tens place? Yes, because 520 <= 520.
        Pick the 7: does 7 work in the ones place? No, because 527 > 520.
        Pick the 3: does 3 work in the ones place? No, because 523 > 520.
        Pick the 0: does 0 work in the ones place? Yes, because 520 <= 520.
        Oh hey, we found a number. Make sure to count it. 
    Pick the 3: does 3 work in the tens place? No; abort this branch.
    Pick the 0: does 0 work in the tens place? Yes.
        ...and so on. 

...然后你会对下限做同样的事情,但会翻转比较器。它不如( a b )区间(即O(1))中的 k -digit组合有效,但是在至少你可以通过修剪早期必须不可能的分支来避免一个很好的交易。无论如何,这种策略确保您只需实际枚举作为边界的两个边缘情况,无论您的( a b )间隔有多宽(或者如果您的下限为0,则只有一个边缘情况。)

修改

我忘记提及的一些事情(对不起,我在公交车上输入了以上所有内容):

在进行深度优先搜索时,实际上只需要在第一个数字等于边界的第一个数字时递归。也就是说,如果您的界限是520并且您刚刚选择了3作为第一个数字,则可以添加( n -1)!/( n -3) !立即跳过整个分支,因为从300开始的所有3位数字肯定都低于500。