如何获得所有相加形成数字的连续数字集?

时间:2018-08-05 12:05:18

标签: java algorithm loops math

我想创建一个程序,该程序生成连续的数字集,这些数字加起来就构成一个数字。例如。如果输入数字为15,则应输入-

7, 8
4, 5, 6
1, 2, 3, 4, 5

某些公式/算法/循环可以完成一些适合的工作。它可以生成数组或打印该数组。这似乎是一个数学问题或愚蠢的问题,但我实际上无法弄清楚如何用Java以编程方式进行操作。

请尝试提供可以执行此操作的确切代码。

8 个答案:

答案 0 :(得分:8)

说您的输入为N。您知道每组k个连续数字将以N / k为中心。如果N / k以0.5结尾,则存在偶数k的解;如果N / k是整数,则存在奇数k的解。解决方案(如果存在)是以N / k为中心的k个整数。

k=1: 15/1 = 15, so 15 (trivial; may want to omit)
k=2: 15/2 = 7.5, so 7,8
k=3: 15/3 = 5, so 4,5,6
k=4: 15/4 = 3.75, so no solution
k=5: 15/5 = 3, so 1,2,3,4,5
k=6: 15/6 = 2.5, so 0,1,2,3,4,5 
etc...
k=15: 15/15 = 1, so -6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8

您可以轻松地对此进行修改,以将其限制为正解或非负解。

答案 1 :(得分:4)

连续数字形成算术级数。如果它从数字a开始并且有n个成员,则总和为

S = n * (2 * b + (n-1)) / 2
so
P = 2 * S = n * (2 * b + (n-1))

因此,对于给定的输入S,我们可以将2*S分解为所有可能的整数因子对P = n * q,其中n<=q,然后获得起始数字

a = (q - n + 1) / 2

如果a为整数(q和n的奇数不同),则对(a, n)表示从a开始具有n成员的有效序列

S = 15, 2S = 30的示例:

30 = 2 * 15 => n = 2, a = 7  => (7,8)
30 = 3 * 10 => n = 3, a = 4  => (4,5,6)
30 = 5 * 6 =>  n = 5, a = 1  => (1,2,3,4,5)

简单的Python示例:

import math
def getseqs(s):
    print(s)
    p = 2 * s
    for n in range(2, math.ceil(math.sqrt(p))):
        if (p % n == 0):
            q = p // n
            if (((q - n) & 1) == 1):  #compare parity
                a = (q - n + 1) // 2
                seq = list(range(a, a+n))
                print(seq, sum(seq))

getseqs(17)
getseqs(15)
getseqs(72)

17
[8, 9] 17
15
[7, 8] 15
[4, 5, 6] 15
[1, 2, 3, 4, 5] 15
72
[23, 24, 25] 72
[4, 5, 6, 7, 8, 9, 10, 11, 12] 72

答案 2 :(得分:4)

由于@MBo传达了一种非常简洁的算法,因此我将继续对它进行回答。 Wiki提供了很好的算术级数介绍,为方便起见,下面将其复制。

总和

Sum

推导

enter image description here

从数字a开始并由n个连续数字组成的序列的总和:

S =(n / 2)* [2 * a +(n-1)* d]

对于连续的数字,步骤d为1。

S =(n / 2)* [2 * a +(n-1)]

在这里我们可以转到@MBo的帖子。

P = 2 * S = n * [2 * a +(n-1)]

我们可以迭代所有可能的连续数字n,并检查结果a是否有效(即a是整数)。

让我们排除a

说P = n * q => q = 2 * a +(n-1)=> 2 * a = q-n + 1 => a =(q-n + 1)/ 2

过滤器

1)我们提到我们可以迭代所有可能的连续数字n,但是鉴于p = n * q,可以肯定地说n需要除以p

  • p % n == 0
  • nMax = (int)Math.sqrt(p)

2)a是整数,a = (q - n + 1) / 2 => (q - n + 1)是偶数=> q - n是奇数。

  • ((q - n) & 1) == 1

实施

