【问题标题】:Find the number of distinct ways of writing '1' as a sum of fractions, each with '1' as numerator and a power of '2' as denominator找出将“1”写成分数之和的不同方式的数量,每种方式都以“1”作为分子,以“2”的幂作为分母
【发布时间】:2018-09-23 19:21:27
【问题描述】:

我得到了一个数字n,我必须找到将数字1 写成n 分数之和的不同 方式的数目,其中每个分数都有以下内容格式:

  • 分子始终为 1。
  • 分母是 2 的幂(例如 2^1、2^2 等)。

1 写为这些分数的总和的两种方法如果它们包含相同的分数,则它们是没有区别的。例如,假设n=4。将1 写为4 分数之和的一种方法如下:1/2 + 1/4 + 1/8 + 1/8。但是将其写为1/8 + 1/4 + 1/2 + 1/8 被认为是相同的(因为它包含完全相同的分数,只是顺序发生了变化),因此与第一种编写方式相比并没有区别。所以对于n=4,只有两种方式可以将1写成4个分数的总和。第一个是1/2 + 1/4 + 1/8 + 1/8(上面提到的那个),第二个是1/4 + 1/4 + 1/4 + 1/4。所以结果将是2n 的边界是:2 <= n <= 2000

我在纸上写了前几个(n=2n=3n=4 等等),我认为结果是斐波那契数列的一部分,所以这就是我尝试过的,但是当我在网站上发送消息来源说它是错误的。我有一种感觉,我必须使用动态编程,但我不确定如何实现它。任何帮助将不胜感激。非常感谢!

【问题讨论】:

  • 我感觉我必须使用动态编程” - 为什么?另外 - 您实际上尝试过什么?我看不到代码
  • @Fureeish 我已经说过我尝试使用斐波那契数列并从数列中提取答案,但事实证明这是错误的。发布错误的代码有什么意义?除此之外,我没有尝试过其他任何事情,因为我不知道如何解决这个问题。我需要一个想法。这就是我问这个问题的原因。
  • "发布错误代码有什么意义?" - 这样我们就可以帮助您检查代码有什么问题。粘贴您的代码,解释思考过程,然后我们将能够看到您在实现中可能出现的错误。当然,除非你证明你的算法和整个想法是不正确的。更重要的是,Stack Overflow 不是人们为你找出算法的地方。这是特定编程问题的地方。不是“关于如何开始解决这个问题的任何想法?”的地方。问题类型。

标签: math dynamic-programming integer-arithmetic


【解决方案1】:

我建议你先玩几轮2048。但是,如果你不能停下来,请不要怪我。

你的和数列表有一个最小的,2-k。您可以将整个总和缩放 2k 以使所有数字成为整数,实际上是 2 的幂。这可能会使思考问题变得更容易。现在将总和视为对二进制数的运算,例如

   0100
   0100
   0100
   0010
   0001
+  0001
= 10000

开始添加最小的整数。必须有两个。否则,您无法将最后一位归零。如果 n=1,则此规则的一个例外,即您只有一个被加数。因此,您可以将两个最小的数字相加,以获得两倍大的数字。然后继续,直到达到一个数字。如果需要,您可以对二进制分数执行相同操作以避免缩放步骤。

因此,这里的一个重要不变量是您可以添加项以使其保持为 2 的负幂。加法序列会形成一棵二叉树,节点的值由深度表示:根有1,下一级有1/2,下一级有1/4,以此类推。每个节点要么是一个有零个孩子的叶子,要么是一个有两个孩子的内部节点。您对具有 n 个叶子的二叉树感兴趣,但如果它们在每个级别中具有相同数量的叶子,则认为树是相等的。

要开始递归思考,如何从一棵有 n 片叶子的树变成一棵有 n+1 片叶子的树?您将一对孩子添加到现有的叶子。让我们写一些python代码。

def expansions(v):
    for i in range(len(v) - 1):
        if v[i]:
            yield tuple(x - 1 if j == i else x + 2 if j == i + 1 else x
                        for j, x in enumerate(v))
    yield v[:-1] + (v[-1] - 1, 2)  # start a new level

s = set([(1,)])  # start with a bare root
for n in range(1, 21):
    print("{:4d}: {:10d}".format(n, len(s)))
    s = set(y for x in s for y in expansions(x))

然后转到http://oeis.org/ 并输入您很难找到的序列。搜索将列出一个命中,A002572,描述为

1 到 1/2 的 n 次方的分区数

宾果游戏。不幸的是,它没有封闭的公式。但是有一个为 n=2000 预先计算的值列表。因此,这是一个 shell 脚本,可通过查找来确定任何给定 n 的结果:

wget -qO- 'http://oeis.org/A002572/b002572.txt' | tail -n+${n:?} | head -n1 | cut -d' ' -f2

如果您想要更严肃的答案,我建议您遵循 OEIS 上引用的参考资料。或者尝试理解 v 描述为的函数

v(c, d) 是d的分区数成正整数形式d = c + c1 + c2 + … + cn,其中 c1 ≤ 2  c, ci+1 ≤ 2 ci em>.

在 Mathematica、Maple 和 Pari 中用于一些神奇的动态规划。

那么连接在哪里?要回答这个问题,请从考虑叶节点切换到考虑内部节点。如果您有 n 个叶节点,那么您有 d=n-1 个内部节点。 v(1,d) 是通过根据层划分内部节点的数量来计算排列这些内部节点的方式。您需要在根部有一个内部节点,除非您的 n=1 没有很好地涵盖在此考虑范围内。并且每个后续层可以有至少零个内部节点,并且最多可以是前一层内部节点数量的两倍。

排除极端情况,基本递归是

v(c, d) = sum(v(i, d-c) for i=1..2*c)

因为如果 d=c+c1+c2+…+cm 那么这对应于d-c=c1+c2+…+cm 所以你需要对d-c 进行分区的方法,i=c1 介于 0 和 2 之间 c。这可以用于一个很好的动态编程实现。以下是您需要计算的所有值,直到 d=20,即 n=21:

v(c,d)   c=1   c=2   c=3   c=4   c=5   c=6   c=7   c=8   c=9
 d= 1:     1
 d= 2:     1     1
 d= 3:     2     1     1
 d= 4:     3     2     1     1
 d= 5:     5     4     2     1     1
 d= 6:     9     7     4     2     1     1
 d= 7:    16    12     7     4     2     1     1
 d= 8:    28    22    13     7     4     2     1     1
 d= 9:    50    39    24    13     7     4     2     1     1
 d=10:    89    70    42    24    13     7     4     2
 d=11:   159   126    76    43    24    13     7     4
 d=12:   285   225   137    78    43    24    13     7
 d=13:   510   404   245   140    78    43    24    13
 d=14:   914   725   441   251   141    78
 d=15:  1639  1299   792   452
 d=16:  2938  2331  1420   812
 d=17:  5269  4182  2550  1457
 d=18:  9451  7501
 d=19: 16952 13458
 d=20: 30410

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-27
    • 1970-01-01
    • 1970-01-01
    • 2014-02-25
    • 2019-11-17
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多