"Fewer than 20 percent of the positive integers from 1 to 1000 are prime."

mathematics · generated 2026-03-28 · v0.10.0
PROVED pure computation — no citations
Verified by computation — no external sources required.
Verified by Proof Engine — an open-source tool that verifies claims using cited sources and executable code. Reasoning transparent and auditable.
methodology · github · re-run this proof · submit your own

The numbers don't lie: primes are rarer than you might think, and the math here leaves no room for doubt.

What Was Claimed?

The claim is that if you look at every whole number from 1 to 1000, fewer than 1 in 5 of them will be prime — that is, divisible only by 1 and themselves. This might matter to anyone who's wondered how densely packed the primes are among ordinary counting numbers, or who's skeptical that primes "thin out" as numbers grow larger.

What Did We Find?

There are exactly 168 prime numbers between 1 and 1000. They start at 2 and end at 997, and they are spread unevenly through the range — clustering a bit more at the low end and growing sparser as numbers get larger.

168 out of 1000 works out to 16.8%. That's comfortably below the 20% threshold. Even if you rounded generously, you'd still be well clear of the line.

To make sure this count was right, two completely independent methods were used. The first was the Sieve of Eratosthenes, a classical algorithm that works by crossing out multiples of each prime in sequence. The second was trial division, which tests each number individually by checking whether anything smaller than its square root divides it evenly. Both methods produced the exact same list of 168 primes — every single one matching.

That agreement matters. If there were a programming error that caused primes to be missed or double-counted, it would have to afflict both algorithms in exactly the same way — which is effectively impossible given how differently they work. As a further sanity check, 168 is also the well-established value that number theorists use for this count, written π(1000) = 168.

One potential quibble: does "fewer than 20 percent" mean strictly less than 20, or could it include exactly 20? The phrase "fewer than" is unambiguously strict. But even setting that aside, 168 is well below 200 — the count would have to be 32 primes higher before the claim could even be questioned under the most lenient reading.

What Should You Keep In Mind?

This result applies exactly to the range 1 through 1000 — no more, no less. The density of primes does continue to decrease as numbers get larger (a fact captured by the Prime Number Theorem), so the 16.8% figure is actually somewhat higher than you'd find for ranges like 1 to a million. In other words, this claim holds, but don't assume the same percentage applies at larger scales.

Also worth noting: 1 is not considered prime by modern convention, and 2 is the only even prime. These edge cases are handled correctly by both algorithms, but they're easy sources of off-by-one errors in informal reasoning about primes.

How Was This Verified?

This claim was checked using two independent computational methods run against the same input range, then cross-validated against a known number-theoretic result. Full details are in the structured proof report and the full verification audit. To inspect or rerun the calculation yourself, see re-run the proof yourself.

What could challenge this verdict?

detailed evidence

Detailed Evidence

Evidence Summary

ID Fact Verified
A1 Prime count via Sieve of Eratosthenes Computed: 168 primes
A2 Prime count via trial division (independent cross-check) Computed: 168 primes
A3 Percentage of primes in [1, 1000] Computed: 16.8%

Proof Logic

Two independent algorithms enumerate all primes in [1, 1000]:

  1. Sieve of Eratosthenes (A1): Marks composites by iterating through multiples of each prime up to sqrt(1000). Produces a list of 168 primes, from 2 to 997.

  2. Trial division (A2): Tests each integer in [1, 1000] individually for primality by checking divisibility up to its square root. Also produces exactly 168 primes.

Both methods yield identical prime lists (A1, A2 — independently computed). The percentage is then 168 / 1000 x 100 = 16.8% (A3), which satisfies 16.8 < 20.

Conclusion

PROVED. There are exactly 168 prime numbers among the positive integers from 1 to 1000, which is 16.8% — strictly fewer than 20%. This was established by two independent computational methods (Sieve of Eratosthenes and trial division) that produce identical results, consistent with the known value pi(1000) = 168.

audit trail

