Facebook Pixel

829. Consecutive Numbers Sum

HardMathEnumeration
Leetcode Link

Problem Description

Given an integer n, you need to find how many different ways you can express n as a sum of consecutive positive integers.

For example, if n = 5, it can be written as:

  • 5 (just the number itself)
  • 2 + 3 (two consecutive numbers)

So there are 2 ways to write 5 as consecutive positive integers.

The problem asks you to count all possible ways to represent n as a sum of one or more consecutive positive integers. The consecutive integers must be positive (greater than 0) and must form an unbroken sequence like a, a+1, a+2, ... where a is the starting number.

The key insight is that any sum of consecutive integers forms an arithmetic sequence. If we have k consecutive numbers starting from a, the sum would be:

  • a + (a+1) + (a+2) + ... + (a+k-1) = n

Using the arithmetic series formula, this sum equals (2a + k - 1) × k / 2 = n, which can be rearranged to 2n = (2a + k - 1) × k.

From this equation, we can derive conditions that must be satisfied:

  1. k must divide 2n evenly (since the right side must equal 2n)
  2. Since a ≥ 1, we have 2n = (2a + k - 1) × k ≥ k × (k + 1), giving us an upper bound for k
  3. From 2n/k = 2a + k - 1, we get 2a = 2n/k - k + 1, which must be even (since 2a is even), meaning 2n/k - k + 1 must be even

The solution iterates through possible values of k (number of consecutive integers) and checks these conditions to count valid representations.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we think about writing a number as consecutive positive integers, we're essentially looking for arithmetic sequences that sum to n. Let's start with a simple example to build our intuition.

If we want to express n as k consecutive numbers starting from a, we'd have: a + (a+1) + (a+2) + ... + (a+k-1) = n

Using the arithmetic series formula, this becomes: sum = (first term + last term) × number of terms / 2 n = (a + (a+k-1)) × k / 2 n = (2a + k - 1) × k / 2

Multiplying both sides by 2: 2n = (2a + k - 1) × k

Now comes the key insight: we can rearrange this to solve for a: 2n = 2ak + k² - k 2n = 2ak + k(k - 1) 2a = 2n/k - k + 1

