单数增加的数量

时间:2016-05-04 14:52:56

标签: python python-2.7 recursion

1。问题简介

我试图找到具有一定数字位数的单调增加数字的数量。具有k数字的单调递增数字可写为

n = a_0 a_1 ... a_k-1

所有a_i <= a_(i+1)的{​​{1}}。更具体的示例是i in range(0, k)123。我正在尝试创建一个这样的函数

12234489

因为有45个数字,两位数正在增加,increasing(2) = 45 increasing(3) = 165 increasing(4) = 495 increasing(5) = 1287 increasing(6) = 3003 。等等。

我认为这是使用递归的好机会。我试图编写一个执行此操作的代码,但结果有问题。我的psudo代码就像这样

2。 Psudo码

  • 从数字11, 12, ..., 22, 23, ..., 88, 89, 99开始循环显示这些数字。将[1, 2, ..., 9]增加一个。
  • 遍历数字length,其中[i, ..., 9]是前一次递归的数字。
  • 如果last_digitlength = number of digits wantedtotal添加一个,则重复上述步骤。

3。代码

return

4。问题:

我的psudocode找到这些数字是否正确?如何正确使用递归来解决这个问题?

我不会要求在我的代码中找到错误,但是一些指针或代码的示例将非常有用。

5 个答案:

答案 0 :(得分:4)

这可以以封闭的形式计算。

我们有8个单位的预算,我们可以分配给每个数字或者#34;剩余时间&#34;。分配给n预算单位的数字比其前面的数字大n;对于第一个数字,如果我们在那里分配n个预算单位,则其值为n+1。剩下的预算什么都不做。

预算分配与单调增加的数字一一对应,因为每个预算分配产生单调增加的数字,并且每个单调增加的数字具有相应的预算分配。因此,长度k的单调增加数量的数量是将8个单位的预算分配给k+1桶,每个数字一个桶和一个剩余桶的方式的数量。

根据经典stars and bars结果,这是(8 + k) choose 8(8+k)!/(8!k!)

def monotone_number_count(k):
    total = 1
    for i in xrange(k+1, k+9):
        total *= i
    for i in xrange(1, 9):
        total //= i
    return total

对于单调减少的数字,可以应用相同的想法。这次我们有9个预算单位,因为我们的数字可以从9下降到0,而不是从1开始,最多到9个。分配给n预算单位的数字为n低于前一个数字;对于第一个数字,n预算单位为其赋予值9-n。需要注意的是,这会将0000计为四位数,对于k的其他值也是如此,因此我们必须明确地将其取消,结果为((9 + k) choose 9) - 1

def monotonely_decreasing_number_count(k):
    total = 1
    for i in xrange(k+1, k+10):
        total *= i
    for i in xrange(1, 10):
        total //= i
    total -= 1
    return total

答案 1 :(得分:3)

您可以使用基于以下关系的简单递归:从i开始的k个数字的单调数量的计数(0

对于k = 1,结果是微不足道的:10-i

这将导致以下递归函数:

def num_increasing(ndigits, first=1):
    n = 0
    if ndigits == 1:
        n = 10 - first
    else:
        for digit in range(first, 10):
            n += num_increasing(ndigits - 1, digit)
    return n

对于ndigits = 6,它给出3003。

答案 2 :(得分:0)

这是一个非递归的解决方案:

def is_monotonic(num, reverse=False):
    num = str(num)
    # check if the string representation of num is same as the sorted one
    return num == ''.join(sorted(num, reverse=reverse))

def get_monotonic_nums(ndigit, reverse=False):
    start = 10**(ndigit-1) if reverse else int('1' * ndigit)
    end = 10**ndigit
    return sum(is_monotonic(num, reverse) for num in xrange(start, end))

然后用法:

>>> get_monotonic_nums(2)
45
>>> get_monotonic_nums(6)
3003

而且,如果您需要减少订单:

>>> get_monotonic_nums(2, reverse=True)
54

答案 3 :(得分:0)

这就是我想出来的;

def is_monotonic(n):
    n = str(n)
    for x, y in zip(n[:-1], n[1:]):
        if x > y:
            return False
    return True

def get_monotonic_nums(digits):
    digits = abs(digits)
    start = 0 if digits == 1 else int('1{}'.format('0'*(digits-1)))
    end = start * 10 if start else 10
    for n in range(start, end):
        if is_monotonic(n):
            yield n

测试;

len(list(get_monotonic_nums(2)))
45

答案 4 :(得分:0)

在互联网上进行一些搜索后,我能够找出以下一个班轮解决方案。基于二项式系数的和公式。我现在意识到我的递归解决方案与这个解决方案相比有多慢。

def choose(n, k):
    """
    A fast way to calculate binomial coefficients by Andrew Dalke (contrib).
    """
    if 0 <= k <= n:
        ntok = 1
        ktok = 1
        for t in xrange(1, min(k, n - k) + 1):
            ntok *= n
            ktok *= t
            n -= 1
        return ntok // ktok
    else:
        return 0

def increasing(digit):
    return choose(digit + 9,9) - 1

def decreasing(digit):
    return choose(digit + 10,10) - 10*digit - 1