import java.util.*;
import java.lang.Math;
import java.util.stream.IntStream;
import static java.util.stream.Collectors.toList;

public class Progressions
{
    public static void main(String[] args)
    {
        List<List<Integer>> list = Calculate(15);
        System.out.print(list);
    }

    public static List<List<Integer>> Calculate(int s)
    {
        List<List<Integer>> list = new ArrayList<>();
        int p = 2*s;
        int nMax = (int)Math.sqrt(p);
        for (int n=2; n<=nMax; n++) {
            if(p % n == 0) {
                int q = p / n;
                if(((q - n) & 1) == 1) {          
                    int a = (q - n + 1) / 2;
                    list.add(range(a,n));
                }
            }
        }
        return list;
    }

    public static List<Integer> range(int a, int n) {
        return IntStream.range(a, a+n)
            .boxed()
            .collect(toList());
    }
}

答案 3 :(得分:3)

考虑TextField是您的输入数字(例如class Bloc { final _type = BehaviorSubject<String>(); final _amount = BehaviorSubject<String>(); final _title = BehaviorSubject<String>(); Stream<String> get type => _type.stream; Stream<String> get amount => _amount.stream; Stream<String> get title => _title.stream; Function(String) get changeType => _type.sink.add; Function(String) get changeAmount => _amount.sink.add; Function(String) get changeTitle => _title.sink.add; createTransaction() { final validType = _type.value; final validAmount = _amount.value; final validTitle = _title.value; print(validType); print(validAmount); print(validTitle); } dispose() { _type.close(); _amount.close(); _title.close(); } } ),而int input是存储结果连续数字的地方,在这里您可以:

15

输入List<int[]> list的结果是这些数组的List<int[]> list = new ArrayList<>(); int lower = 1; // Start searching from 1 int upper = (int) Math.floor(input + 1 / 2); // Up to the half of input (8+9 > 15) while (lower < upper) { // Iterate between the bounds int sum = 0; for (int i = lower; i <= upper; i++) { // Iterate and sum the numbers sum += i; if (sum == input) { // If it matches the input // Add the range to the List // You have to loop them by one and add to the // List before version Java-8 list.add(IntStream .range(lower, i + 1) .toArray()); break; // Found, no reason to continue } if (sum > input) { // Terminate the loop if the sum overlaps break; } lower++; // Increment and try the sums from // a higher starting number sum = 0; // Reset the sum }

15

答案 4 :(得分:2)

这是一个建议:

对于输入数字N:

  • 您只需考虑1到N之间的数字。
  • 您可以维护一个表示[1,...,N]当前子集的间隔。保持当前间隔的总和。第一个间隔为[1,1],其总和为1。
  • 只要总和
  • 如果当前间隔的总和等于N,则将该间隔添加到输出中,删除间隔的左端(也将其从当前总和中删除),然后继续。
  • 如果总和超过N,则还要移除间隔的左端(也将其从当前总和中移除),然后继续。
  • 当间隔变为[N,N](这是应该添加到输出的最终间隔)时,您完成操作。

对于输入15,以下是时间间隔随时间变化的方式:

Interval        Sum

[1]             1
[1,2]           3
[1,2,3]         6
[1,2,3,4]       10
[1,2,3,4,5]     15 -> output [1,2,3,4,5]
[2,3,4,5]       14
[2,3,4,5,6]     20
[3,4,5,6]       18
[4,5,6]         15 -> output [4,5,6]
[5,6]           11
[5,6,7]         18
[6,7]           13
[6,7,8]         21
[7,8]           15 -> output [7,8]
[8]             8
[8,9]           17
[9]             9
[9,10]          19
[10]
...
[15]            15 -> output 15

一旦两个连续数字的总和变得高于目标总和,您可能可以进行一些优化,此时可以终止循环,而只需添加最终集合(仅包含目标总和)即可。

答案 5 :(得分:1)

它使用了Window Sliding Technique/Algorithm。您也可以Google sliding window algorithm sum

答案 6 :(得分:1)

我正在编写@Dave解决方案的实现。 在问之前尝试解决...这就是我们的学习方式。 (仅当我们听不清时问)

