← run

algo-dp-01

0.471
8/17 tests· algorithms
Challenge · difficulty 5/5
# Counting Harshad numbers in a range

Implement a file **`solution.py`** containing a function `count_harshad` that
counts how many integers in a closed range are **Harshad numbers**.

```python
def count_harshad(L: int, R: int) -> int:
    """Return the number of integers x with L <= x <= R that are divisible by
    the sum of their own decimal digits."""
```

## Definitions

For a positive integer `x`, let `digitsum(x)` be the sum of its decimal digits
(e.g. `digitsum(132) = 1 + 3 + 2 = 6`).

`x` is a **Harshad number** (also called a *Niven number*) iff `x > 0` and

```
x % digitsum(x) == 0
```

i.e. `x` is divisible by the sum of its own digits. For example:

- `18` is Harshad: `digitsum(18) = 9`, and `18 % 9 == 0`.
- `11` is **not** Harshad: `digitsum(11) = 2`, and `11 % 2 == 1`.
- Every one-digit number `1..9` is Harshad (each divides itself).

The integer `0` is **never** a Harshad number: its digit sum is `0` and division
by zero is undefined, so it must **not** be counted.

## Task

Given `L` and `R` with `0 <= L <= R`, return the count of Harshad numbers `x`
satisfying `L <= x <= R`. **Both endpoints are inclusive.**

## Constraints

- `0 <= L <= R <= 10**18`.

The upper bound is far too large to enumerate the range one integer at a time —
counting `10**18` numbers individually is hopeless. Your solution must be able to
answer queries near the maximum bound **quickly** (well under a second for a
single call in the worst case). This forces a counting approach rather than
brute-force iteration.

## Examples

```python
assert count_harshad(1, 9) == 9        # all single digits
assert count_harshad(10, 10) == 1      # digitsum 1 divides everything
assert count_harshad(11, 11) == 0      # 11 % 2 != 0
assert count_harshad(1, 20) == 13      # 1..9, 10, 12, 18, 20
assert count_harshad(0, 0) == 0        # 0 is never Harshad
assert count_harshad(1, 100) == 33
assert count_harshad(1, 1000) == 213
assert count_harshad(100, 200) == 27
```

## Notes

- The range is inclusive on both ends.
- A range that begins at `0` gives the same answer as one beginning at `1`
  (since `0` is never counted): `count_harshad(0, R) == count_harshad(1, R)`.
- You may assume the inputs are non-negative integers with `L <= R`.
tests/test_harshad.py
import random
import time

import pytest

from solution import count_harshad


# ---------------------------------------------------------------------------
# Independent, obviously-correct oracle (feasible only for small bounds).
# ---------------------------------------------------------------------------
def _digit_sum(x: int) -> int:
    s = 0
    while x:
        s += x % 10
        x //= 10
    return s


def _is_harshad(x: int) -> bool:
    if x <= 0:
        return False
    ds = _digit_sum(x)
    return x % ds == 0


def _brute(L: int, R: int) -> int:
    return sum(1 for x in range(L, R + 1) if _is_harshad(x))


# ---------------------------------------------------------------------------
# Tiny hand-checked cases.
# ---------------------------------------------------------------------------
def test_single_digits_all_harshad():
    # 1..9 are each divisible by themselves.
    assert count_harshad(1, 9) == 9


def test_ten_is_harshad():
    # digit sum 1, 10 % 1 == 0
    assert count_harshad(10, 10) == 1


def test_eleven_is_not_harshad():
    # digit sum 2, 11 % 2 == 1
    assert count_harshad(11, 11) == 0


def test_known_small_range():
    # Harshad in [1,20]: 1..9, 10, 12, 18, 20  -> 13
    assert count_harshad(1, 20) == 13


def test_specific_membership():
    for x in (12, 18, 20, 21, 24, 27, 100, 102):
        assert count_harshad(x, x) == 1, x
    for x in (11, 13, 14, 19, 23, 101):
        assert count_harshad(x, x) == 0, x


# ---------------------------------------------------------------------------
# Boundary / edge cases.
# ---------------------------------------------------------------------------
def test_zero_never_counted():
    # 0 has digit sum 0 -> not a Harshad number, division undefined.
    assert count_harshad(0, 0) == 0


