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中,我无法阅读/运行/理解。
非常感谢任何帮助,提前谢谢!
答案 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 here和here上阅读。
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)