<!--Resize Article Element-->
                          <div id="articleResizer"
                        resizer="vertical"
                        resizer-width="6"
                        resizer-right="#singleArticle"
                      resizer-left="#listViewId"
                        resizer-max="90%">
                    </div>

答案 7 :(得分:1)

这是一个类似于Eran解决方案的想法。

由于我们要处理连续的数字,所以累积和(cumsum)通常可以提供帮助。基本思想是,我们要找到两个恰好为K的累加和之间的差,在您的示例中K为15。

number:    1, 2, 3,  4,  5,  6,  7,  8,  9, 10
cumsum: 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55

differences:
15 - 0  = 15 -> [1, 2, 3, 4]
21 - 6  = 15 -> [4, 5, 6]
36 - 21 = 15 -> [7, 8]

累计和从0开始,因此我们可以进行15 - 0减法。解决方案中包含的数字将为左排他性和右排左性。这只是意味着向左索引加1(索引从0开始)。希望模式很清楚。

下一个任务是创建一个算法,该算法执行一些滑动窗口,该滑动窗口在累积总和上具有变化的宽度。这个想法是用精确的K值搜索差异。我们可以从窗口的左侧和右侧指向0的起点开始。虽然差异为<= K,但我们想增加右边的数值窗口的侧面,扩大了窗口和区别。

number:     1, 2, 3,  4,  5,  6,  7,  8,  9, 10
cumsum:  0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55
   1st: (]       -> 0 - 0 = 0
   2nd: (---]    -> 3 - 0 = 3
   3rd: (------] -> 6 - 0 = 0

一旦算法达到15,它将打印出第一个答案,然后将其增加一次。但是,一旦有了差异> K,我们想增加左侧数字,以减少差异。

number:     1, 2, 3,  4,  5,  6,  7,  8,  9, 10
cumsum:  0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55
   1st: (-----------------]     -> 15 - 0 = 15 <print>
   2nd: (---------------------] -> 21 - 0 = 21
   3rd:     (-----------------] -> 21 - 1 = 20

请注意,自< K/2以来,左侧的边界必定为K//2 + (K//2 + 1) >= K(由于//所表示的整数除法,因此可以实现相等性)。因此,我们可以在左侧到达K//2时尽早停止循环(由于左侧不包含在内)。

public static int cumsum(int index) {
    return index * (index + 1) / 2;
}

public static String printRange(int left, int right) {
    StringBuilder buffer = new StringBuilder();
    buffer.append('[');
    for (int i=left+1;i<=right;i++) {
        buffer.append(i);
        buffer.append(',');
    }
    buffer.deleteCharAt(buffer.length()-1);
    buffer.append(']');
    return buffer.toString();
}

public static void main(String[] args) {
    int K = 15;

    int K_ov_2 = K/2;
    int left_index = 0;
    int right_index = 0;
    int diff;
    while (left_index < K_ov_2) {
        diff = cumsum(right_index) - cumsum(left_index);
        System.out.println("diff = " + diff + ", left = " + left_index + ", right = " + right_index);
        if (diff == K) {
            System.out.println(printRange(left_index,right_index));
        }

        if (diff <= K) {
            right_index++;
        } else {
            left_index++;
        }

    }

}

我添加了调试行,以便输出变得更加明显。

diff = 0, left = 0, right = 0
diff = 1, left = 0, right = 1
diff = 3, left = 0, right = 2
diff = 6, left = 0, right = 3
diff = 10, left = 0, right = 4
diff = 15, left = 0, right = 5
[1,2,3,4,5]
diff = 21, left = 0, right = 6
diff = 20, left = 1, right = 6
diff = 18, left = 2, right = 6
diff = 15, left = 3, right = 6
[4,5,6]
diff = 22, left = 3, right = 7
diff = 18, left = 4, right = 7
diff = 13, left = 5, right = 7
diff = 21, left = 5, right = 8
diff = 15, left = 6, right = 8
[7,8]
diff = 24, left = 6, right = 9