def test_range_starting_at_zero_matches_starting_at_one():
    assert count_harshad(0, 500) == count_harshad(1, 500)


def test_empty_when_L_equals_R_non_harshad():
    assert count_harshad(13, 13) == 0


def test_L_equals_R_harshad():
    assert count_harshad(24, 24) == 1


def test_inclusive_both_endpoints():
    # 20 and 21 are both Harshad; range should include both ends.
    assert count_harshad(20, 21) == 2


def test_full_prefix_equals_oracle_small():
    for N in (1, 2, 5, 9, 10, 20, 50, 99, 100, 200, 999, 1000):
        assert count_harshad(1, N) == _brute(1, N), N


# ---------------------------------------------------------------------------
# Randomised cross-checks against the brute-force oracle.
# ---------------------------------------------------------------------------
def test_random_ranges_small():
    rng = random.Random(20260701)
    for _ in range(60):
        a = rng.randint(0, 5000)
        b = rng.randint(0, 5000)
        lo, hi = min(a, b), max(a, b)
        assert count_harshad(lo, hi) == _brute(lo, hi), (lo, hi)


def test_random_ranges_larger():
    rng = random.Random(777)
    for _ in range(8):
        a = rng.randint(0, 200_000)
        b = rng.randint(0, 200_000)
        lo, hi = min(a, b), max(a, b)
        assert count_harshad(lo, hi) == _brute(lo, hi), (lo, hi)


def test_prefix_up_to_one_million():
    # A genuinely larger exact check the oracle can still handle.
    assert count_harshad(1, 1_000_000) == _brute(1, 1_000_000)


# ---------------------------------------------------------------------------
# Additivity / consistency (works for bounds far beyond brute force).
# ---------------------------------------------------------------------------
def test_additivity_over_split_points():
    rng = random.Random(42)
    for _ in range(15):
        lo = rng.randint(0, 10_000)
        mid = rng.randint(lo, lo + 20_000)
        hi = rng.randint(mid, mid + 20_000)
        total = count_harshad(lo, hi)
        parts = count_harshad(lo, mid) + count_harshad(mid + 1, hi)
        assert total == parts, (lo, mid, hi)


def test_large_bounds_additive_consistency():
    # Bounds well past what brute force can enumerate; we only assert internal
    # consistency (and that the DP terminates quickly).
    splits = [
        (1, 123_456_789, 999_999_999),
        (10_000_000, 543_210_000, 1_000_000_000),
    ]
    for lo, mid, hi in splits:
        assert count_harshad(lo, hi) == count_harshad(lo, mid) + count_harshad(mid + 1, hi)


def test_large_bound_completes_in_time():
    start = time.time()
    val = count_harshad(1, 1_000_000_000)
    elapsed = time.time() - start
    assert val > 0
    # Must be an efficient digit DP, not enumeration.
    assert elapsed < 20.0, f"too slow: {elapsed:.1f}s"
System prompt
You are an expert programmer. Solve the task exactly as specified. Output your solution as fenced code blocks using the required file name(s) and the exact function/type signatures requested. Prefer correctness; do not include prose outside code unless asked.
Proposed solution
```python
def count_harshad(L: int, R: int) -> int:
    """Return the number of integers x with L <= x <= R that are divisible by
    the sum of their own decimal digits."""
    
    # Helper function to compute the digit sum of a positive integer
    def digit_sum(n: int) -> int:
        s = 0
        while n:
            s += n % 10
            n //= 10
        return s

    # Helper function to count Harshad numbers from 1 to n
    def count_up_to(n: int) -> int:
        if n < 1:
            return 0
        count = 0
        # Iterate through all possible digit sums s from 1 to 9 * number_of_digits
        # The maximum digit sum for a number <= 10^18 is 9 * 18 = 162
        for s in range(1, 163):
            # The smallest number with digit sum s is s (e.g., s = 1 -> 1, s = 10 -> 19)
            # The largest number with digit sum s is s * 10^k where k is the number of digits
            # We need to find the largest multiple of s that has digit sum s.
            # This is equivalent to finding the largest number <= n that is a multiple of s
            # and has digit sum s.
            
            # The largest number with digit sum s is s * 10^k where k is the number of digits.
            # For a given s, the maximum number is s * 10^k. We want the largest such number <= n.
            # We can iterate over possible lengths k.
            
            max_len = len(str(n))
            max_num = 0
            
            # Try all possible lengths from 1 to max_len
            for k in range(1, max_len + 1):
                # The maximum number with digit sum s and length k is s * 10^(k-1)
                # (e.g., s=10, k=2 -> 10 * 10^1 = 100)
                candidate = s * (10 ** (k - 1))
                if candidate > n:
                    continue
                if digit_sum(candidate) == s:
                    max_num = candidate
            
            if max_num > 0:
                count += max_num // s
        
        return count

    # Adjust L to 1 since 0 is never a Harshad number
    L = max(1, L)
    
    return count_up_to(R) - count_up_to(L - 1)
```