Claim Specification
Field Value
Subject positive integers from 1 to 1000
Property percentage that are prime
Operator <
Threshold 20
Operator note 'Fewer than 20 percent' means the count of primes in [1, 1000] divided by 1000, multiplied by 100, must be strictly less than 20. Equivalently, the prime count must be strictly less than 200.
Claim Interpretation

Natural language: Fewer than 20 percent of the positive integers from 1 to 1000 are prime.

Formal interpretation: The count of primes in {1, 2, ..., 1000} divided by 1000, expressed as a percentage, is strictly less than 20. Equivalently, the prime count must be strictly less than 200. The phrase "fewer than" unambiguously implies strict inequality (<).

Computation Traces
Sieve of Eratosthenes: found 168 primes in [1, 1000]
  First 10: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
  Last 10:  [937, 941, 947, 953, 967, 971, 977, 983, 991, 997]

Trial division: found 168 primes in [1, 1000]
Cross-check: both methods agree — 168 primes
  percentage of primes in [1, 1000]: prime_count_sieve / N * 100 = 168 / 1000 * 100 = 16.8000
  prime percentage < 20%: 16.8 < 20 = True
Adversarial Checks

Check 1: Could the sieve or trial division have a bug that undercounts primes?

  • Verification performed: Cross-checked two independent algorithms (Sieve of Eratosthenes and trial division). Both produce identical prime lists. Additionally, the result of 168 primes up to 1000 is a well-known value in number theory, denoted pi(1000) = 168.
  • Finding: Both methods agree on 168 primes. This matches the known value pi(1000) = 168.
  • Breaks proof: No

Check 2: Is there an interpretation where '20 percent' could mean something other than 200 out of 1000?

  • Verification performed: Considered whether 'fewer than 20 percent' could be non-strict (<= vs <). The phrase 'fewer than' is unambiguously strict inequality. Even under <=, 168 <= 200 holds, so the claim would still be true.
  • Finding: No alternative interpretation changes the result. 168 < 200 under any reading.
  • Breaks proof: No
Quality Checks
  • Rule 1: N/A — pure computation, no empirical facts
  • Rule 2: N/A — pure computation, no empirical facts
  • Rule 3: date.today() used for generation date
  • Rule 4: CLAIM_FORMAL with operator_note explicitly documents the strict inequality interpretation
  • Rule 5: Two adversarial checks performed — algorithm correctness and interpretation ambiguity
  • Rule 6: N/A — pure computation, no empirical facts. Cross-check uses mathematically independent method (sieve vs trial division)
  • Rule 7: explain_calc() and compare() from computations.py used for all computations
  • validate_proof.py result: PASS (14/14 checks passed, 0 issues, 0 warnings)
