获取总和与给定数字匹配的连续数字
Posted
技术标签:
【中文标题】获取总和与给定数字匹配的连续数字【英文标题】:Get the consecutive numbers whose sum matches with given number 【发布时间】:2019-06-27 17:16:04 【问题描述】:我正在执行一个简单的程序,该程序接受一个数字并查找与给定数字匹配的连续数字的出现次数。
例如:
if input is 15, then the consecutive numbers that sum upto 15 are:
1,2,3,4,5
4,5,6
7,8
So the answer is 3 as we have 3 possibilities here.
当我在寻找解决方案时,我发现了以下答案:
static long process(long input)
long count = 0;
for (long j = 2; j < input/ 2; j++)
long temp = (j * (j + 1)) / 2;
if (temp > input)
break;
if ((input- temp) % j == 0)
count++;
return count;
我无法理解这是如何解决要求的,因为该程序使用了一些我无法正确理解的公式,以下是我的疑问:
for循环从2开始,这是什么原因?long temp = (j * (j + 1)) / 2;
这个逻辑说明了什么?这对解决问题有什么帮助?
if ((num - temp) % j == 0)
还有这说明什么?
请帮助我理解这个解决方案。
【问题讨论】:
@JoeC,我也试过调试,但是这个程序是用一些公式来解决问题的,我无法理解这个公式的含义 您是否尝试过手写操作?对于给定的小输入,尝试按照算法手动计算。这可能会有所启发。 @Rietty 我已经这样做了,但是程序的复杂性不好,所以在搜索时我发现了这个高效的程序 是的,我是说,把你亲手找到的算法写出来。例如.. 以count = 0, input = 15, j = 2.
开头,因此temp = (2 * (2 + 1)) / 2 = 3
和3 is not > 15
,所以你这样做:(15 - 3) / 2
并且因为它不等于0
,所以将1
添加到计数中。现在用j = 3
重复一遍,以此类推,直到j = 15/2
,然后看看你还剩下什么。
@Rietty,问题是我们为什么需要做temp = (2 * (2 + 1)) / 2 = 3
以及这如何有助于解决问题。我可以理解程序在做什么,但我想知道这些步骤如何解决问题陈述。
【参考方案1】:
我会尽量简单地解释这一点。
如果输入是 15,那么和为 15 的连续数是:
1,2,3,4,5 -> 5 numbers
4,5,6 -> 3 numbers
7,8 -> 2 numbers
在最坏的情况下,这必须小于第一个 n 个自然数之和 = (n*(n+1) /2。
因此对于数字 15,永远不会有 6 个连续数字的组合总和为 15,因为第一个 6 个数字的总和 =21,大于 15。
计算温度:这是 (j*(j+1))/2。
举个例子。设输入 = 15。设 j =2。
温度 = 2*3/2 = 3; #意义1+2 =3
对于 2 个数字对,让这 2 个术语为 'a+1' 和 'a+2'。(因为我们知道这些数字是连续的。)
现在,根据问题,总和必须等于数字。
这意味着2a+3 =15;
如果 (15-3) 可以被 2 整除,则可以找到“a”。 a=6
-> a+1=7 和 a+2=8
同样,让a+1
,a+2
和 a+3
a + 1 + a + 2 + a + 3 = 15
3a + 6 = 15
(15-6) 必须能被 3 整除。
最后,对于连续 5 个数字 a+1
,a+2
,a+3
,a+4
,a+5
,我们有
5a + 15 = 15;
(15-15) 必须能被 5 整除。
因此,当输入为 15 时,j =2,3 和 5 的计数将发生变化
如果循环从 1 开始,那么我们也将计算 1 个数字集 -> 15 这不是必需的
总结一下:
1)for循环从2开始,这是什么原因?
我们不担心这里设置的 1-number。
2) long temp = (j * (j + 1)) / 2;
这个逻辑说明了什么?这对解决问题有什么帮助?
这是因为我拥有的第一个 n 个自然数属性的总和 通过将
a+1
和a+2
作为连续 2 个来解释上述内容 数字。
3) if ((num - temp) % j == 0)
还有这说明什么?
这表示输入从 1st 的总和中减去的逻辑 j 个自然数必须能被
j
整除。
【讨论】:
Subtract 15 -3 = 12 . Now, 12 must be divisible by 2 for a 2-number-pair to exist.
和But since we are subtracting (j*(j+1))/2 from it, it must be divisible by 2.
你能解释一下减法背后的原因是什么,为什么我们必须这样做,以及它如何帮助解决这个问题?我被困在这一点上。剩下的解释很好。【参考方案2】:
我们正在寻找总和等于给定数字的连续数字。 很明显,最多可以有一个给定长度的系列,所以基本上我们正在寻找那些可能是这样一个系列的长度的值。 变量“j”是测试的长度。它从 2 开始,因为该系列必须至少有 2 长。 变量 'temp' 是从 1 到 'j' 的等差数列之和。
如果有适当的系列,则让 X 成为第一个元素。在这种情况下,“输入”= j*(X-1) + temp。 (所以如果 temp> 输入那么我们就完成了)
在最后一行,它检查方程是否有整数解。如果有,则增加计数器,因为有一个带有 j 元素的系列是一个解决方案。
其实解是错误的,因为如果input = 3,它就找不到解。(它会立即终止。)循环应该是:
for(long j=2;;j++)
无论如何,其他条件更快地终止循环。
【讨论】:
我完全糊涂了。variable 'temp' is the sum of a arithmetic progression from 1 to 'j'
你能解释一下这是什么意思吗? ` 让 X 成为第一个元素。在这种情况下,'input' = j*(X-1) + temp.` 这个公式来自哪里,它不在我的程序中。
如果 J=5 那么 temp = 1+2+3+4+5
我们要在以下等式中确定 X 是否为整数:'input' = j*(X-1) + temp.解决方案是: X 是整数 if (input-temp)%J==0
为什么我们需要使用这个方程'input' = j*(X-1) + temp
以及它与(input-temp)%J==0
的关系,请你解释一下。【参考方案3】:
我们需要找到所有a
s 和n
s,对于给定的b
,以下是正确的:
a + (a + 1) + (a + 2) + ... (a + (n - 1)) = b
左边是等差数列,可以写成:
(a + (n - 1) / 2) * n = b (*)
要找到n的极限值,我们知道a > 0
,所以:
(1 + (n - 1) / 2) * n = n(n + 1) / 2 <= b
n(n + 1) <= 2b
n^2 + n + 1/4 <= 2b + 1/4
(n + 1/2)^2 <= 2b + 1/4
n <= sqrt(2b + 1/4) - 1/2
现在我们可以重写(*)
来得到a
的公式:
a = b / n - (n - 1) / 2
b = 15 和 n = 3 的示例:
15 / 3 - (3 - 1) / 2 = 4 => 4 + 5 + 6 = 15
现在是代码:
double b = 15;
for (double n = 2; n <= Math.ceil(Math.sqrt(2 * b + .25) - .5); n++)
double candidate = b / n - (n - 1) / 2;
if (candidate == (int) candidate)
System.out.println("" + candidate + IntStream.range(1, (int) n).mapToObj(i -> " + " + (candidate + i)).reduce((s1, s2) -> s1 + s2).get() + " = " + b);
结果是:
7.0 + 8.0 = 15.0
4.0 + 5.0 + 6.0 = 15.0
1.0 + 2.0 + 3.0 + 4.0 + 5.0 = 15.0
【讨论】:
【参考方案4】:注意:循环从 2 开始,因为=> (1*(1+1))/2 == 1,这没有意义,即不影响进度;
让,k = 21;
所以循环将迭代到 (k/2) => 10 次;
temp = (j*(j+1))/2 => 即,当 j =2 时为 3,当 j = 3 时为 6,依此类推(计算 N 个自然数之和)
temp > k => 将中断循环,因为当我们得到大于 'K' 的 'sum' 时,我们不需要迭代循环
((k-temp)%j) == 0 => 当输入从前 j 个自然数的总和中减去可被 j 整除时,基本上是正确的,如果是,则增加计数以获得此类方程的总数!
【讨论】:
【参考方案5】:public static long process(long input)
long count = 0, rest_of_sum;
for (long length = 2; length < input / 2; length++)
long partial_sum = (length * (length + 1)) / 2;
if (partial_sum > input)
break;
rest_of_sum = input - partial_sum
if (rest_of_sum % length == 0)
count++;
return count;
input - 这里给定的输入数字是 15
length - 连续数字的长度,在最大输入/2 时至少为 2
partial_sum = 从 1 到长度的数字的总和(对于 1 到 a 的数字是 a*(a+1)/2)假设这是一个部分序列
rest_of_sum = 表示输入中剩余的余额
如果总和的其余部分是长度的倍数,则意味着我们可以将 (rest_of_sum/length) 添加到我们的部分序列中
让我们将 (rest_of_sum/length) 称为 k
这仅仅意味着我们可以在这里建立一个序列来总结我们的输入数字 从 (k+1) , (k+2), ... (k+length) 开始
现在可以验证 (k+1) + (k+2) + ... (k+长度)
我们可以将其减少为 k+k+k+.. 长度乘以 (1+2+3..length)
可以减少为 => k* length + partial_sum
可以减少为 => 输入(因为我们现在验证了这一点)
所以这里的想法是每次我们在这里找到满足这种情况的长度时增加计数
【讨论】:
【参考方案6】:如果您进行此调整,它可能会修复代码。我没有对它进行广泛的测试。这是一个奇怪的问题,但它使代码通过额外的迭代来修复早期的错误计算。即使是 1/20000 也可以!如果这是用四舍五入的浮点数完成的,并在其中添加 1,我认为这也会起作用:
for (long j = 2; j < input+ (1/2); j++)
本质上你只需要知道一个公式:
m..n
的数字总和(或 m 到 n)(其中 n>m 在代码中)
这是((n-m+1)*(n+m))/2
正如我已经评论过的,原始问题中的代码被窃听了。
见here。
尝试喂它 3。连续数字 1,2 出现了 1 次。它产生 0。 或 5。有 2,3 - 也应该产生 1 - 产生 0。 或 6。这有 1,2,3 - 也应该产生 1 - 产生 0。 在您的原始代码中,temp
或 (j * (j + 1)) / 2
表示数字 1 到 j 的总和。
1 2 3 4 5 5 4 3 2 1 ======= 6 6 6 6 6 => (5 x 6) /2 => 30/2 => 15
正如我在下面的代码中所示 - 使用 System.out.println();
来输出调试信息。
代码:
class Playground
private static class CountRes
String ranges;
long count;
CountRes(String ranges, long count)
this.ranges = ranges;
this.count = count;
String getRanges()
return this.ranges;
long getCount()
return this.count;
static long sumMtoN(long m, long n)
return ((n-m+1)* (n+m))/2;
static Playground.CountRes countConsecutiveSums(long i, boolean d)
long count = 0;
StringBuilder res = new StringBuilder("[");
for (long m = 1; m< 10; m++)
for (long n = m+1; n<=10; n++)
long r = Playground.sumMtoN(m,n);
if (d)
System.out.println(String.format("%d..%d %d",m,n, r));
if (i == r)
count++;
StringBuilder s = new StringBuilder(String.format("[%d..%d], ",m,n));
res.append(s);
if (res.length() > 2)
res = new StringBuilder(res.substring(0,res.length()-2));
res.append("]");
return new CountRes(res.toString(), count);
public static void main(String[ ] args)
Playground.CountRes o = countConsecutiveSums(3, true);
for (long i=3; i<=15; i++)
o = Playground.countConsecutiveSums(i,false);
System.out.println(String.format("i: %d Count: %d Instances: %s", i, o.getCount(), o.getRanges()));
你可以试试运行here
输出:
1..2 3
1..3 6
1..4 10
1..5 15
1..6 21
1..7 28
1..8 36
1..9 45
1..10 55
2..3 5
2..4 9
2..5 14
2..6 20
2..7 27
2..8 35
2..9 44
2..10 54
3..4 7
3..5 12
3..6 18
3..7 25
3..8 33
3..9 42
3..10 52
4..5 9
4..6 15
4..7 22
4..8 30
4..9 39
4..10 49
5..6 11
5..7 18
5..8 26
5..9 35
5..10 45
6..7 13
6..8 21
6..9 30
6..10 40
7..8 15
7..9 24
7..10 34
8..9 17
8..10 27
9..10 19
i: 3 Count: 1 Instances: [[1..2]]
i: 4 Count: 0 Instances: []
i: 5 Count: 1 Instances: [[2..3]]
i: 6 Count: 1 Instances: [[1..3]]
i: 7 Count: 1 Instances: [[3..4]]
i: 8 Count: 0 Instances: []
i: 9 Count: 2 Instances: [[2..4], [4..5]]
i: 10 Count: 1 Instances: [[1..4]]
i: 11 Count: 1 Instances: [[5..6]]
i: 12 Count: 1 Instances: [[3..5]]
i: 13 Count: 1 Instances: [[6..7]]
i: 14 Count: 1 Instances: [[2..5]]
i: 15 Count: 3 Instances: [[1..5], [4..6], [7..8]]
【讨论】:
以上是关于获取总和与给定数字匹配的连续数字的主要内容,如果未能解决你的问题,请参考以下文章