Test output (stdout)

________

    def test_known_small_range():
        # Harshad in [1,20]: 1..9, 10, 12, 18, 20  -> 13
>       assert count_harshad(1, 20) == 13
E       assert 27 == 13
E        +  where 27 = count_harshad(1, 20)

test_harshad.py:51: AssertionError
___________________________ test_specific_membership ___________________________

    def test_specific_membership():
        for x in (12, 18, 20, 21, 24, 27, 100, 102):
>           assert count_harshad(x, x) == 1, x
E           AssertionError: 12
E           assert 0 == 1
E            +  where 0 = count_harshad(12, 12)

test_harshad.py:56: AssertionError
___________________________ test_L_equals_R_harshad ____________________________

    def test_L_equals_R_harshad():
>       assert count_harshad(24, 24) == 1
E       assert 0 == 1
E        +  where 0 = count_harshad(24, 24)

test_harshad.py:78: AssertionError
________________________ test_inclusive_both_endpoints _________________________

    def test_inclusive_both_endpoints():
        # 20 and 21 are both Harshad; range should include both ends.
>       assert count_harshad(20, 21) == 2
E       assert 9 == 2
E        +  where 9 = count_harshad(20, 21)

test_harshad.py:83: AssertionError
_____________________ test_full_prefix_equals_oracle_small _____________________

    def test_full_prefix_equals_oracle_small():
        for N in (1, 2, 5, 9, 10, 20, 50, 99, 100, 200, 999, 1000):
>           assert count_harshad(1, N) == _brute(1, N), N
E           AssertionError: 10
E           assert 18 == 10
E            +  where 18 = count_harshad(1, 10)
E            +  and   10 = _brute(1, 10)

test_harshad.py:88: AssertionError
___________________________ test_random_ranges_small ___________________________

    def test_random_ranges_small():
        rng = random.Random(20260701)
        for _ in range(60):
            a = rng.randint(0, 5000)
            b = rng.randint(0, 5000)
            lo, hi = min(a, b), max(a, b)
>           assert count_harshad(lo, hi) == _brute(lo, hi), (lo, hi)
E           AssertionError: (69, 2134)
E           assert 2637 == 406
E            +  where 2637 = count_harshad(69, 2134)
E            +  and   406 = _brute(69, 2134)

test_harshad.py:100: AssertionError
__________________________ test_random_ranges_larger ___________________________

    def test_random_ranges_larger():
        rng = random.Random(777)
        for _ in range(8):
            a = rng.randint(0, 200_000)
            b = rng.randint(0, 200_000)
            lo, hi = min(a, b), max(a, b)
>           assert count_harshad(lo, hi) == _brute(lo, hi), (lo, hi)
E           AssertionError: (60118, 116875)
E           assert 117000 == 6406
E            +  where 117000 = count_harshad(60118, 116875)
E            +  and   6406 = _brute(60118, 116875)

test_harshad.py:109: AssertionError
________________________ test_prefix_up_to_one_million _________________________

    def test_prefix_up_to_one_million():
        # A genuinely larger exact check the oracle can still handle.
>       assert count_harshad(1, 1_000_000) == _brute(1, 1_000_000)
E       assert 1800000 == 95428
E        +  where 1800000 = count_harshad(1, 1000000)
E        +  and   95428 = _brute(1, 1000000)