For a to be a positive integer, two things must happen:

  1. k must divide 2n evenly (otherwise 2n/k wouldn't be an integer)
  2. 2a must be a positive even number, which means 2n/k - k + 1 must be even

Also, since a must be at least 1 (we need positive integers), we have: 2a ≥ 2 2n/k - k + 1 ≥ 2 2n/k ≥ k + 1 2n ≥ k(k + 1)

This gives us an upper bound for k: we only need to check values where k(k + 1) ≤ 2n.

The beautiful part of this approach is that instead of trying different starting points a and lengths k (which would be inefficient), we can iterate through possible lengths k and directly check if they lead to a valid starting point a. This transforms a potentially complex search problem into a simple iteration with mathematical checks.

Learn more about Math patterns.

Solution Approach

Based on our mathematical derivation, we implement the solution by checking each possible value of k (the number of consecutive integers) and verifying if it leads to a valid representation.

The implementation follows these steps:

  1. Double the value of n: We start by computing n << 1 (which is n × 2) since all our conditions involve 2n.

  2. Initialize variables: Set ans = 0 to count valid ways, and k = 1 to start checking from sequences of length 1.

  3. Iterate through possible values of k: We loop while k × (k + 1) ≤ 2n. This bound comes from our constraint that a ≥ 1, which gives us 2n ≥ k × (k + 1).

  4. Check validity conditions: For each k, we verify two conditions:

    • n % k == 0: This checks if k divides 2n evenly (note that since we already doubled n, we check n % k in the code)
    • (n // k - k + 1) % 2 == 0: This ensures that 2n/k - k + 1 is even, which guarantees that a will be a positive integer
  5. Count valid representations: If both conditions are satisfied, we increment ans by 1.

  6. Move to next k: Increment k and continue the loop.

The algorithm efficiently checks all possible lengths of consecutive sequences in O(√n) time complexity, since k can be at most around √(2n) based on the constraint k × (k + 1) ≤ 2n.

Here's how the code maps to our mathematical conditions:

  • n <<= 1 computes 2n
  • The while loop condition k * (k + 1) <= n ensures we stay within valid bounds
  • n % k == 0 checks divisibility
  • (n // k - k + 1) % 2 == 0 verifies the evenness condition for a valid starting point

This approach is elegant because it avoids the need to actually find the starting value a for each sequence - we just verify that a valid a exists through our mathematical conditions.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with n = 9 to see how we count the ways to express it as consecutive positive integers.

Step 1: Double n

  • n = 9 × 2 = 18 (we work with 2n for our formula)

Step 2: Check each possible length k

For each k, we need:

  • Condition 1: k × (k + 1) ≤ 18 (upper bound)
  • Condition 2: 18 % k == 0 (k divides 2n)
  • Condition 3: (18/k - k + 1) % 2 == 0 (ensures valid starting point)

k = 1:

  • 1 × 2 = 2 ≤ 18
  • 18 % 1 = 0
  • (18/1 - 1 + 1) = 18 % 2 = 0
  • Valid! This represents 9 (single number)

k = 2:

  • 2 × 3 = 6 ≤ 18
  • 18 % 2 = 0
  • (18/2 - 2 + 1) = 8 % 2 = 0
  • Valid! This represents 4 + 5 = 9

k = 3:

  • 3 × 4 = 12 ≤ 18
  • 18 % 3 = 0
  • (18/3 - 3 + 1) = 4 % 2 = 0
  • Valid! This represents 2 + 3 + 4 = 9

k = 4:

  • 4 × 5 = 20 > 18
  • Stop here as we've exceeded our bound

Result: There are 3 ways to express 9 as consecutive positive integers:

  • 9 (one number)
  • 4 + 5 (two consecutive numbers)
  • 2 + 3 + 4 (three consecutive numbers)

The algorithm efficiently found all valid representations by checking mathematical conditions rather than trying all possible starting points.

Solution Implementation

1class Solution:
2    def consecutiveNumbersSum(self, n: int) -> int:
3        # Double n to avoid floating point arithmetic
4        # This allows us to work with 2n = k * (2x + k - 1)
5        doubled_n = n << 1
6      
7        # Initialize counter for valid ways and starting sequence length
8        count = 0
9        sequence_length = 1
10      
11        # Iterate through possible sequence lengths
12        # k * (k + 1) represents the minimum sum for k consecutive numbers starting from 1
13        # If k * (k + 1) > 2n, then we can't form a valid sequence
14        while sequence_length * (sequence_length + 1) <= doubled_n:
15            # Check two conditions:
16            # 1. doubled_n is divisible by sequence_length
17            # 2. The starting number would be a positive integer
18            #    (doubled_n // sequence_length - sequence_length + 1) must be even
19            #    to ensure the starting number is an integer
20            if (doubled_n % sequence_length == 0 and 
21                (doubled_n // sequence_length - sequence_length + 1) % 2 == 0):
22                count += 1
23          
24            sequence_length += 1
25      
26        return count
27
1class Solution {
2  
3    public int consecutiveNumbersSum(int n) {
4        // Double the value of n for the mathematical formula
5        // If we have k consecutive numbers starting from a, the sum is:
6        // k * a + k * (k - 1) / 2 = n
7        // Rearranging: 2n = k * (2a + k - 1)
8        int doubleN = n << 1;
9        int count = 0;
10      
11        // Iterate through possible sequence lengths k
12        // k * (k + 1) represents the minimum sum for k consecutive numbers starting from 1
13        // This bounds our search space
14        for (int sequenceLength = 1; 
15             sequenceLength * (sequenceLength + 1) <= doubleN; 
16             sequenceLength++) {
17          
18            // Check if current sequence length k is valid:
19            // 1. doubleN must be divisible by k
20            // 2. The starting number 'a' must be a positive integer
21            //    From 2n = k * (2a + k - 1), we get: a = (2n/k - k + 1) / 2
22            //    For 'a' to be an integer, (2n/k - k + 1) must be even
23            if (doubleN % sequenceLength == 0) {
24                int quotient = doubleN / sequenceLength;
25                // Check if (quotient - sequenceLength + 1) is even
26                // This ensures the starting number is a positive integer
27                if ((quotient + 1 - sequenceLength) % 2 == 0) {
28                    count++;
29                }
30            }
31        }
32      
33        return count;
34    }
35}
36
1class Solution {
2public:
3    int consecutiveNumbersSum(int n) {
4        // Double n to avoid floating point arithmetic when checking divisibility
5        // This allows us to work with 2n = k * (2x + k - 1)
6        n <<= 1;
7      
8        int count = 0;
9      
10        // Iterate through possible lengths of consecutive sequences
11        // k represents the number of consecutive integers in the sum
12        // The condition k * (k + 1) <= n ensures the starting number remains positive
13        for (int length = 1; length * (length + 1) <= n; ++length) {
14            // Check two conditions:
15            // 1. n is divisible by length (ensures integer solution)
16            // 2. (n/length + 1 - length) is even (ensures starting number is an integer)
17            //    This comes from: 2x = n/length - length + 1, where x is the starting number
18            if (n % length == 0 && (n / length + 1 - length) % 2 == 0) {
19                ++count;
20            }
21        }
22      
23        return count;
24    }
25};
26
1/**
2 * Counts the number of ways to express n as a sum of consecutive positive integers
3 * @param n - The target number to express as consecutive sum
4 * @returns The count of different ways to express n
5 */
6function consecutiveNumbersSum(n: number): number {
7    let waysCount = 0;
8  
9    // Double n for easier calculation (avoiding fractions in the formula)
10    // This helps us work with 2n = k * (2a + k - 1)
11    const doubledN = n << 1;
12  
13    // Iterate through possible sequence lengths k
14    // k * (k + 1) <= 2n ensures the starting number 'a' remains positive
15    // Since a = (2n/k - k + 1) / 2, we need 2n/k > k - 1, hence k * (k + 1) <= 2n
16    for (let sequenceLength = 1; sequenceLength * (sequenceLength + 1) <= doubledN; sequenceLength++) {
17        // Check if current sequence length k is valid:
18        // 1. doubledN must be divisible by k
19        // 2. (doubledN / k - k + 1) must be even (so that a is an integer)
20        const isDivisible = doubledN % sequenceLength === 0;
21        const quotient = Math.floor(doubledN / sequenceLength);
22        const isStartingNumberInteger = (quotient + 1 - sequenceLength) % 2 === 0;
23      
24        if (isDivisible && isStartingNumberInteger) {
25            waysCount++;
26        }
27    }
28  
29    return waysCount;
30}
31

Time and Space Complexity

The time complexity is O(√n), where n is the given positive integer. The loop continues while k * (k + 1) <= n, which after doubling n (from n <<= 1) becomes k * (k + 1) <= 2n. This inequality means k² + k <= 2n, so approximately k² <= 2n, giving us k <= √(2n). Since the loop increments k from 1 until this condition is violated, it runs approximately √(2n) times, which is O(√n).

The space complexity is O(1). The algorithm only uses a constant amount of extra space for variables ans and k, regardless of the input size.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow When Computing k × (k + 1)

One common pitfall is potential integer overflow when calculating k * (k + 1) in the while loop condition, especially for large values of n. In languages with fixed integer sizes, this multiplication could overflow before the comparison is made.

Solution: Rearrange the inequality to avoid large multiplications:

# Instead of:
while sequence_length * (sequence_length + 1) <= doubled_n:

# Use:
while sequence_length <= int((2 * doubled_n) ** 0.5) + 1:
# Or check with division to avoid overflow:
while sequence_length <= doubled_n // sequence_length:

2. Confusion About Why We Double n

Developers often forget why we double n at the beginning and may accidentally use the wrong variable in calculations, leading to incorrect results. The doubling is done to avoid floating-point arithmetic when working with the formula 2n = k × (2a + k - 1).

Solution: Use clear variable names and maintain consistency:

def consecutiveNumbersSum(self, n: int) -> int:
    # Keep original n for clarity
    original_n = n
    doubled_n = 2 * original_n  # More explicit than n << 1
  
    count = 0
    k = 1  # Use k instead of sequence_length for consistency with formula
  
    while k * (k + 1) <= doubled_n:
        # Now it's clear we're working with doubled_n throughout
        if doubled_n % k == 0 and (doubled_n // k - k + 1) % 2 == 0:
            count += 1
        k += 1
  
    return count

3. Off-by-One Errors in the Mathematical Formula

A subtle pitfall is misunderstanding the formula derivation. The sum of k consecutive integers starting from a is a + (a+1) + ... + (a+k-1), NOT a + (a+1) + ... + (a+k). This affects the formula and can lead to incorrect implementations.

Solution: Always verify with small examples:

# Test with n = 9
# Valid representations: [9], [4,5], [2,3,4]
# k=1: 9 = 9 ✓
# k=2: 9 = 4+5 ✓  
# k=3: 9 = 2+3+4 ✓
# Your solution should return 3 for n=9

4. Forgetting Edge Cases

Some implementations might not handle edge cases properly, such as n = 1 or very large values of n.

Solution: Add explicit handling if needed:

def consecutiveNumbersSum(self, n: int) -> int:
    if n == 1:
        return 1  # Only one way: [1]
  
    # Continue with main algorithm...

5. Incorrect Modulo Check

A critical error occurs when developers check (doubled_n // k) % 2 instead of (doubled_n // k - k + 1) % 2. The -k+1 part is essential for ensuring the starting value a is a positive integer.

Solution: Remember the complete formula and what each part represents:

# The condition (doubled_n // k - k + 1) % 2 == 0 ensures:
# 2a = doubled_n // k - k + 1 is even (which it must be since 2a is even)
# This guarantees 'a' is a positive integer

# Wrong:
if doubled_n % k == 0 and (doubled_n // k) % 2 == 0:  # Missing -k+1

# Correct:
if doubled_n % k == 0 and (doubled_n // k - k + 1) % 2 == 0:
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How does quick sort divide the problem into subproblems?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More