项目Euler 76 - 列出给定数字的所有分区

时间:2011-08-16 19:24:49

标签: .net vb.net numbers

Project Euler Problem #76听起来像是这样的: “有多少种不同的方式可以写成至少两个正整数的总和?”

我努力在几天内做到正确,试图以不同的方式解决,并且对于小数字(那些易于检查的)获得大致相同的结果。我最终得到了一个算法,该算法按字母顺序列出给定数字的所有分区,降序(从“N-1 + 1”开始)。用VB.NET编写:

Dim ub As Integer = 6
Dim wayCount As Integer = 0
For n = ub - 1 To 1 Step -1
  'init value array (first combination)
  Dim s As New List(Of Integer)
  For m = 1 To ub \ n : s.Add(n) : Next
  Dim b As Integer = ub Mod n
  If b <> 0 Then s.Add(b)

  'from where to start decrementing
  Dim k As Integer = s.Count - 1
  While k > 0 And s(k) = 1 : k -= 1 : End While

  Do
    wayCount += 1 : Console.WriteLine(String.Join(" + ", s) & " = " & s.Sum)
    If s(k) = 1 Then k -= 1
    If k = -1 Then Exit Do
    s(k) -= 1
    s.Add(1)
  Loop While k >= 1
Next

Console.WriteLine("count=" & wayCount)

该代码适用于包含数字1-6的数字,并且在N = 7时开始失败,错过了1个组合。对于N = 8,它错过2(19而不是21)。对于N = 100,答案是4576,比所需要的小几个数量级。显然,我遗漏了一些非常重要的细节。

如果你没有时间或方法来编译和运行上面的代码,这里是输出(N = 7):

6 + 1 = 7
5 + 2 = 7
5 + 1 + 1 = 7
4 + 3 = 7
4 + 2 + 1 = 7
4 + 1 + 1 + 1 = 7
3 + 3 + 1 = 7
3 + 2 + 1 + 1 = 7
3 + 1 + 1 + 1 + 1 = 7
2 + 2 + 2 + 1 = 7
2 + 2 + 1 + 1 + 1 = 7
2 + 1 + 1 + 1 + 1 + 1 = 7
1 + 1 + 1 + 1 + 1 + 1 + 1 = 7
count=13

我研究过这些链接:

(ProjectEuler) Sum Combinations - 提供数学解决方案,但不列出所有组合

Generating the partitions of a number - 在python中,我无法阅读/运行/理解。

非常感谢任何帮助,提前谢谢!

3 个答案:

答案 0 :(得分:3)

你说,“该代码适用于1-6号码,并且在N = 7时开始失败,错过了1个组合。”对于N = 7,您应该在调试器或手动中一次一行地执行代码。通过详细了解您的代码正在做什么,您将能够看到它错过组合的位置。

答案 1 :(得分:1)

对于这个问题,有一个相当准确的封闭式解决方案,信不信由你。

Hardy和Ramanujan的公式:

                    e^(PI sqrt(2n/3))
            p(n) ~   -----------------
                        4n sqrt(3)

越接近n->无穷大。 数学家拉德马赫制作了一个精确的公式,但它并不漂亮。 Here's a discussion.

我建议在维基百科和Mathworld herehere上阅读。

This是MathOverflow对此问题的讨论。

我不是说这些是这个问题的最佳解决方案。事实上,对于相当小的n值,它们是浪费时间,但看到这个解决方案以及更“计算机导向”的解决方案很有意思。

答案 2 :(得分:0)

正如所承诺的,这里有一些代码使用最多N的自然数来对N进行分区(存储在ub中),但不包括它。稍作修改,它应该能够通过任何函数进行分区,包括浮点输出。

基本思想是,对于分区中使用的每个值,我们使用系数桶,它是该值的乘数。在每一步,我们要么将值充电到最大可用值,向左或向右移动,递减电流倍增器并测试我们是否得到总和。一旦sum成功分区,wayCount就会递增,结果将被打印到屏幕上。

这可能是一个有点脏的实现,但它在合理的时间内工作,即使对于问题的范围(在我的机器上不到5分钟),每秒产生数百万个分区。总是欢迎健康的批评!

Dim ub As Integer = 10
Dim availableIncrements(ub - 2) As Integer
Dim weightCoefficients(ub - 2) As Integer
For i = 0 To ub - 2
  availableIncrements(i) = ub - i - 1
  weightCoefficients(i) = -1 'indicates that enumeration has not started yet
Next
Dim wayCount As Integer = 0

Dim pos, sum As Integer
pos = 0 : sum = 0
Do
  If weightCoefficients(pos) = -1 Then
    'when we came here first, charge coefficient to maximum available
    weightCoefficients(pos) = (ub - sum) \ availableIncrements(pos)
  ElseIf weightCoefficients(pos) > 0 Then
    'regular cycle: decrease by one
    weightCoefficients(pos) -= 1
  Else
    'return to previous bucket
    If pos = 0 Then Exit Do
    pos -= 1
    Continue Do
  End If

  'calculate current sum
  sum = 0
  For k = 0 To pos
    sum += availableIncrements(k) * weightCoefficients(k)
  Next
  'found combination
  If sum = ub And weightCoefficients(pos) > 0 Then
    wayCount += 1

    'printing to screen, remove when expecting many combinations
    Dim printList As New List(Of Integer)
    For i = 0 To pos 'which number to print
      For k = 1 To weightCoefficients(i) 'how many times to print a number
        printList.Add(availableIncrements(i))
      Next
    Next
    Console.WriteLine(String.Join(" + ", printList))

    'if we were in the last bucket and we just partitioned the number,
    'no need to go down and check all lower coefficients, instead move one column back.
    If pos = UBound(availableIncrements) Then
      pos -= 1
      Continue Do
    End If
  End If

  If pos < UBound(availableIncrements) Then
    pos += 1
    weightCoefficients(pos) = -1 'prepare to charge
  End If

  'this is something to keep you busy (so you know it's not hanging)
  'uncomment for long enumerations
  'If wayCount Mod 100000 = 0 Then Console.WriteLine(wayCount)
Loop

Console.WriteLine("count=" & wayCount)