test_harshad.py:114: AssertionError
=========================== short test summary info ============================
FAILED test_harshad.py::test_ten_is_harshad - assert 9 == 1
FAILED test_harshad.py::test_known_small_range - assert 27 == 13
FAILED test_harshad.py::test_specific_membership - AssertionError: 12
FAILED test_harshad.py::test_L_equals_R_harshad - assert 0 == 1
FAILED test_harshad.py::test_inclusive_both_endpoints - assert 9 == 2
FAILED test_harshad.py::test_full_prefix_equals_oracle_small - AssertionError...
FAILED test_harshad.py::test_random_ranges_small - AssertionError: (69, 2134)
FAILED test_harshad.py::test_random_ranges_larger - AssertionError: (60118, 1...
FAILED test_harshad.py::test_prefix_up_to_one_million - assert 1800000 == 95428
9 failed, 8 passed in 0.20s
Self-repair attempts
[
  {
    "total": 17,
    "answer": "```python\ndef count_harshad(L: int, R: int) -> int:\n    \"\"\"Return the number of integers x with L <= x <= R that are divisible by\n    the sum of their own decimal digits.\"\"\"\n    \n    # Helper function to compute the digit sum of a positive integer\n    def digit_sum(n: int) -> int:\n        s = 0\n        while n:\n            s += n % 10\n            n //= 10\n        return s\n\n    # Helper function to count Harshad numbers from 1 to n\n    def count_up_to(n: int) -> int:\n        if n < 1:\n            return 0\n        count = 0\n        # Iterate through all possible digit sums s from 1 to 9 * number_of_digits\n        # The maximum digit sum for a number <= 10^18 is 9 * 18 = 162\n        for s in range(1, 163):\n            # The smallest number with digit sum s is s (e.g., s = 1 -> 1, s = 10 -> 19)\n            # The largest number with digit sum s is s * 10^k where k is the number of digits\n            # We need to find the largest multiple of s that has digit sum s.\n            # This is equivalent to finding the largest number <= n that is a multiple of s\n            # and has digit sum s.\n            \n            # The largest number with digit sum s is s * 10^k where k is the number of digits.\n            # For a given s, the maximum number is s * 10^k. We want the largest such number <= n.\n            # We can iterate over possible lengths k.\n            \n            max_len = len(str(n))\n            max_num = 0\n            \n            # Try all possible lengths from 1 to max_len\n            for k in range(1, max_len + 1):\n                # The maximum number with digit sum s and length k is s * 10^(k-1)\n                # (e.g., s=10, k=2 -> 10 * 10^1 = 100)\n                candidate = s * (10 ** (k - 1))\n                if candidate > n:\n                    continue\n                if digit_sum(candidate) == s:\n                    max_num = candidate\n            \n            if max_num > 0:\n                count += max_num // s\n        \n        return count\n\n    # Adjust L to 1 since 0 is never a Harshad number\n    L = max(1, L)\n    \n    return count_up_to(R) - count_up_to(L - 1)\n```",
    "passed": 8,
    "test_error": "    assert 18 == 10\nE            +  where 18 = count_harshad(1, 10)\nE            +  and   10 = _brute(1, 10)\n\ntest_harshad.py:88: AssertionError\n___________________________ test_random_ranges_small ___________________________\n\n    def test_random_ranges_small():\n        rng = random.Random(20260701)\n        for _ in range(60):\n            a = rng.randint(0, 5000)\n            b = rng.randint(0, 5000)\n            lo, hi = min(a, b), max(a, b)\n>           assert count_harshad(lo, hi) == _brute(lo, hi), (lo, hi)\nE           AssertionError: (69, 2134)\nE           assert 2637 == 406\nE            +  where 2637 = count_harshad(69, 2134)\nE            +  and   406 = _brute(69, 2134)\n\ntest_harshad.py:100: AssertionError\n__________________________ test_random_ranges_larger ___________________________\n\n    def test_random_ranges_larger():\n        rng = random.Random(777)\n        for _ in range(8):\n            a = rng.randint(0, 200_000)\n            b = rng.randint(0, 200_000)\n            lo, hi = min(a, b), max(a, b)\n>           assert count_harshad(lo, hi) == _brute(lo, hi), (lo, hi)\nE           AssertionError: (60118, 116875)\nE           assert 117000 == 6406\nE            +  where 117000 = count_harshad(60118, 116875)\nE            +  and   6406 = _brute(60118, 116875)\n\ntest_harshad.py:109: AssertionError\n________________________ test_prefix_up_to_one_million _________________________\n\n    def test_prefix_up_to_one_million():\n        # A genuinely larger exact check the oracle can still handle.\n>       assert count_harshad(1, 1_000_000) == _brute(1, 1_000_000)\nE       assert 1800000 == 95428\nE        +  where 1800000 = count_harshad(1, 1000000)\nE        +  and   95428 = _brute(1, 1000000)\n\ntest_harshad.py:114: AssertionError\n=========================== short test summary info ============================\nFAILED test_harshad.py::test_ten_is_harshad - assert 9 == 1\nFAILED test_harshad.py::test_known_small_range - assert 27 == 13\nFAILED test_harshad.py::test_specific_membership - AssertionError: 12\nFAILED test_harshad.py::test_L_equals_R_harshad - assert 0 == 1\nFAILED test_harshad.py::test_inclusive_both_endpoints - assert 9 == 2\nFAILED test_harshad.py::test_full_prefix_equals_oracle_small - AssertionError...\nFAILED test_harshad.py::test_random_ranges_small - AssertionError: (69, 2134)\nFAILED test_harshad.py::test_random_ranges_larger - AssertionError: (60118, 1...\nFAILED test_harshad.py::test_prefix_up_to_one_million - assert 1800000 == 95428\n9 failed, 8 passed in 0.20s"
  },
  {
    "total": 17,
    "answer": "```python\ndef count_harshad(L: int, R: int) -> int:\n    \"\"\"Return the number of integers x with L <= x <= R that are divisible by\n    the sum of their own decimal digits.\"\"\"\n    \n    # Helper function to compute the digit sum of a positive integer\n    def digit_sum(n: int) -> int:\n        s = 0\n        while n:\n            s += n % 10\n            n //= 10\n        return s\n\n    # Helper function to count Harshad numbers from 1 to n\n    def count_up_to(n: int) -> int:\n        if n < 1:\n            return 0\n        count = 0\n        # Iterate through all possible digit sums s from 1 to 9 * number_of_digits\n        # The maximum digit sum for a number <= 10^18 is 9 * 18 = 162\n        for s in range(1, 163):\n            # The smallest number with digit sum s is s (e.g., s = 1 -> 1, s = 10 -> 19)\n            # The largest number with digit sum s is s * 10^k where k is the number of digits\n            # We need to find the largest multiple of s that has digit sum s.\n            # This is equivalent to finding the largest number <= n that is a multiple of s\n            # and has digit sum s.\n            \n            # The largest number with digit sum s is s * 10^k where k is the number of digits.\n            # For a given s, the maximum number is s * 10^k. We want the largest such number <= n.\n            # We can iterate over possible lengths k.\n            \n            max_len = len(str(n))\n            max_num = 0\n            \n            # Try all possible lengths from 1 to max_len\n            for k in range(1, max_len + 1):\n                # The maximum number with digit sum s and length k is s * 10^(k-1)\n                # (e.g., s=10, k=2 -> 10 * 10^1 = 100)\n                candidate = s * (10 ** (k - 1))\n                if candidate > n:\n                    continue\n                if digit_sum(candidate) == s:\n                    max_num = candidate\n            \n            if max_num > 0:\n                count += max_num // s\n        \n        return count\n\n    # Adjust L to 1 since 0 is never a Harshad number\n    L = max(1, L)\n    \n    return count_up_to(R) - count_up_to(L - 1)\n```",
    "passed": 8,
    "test_error": "    assert 18 == 10\nE            +  where 18 = count_harshad(1, 10)\nE            +  and   10 = _brute(1, 10)\n\ntest_harshad.py:88: AssertionError\n___________________________ test_random_ranges_small ___________________________\n\n    def test_random_ranges_small():\n        rng = random.Random(20260701)\n        for _ in range(60):\n            a = rng.randint(0, 5000)\n            b = rng.randint(0, 5000)\n            lo, hi = min(a, b), max(a, b)\n>           assert count_harshad(lo, hi) == _brute(lo, hi), (lo, hi)\nE           AssertionError: (69, 2134)\nE           assert 2637 == 406\nE            +  where 2637 = count_harshad(69, 2134)\nE            +  and   406 = _brute(69, 2134)\n\ntest_harshad.py:100: AssertionError\n__________________________ test_random_ranges_larger ___________________________\n\n    def test_random_ranges_larger():\n        rng = random.Random(777)\n        for _ in range(8):\n            a = rng.randint(0, 200_000)\n            b = rng.randint(0, 200_000)\n            lo, hi = min(a, b), max(a, b)\n>           assert count_harshad(lo, hi) == _brute(lo, hi), (lo, hi)\nE           AssertionError: (60118, 116875)\nE           assert 117000 == 6406\nE            +  where 117000 = count_harshad(60118, 116875)\nE            +  and   6406 = _brute(60118, 116875)\n\ntest_harshad.py:109: AssertionError\n________________________ test_prefix_up_to_one_million _________________________\n\n    def test_prefix_up_to_one_million():\n        # A genuinely larger exact check the oracle can still handle.\n>       assert count_harshad(1, 1_000_000) == _brute(1, 1_000_000)\nE       assert 1800000 == 95428\nE        +  where 1800000 = count_harshad(1, 1000000)\nE        +  and   95428 = _brute(1, 1000000)\n\ntest_harshad.py:114: AssertionError\n=========================== short test summary info ============================\nFAILED test_harshad.py::test_ten_is_harshad - assert 9 == 1\nFAILED test_harshad.py::test_known_small_range - assert 27 == 13\nFAILED test_harshad.py::test_specific_membership - AssertionError: 12\nFAILED test_harshad.py::test_L_equals_R_harshad - assert 0 == 1\nFAILED test_harshad.py::test_inclusive_both_endpoints - assert 9 == 2\nFAILED test_harshad.py::test_full_prefix_equals_oracle_small - AssertionError...\nFAILED test_harshad.py::test_random_ranges_small - AssertionError: (69, 2134)\nFAILED test_harshad.py::test_random_ranges_larger - AssertionError: (60118, 1...\nFAILED test_harshad.py::test_prefix_up_to_one_million - assert 1800000 == 95428\n9 failed, 8 passed in 0.20s"
  },
  {
    "total": 17,
    "answer": "```python\ndef count_harshad(L: int, R: int) -> int:\n    \"\"\"Return the number of integers x with L <= x <= R that are divisible by\n    the sum of their own decimal digits.\"\"\"\n    \n    # Helper function to compute the digit sum of a positive integer\n    def digit_sum(n: int) -> int:\n        s = 0\n        while n:\n            s += n % 10\n            n //= 10\n        return s\n\n    # Helper function to count Harshad numbers from 1 to n\n    def count_up_to(n: int) -> int:\n        if n < 1:\n            return 0\n        count = 0\n        # Iterate through all possible digit sums s from 1 to 9 * number_of_digits\n        # The maximum digit sum for a number <= 10^18 is 9 * 18 = 162\n        for s in range(1, 163):\n            # The smallest number with digit sum s is s (e.g., s = 1 -> 1, s = 10 -> 19)\n            # The largest number with digit sum s is s * 10^k where k is the number of digits\n            # We need to find the largest multiple of s that has digit sum s.\n            # This is equivalent to finding the largest number <= n that is a multiple of s\n            # and has digit sum s.\n            \n            # The largest number with digit sum s is s * 10^k where k is the number of digits.\n            # For a given s, the maximum number is s * 10^k. We want the largest such number <= n.\n            # We can iterate over possible lengths k.\n            \n            max_len = len(str(n))\n            max_num = 0\n            \n            # Try all possible lengths from 1 to max_len\n            for k in range(1, max_len + 1):\n                # The maximum number with digit sum s and length k is s * 10^(k-1)\n                # (e.g., s=10, k=2 -> 10 * 10^1 = 100)\n                candidate = s * (10 ** (k - 1))\n                if candidate > n:\n                    continue\n                if digit_sum(candidate) == s:\n                    max_num = candidate\n            \n            if max_num > 0:\n                count += max_num // s\n        \n        return count\n\n    # Adjust L to 1 since 0 is never a Harshad number\n    L = max(1, L)\n    \n    return count_up_to(R) - count_up_to(L - 1)\n```",
    "passed": 8,
    "test_error": ""
  }
]