Cite this proof
Proof Engine. (2026). Claim Verification: “Fewer than 20 percent of the positive integers from 1 to 1000 are prime.” — Proved. https://proofengine.info/proofs/fewer-than-20-percent-of-the-positive-integers-fro/
Proof Engine. "Claim Verification: “Fewer than 20 percent of the positive integers from 1 to 1000 are prime.” — Proved." 2026. https://proofengine.info/proofs/fewer-than-20-percent-of-the-positive-integers-fro/.
@misc{proofengine_fewer_than_20_percent_of_the_positive_integers_fro,
  title   = {Claim Verification: “Fewer than 20 percent of the positive integers from 1 to 1000 are prime.” — Proved},
  author  = {{Proof Engine}},
  year    = {2026},
  url     = {https://proofengine.info/proofs/fewer-than-20-percent-of-the-positive-integers-fro/},
  note    = {Verdict: PROVED. Generated by proof-engine v0.10.0},
}
TY  - DATA
TI  - Claim Verification: “Fewer than 20 percent of the positive integers from 1 to 1000 are prime.” — Proved
AU  - Proof Engine
PY  - 2026
UR  - https://proofengine.info/proofs/fewer-than-20-percent-of-the-positive-integers-fro/
N1  - Verdict: PROVED. Generated by proof-engine v0.10.0
ER  -
View proof source 172 lines · 6.4 KB

This is the proof.py that produced the verdict above. Every fact traces to code below. (This proof has not yet been minted to Zenodo; the source here is the working copy from this repository.)

"""
Proof: Fewer than 20 percent of the positive integers from 1 to 1000 are prime.
Generated: 2026-03-28
Type: Pure Math (Type A)
"""
import json
import os
import sys

PROOF_ENGINE_ROOT = os.environ.get("PROOF_ENGINE_ROOT")
if not PROOF_ENGINE_ROOT:
    _d = os.path.dirname(os.path.abspath(__file__))
    while _d != os.path.dirname(_d):
        if os.path.isdir(os.path.join(_d, "proof-engine", "skills", "proof-engine", "scripts")):
            PROOF_ENGINE_ROOT = os.path.join(_d, "proof-engine", "skills", "proof-engine")
            break
        _d = os.path.dirname(_d)
    if not PROOF_ENGINE_ROOT:
        raise RuntimeError("PROOF_ENGINE_ROOT not set and skill dir not found via walk-up from proof.py")
sys.path.insert(0, PROOF_ENGINE_ROOT)
from datetime import date

from scripts.computations import compare, explain_calc

# 1. CLAIM INTERPRETATION (Rule 4)
CLAIM_NATURAL = "Fewer than 20 percent of the positive integers from 1 to 1000 are prime."
CLAIM_FORMAL = {
    "subject": "positive integers from 1 to 1000",
    "property": "percentage that are prime",
    "operator": "<",
    "operator_note": (
        "'Fewer than 20 percent' means the count of primes in [1, 1000] divided by 1000, "
        "multiplied by 100, must be strictly less than 20. Equivalently, the prime count "
        "must be strictly less than 200."
    ),
    "threshold": 20,
}

# 2. FACT REGISTRY — A-types only for pure math
FACT_REGISTRY = {
    "A1": {"label": "Prime count via Sieve of Eratosthenes", "method": None, "result": None},
    "A2": {"label": "Prime count via trial division (independent cross-check)", "method": None, "result": None},
    "A3": {"label": "Percentage of primes in [1, 1000]", "method": None, "result": None},
}

# 3. COMPUTATION — primary method: Sieve of Eratosthenes
N = 1000

def sieve_of_eratosthenes(n):
    """Return list of primes up to n using the Sieve of Eratosthenes."""
    is_prime = [True] * (n + 1)
    is_prime[0] = is_prime[1] = False
    for i in range(2, int(n**0.5) + 1):
        if is_prime[i]:
            for j in range(i * i, n + 1, i):
                is_prime[j] = False
    return [i for i in range(2, n + 1) if is_prime[i]]

primes_sieve = sieve_of_eratosthenes(N)
prime_count_sieve = len(primes_sieve)
print(f"Sieve of Eratosthenes: found {prime_count_sieve} primes in [1, {N}]")
print(f"  First 10: {primes_sieve[:10]}")
print(f"  Last 10:  {primes_sieve[-10:]}")

# 4. CROSS-CHECK — trial division (Rule 6)
def is_prime_trial(n):
    """Check primality by trial division."""
    if n < 2:
        return False
    if n < 4:
        return True
    if n % 2 == 0 or n % 3 == 0:
        return False
    i = 5
    while i * i <= n:
        if n % i == 0 or n % (i + 2) == 0:
            return False
        i += 6
    return True

primes_trial = [i for i in range(1, N + 1) if is_prime_trial(i)]
prime_count_trial = len(primes_trial)
print(f"\nTrial division: found {prime_count_trial} primes in [1, {N}]")

# Verify both methods produce identical lists
assert primes_sieve == primes_trial, (
    f"Cross-check failed: sieve produced {prime_count_sieve} primes, "
    f"trial division produced {prime_count_trial} primes"
)
print(f"Cross-check: both methods agree — {prime_count_sieve} primes")

# 5. PERCENTAGE COMPUTATION (Rule 7)
percentage = explain_calc(
    "prime_count_sieve / N * 100",
    {"prime_count_sieve": prime_count_sieve, "N": N},
    label="percentage of primes in [1, 1000]"
)

# 6. ADVERSARIAL CHECKS (Rule 5)
adversarial_checks = [
    {
        "question": "Could the sieve or trial division have a bug that undercounts primes?",
        "verification_performed": (
            "Cross-checked two independent algorithms (Sieve of Eratosthenes and trial division). "
            "Both produce identical prime lists. Additionally, the result of 168 primes up to 1000 "
            "is a well-known value in number theory, denoted π(1000) = 168."
        ),
        "finding": "Both methods agree on 168 primes. This matches the known value π(1000) = 168.",
        "breaks_proof": False,
    },
    {
        "question": "Is there an interpretation where '20 percent' could mean something other than 200 out of 1000?",
        "verification_performed": (
            "Considered whether 'fewer than 20 percent' could be non-strict (≤ vs <). "
            "The phrase 'fewer than' is unambiguously strict inequality. Even under ≤, "
            "168 ≤ 200 holds, so the claim would still be true."
        ),
        "finding": "No alternative interpretation changes the result. 168 < 200 under any reading.",
        "breaks_proof": False,
    },
]

# 7. VERDICT AND STRUCTURED OUTPUT
if __name__ == "__main__":
    claim_holds = compare(percentage, CLAIM_FORMAL["operator"], CLAIM_FORMAL["threshold"],
                          label="prime percentage < 20%")

    # Pure-math: no citations
    verdict = "PROVED" if claim_holds else "DISPROVED"

    FACT_REGISTRY["A1"]["method"] = "Sieve of Eratosthenes"
    FACT_REGISTRY["A1"]["result"] = str(prime_count_sieve)
    FACT_REGISTRY["A2"]["method"] = "Trial division"
    FACT_REGISTRY["A2"]["result"] = str(prime_count_trial)
    FACT_REGISTRY["A3"]["method"] = "prime_count / 1000 * 100"
    FACT_REGISTRY["A3"]["result"] = f"{percentage}%"

    summary = {
        "fact_registry": {
            fid: {k: v for k, v in info.items()}
            for fid, info in FACT_REGISTRY.items()
        },
        "claim_formal": CLAIM_FORMAL,
        "claim_natural": CLAIM_NATURAL,
        "cross_checks": [
            {
                "description": "Sieve of Eratosthenes vs trial division prime count",
                "values_compared": [str(prime_count_sieve), str(prime_count_trial)],
                "agreement": prime_count_sieve == prime_count_trial,
            },
        ],
        "adversarial_checks": adversarial_checks,
        "verdict": verdict,
        "key_results": {
            "prime_count": prime_count_sieve,
            "total_integers": N,
            "percentage": percentage,
            "threshold": CLAIM_FORMAL["threshold"],
            "operator": CLAIM_FORMAL["operator"],
            "claim_holds": claim_holds,
        },
        "generator": {
            "name": "proof-engine",
            "version": open(os.path.join(PROOF_ENGINE_ROOT, "VERSION")).read().strip(),
            "repo": "https://github.com/yaniv-golan/proof-engine",
            "generated_at": date.today().isoformat(),
        },
    }

    print("\n=== PROOF SUMMARY (JSON) ===")
    print(json.dumps(summary, indent=2, default=str))

↓ download proof.py

Re-execute this proof

The verdict above is cached from when this proof was minted. To re-run the exact proof.py shown in "View proof source" and see the verdict recomputed live, launch it in your browser — no install required.

Re-execute from GitHub commit 1ba3732 — same bytes shown above.

Re-execute in Binder runs in your browser · ~60s · no install

First run takes longer while Binder builds the container image; subsequent runs are cached.

machine-readable formats

Jupyter Notebook interactive re-verification W3C PROV-JSON provenance trace RO-Crate 1.1 research object package
Downloads & raw data

found this useful? ★ star on github