Prime Filter — Beginner Project Looking for Feedback

Prime Filter — Beginner Project Looking for Feedback

I’ve been learning programming recently, and one day while watching a YouTube video I remembered the rule that a number is divisible by 3 if the sum of its digits is divisible by 3.

That made me curious about whether simple mathematical rules like this could help reduce the amount of work needed when checking for prime numbers.

So I started experimenting with prime number optimization problems as a way to improve my understanding of:

  • algorithms

  • filtering

  • performance

  • computational efficiency

This project is called Prime Filter.

The basic idea is simple:
before running a more expensive prime check, I try to eliminate numbers early using lightweight filters such as:

  • even number checks

  • last digit filtering

  • digit sum rules

After filtering, the remaining numbers go through a standard prime-checking function.

I know this is probably not the best or most advanced approach, but the goal of this project is learning and experimentation rather than building a perfect solution.

I also want to be transparent that I built a large part of this project with the help of AI tools. I used AI to:

  • understand concepts

  • explore optimization ideas

  • structure experiments

  • improve and rewrite parts of the code

Even with AI assistance, I still spent time trying to understand the logic and experimenting with different approaches myself.

I used C for this project because it was the only language besides Python that I could realistically work with, and I wanted more accurate performance measurements than I expected from Python.

That said, I’m still very much a beginner in C and generally more comfortable with Python. If there are ways to do this kind of experimentation properly in Python while still getting reliable benchmarking results, I’d really appreciate suggestions.

Here is the current version of the experiment:

#include <stdio.h>
#include <math.h>
#include <time.h>

typedef int (*filter_fn)(int);

/* ---------------- PRIME SOLVER ---------------- */

int base_prime(int n) {
    if (n < 2) return 0;

    int limit = (int)sqrt(n);
    for (int i = 2; i <= limit; i++) {
        if (n % i == 0)
            return 0;
    }
    return 1;
}

/* ---------------- DIGIT UTIL ---------------- */

int digit_sum(int n) {
    int s = 0;
    while (n > 0) {
        s += n % 10;
        n /= 10;
    }
    return s;
}

/* ---------------- FILTERS ---------------- */

int filter_even(int n) {
    if (n == 2) return 1;
    return n % 2 != 0;
}

int filter_last_digit(int n) {
    if (n < 10) return 1;

    int last = n % 10;
    if (last == 0 || last == 2 || last == 4 || last == 5 || last == 6 || last == 8)
        return 0;

    return 1;
}

int filter_digit_sum(int n) {
    if (n <= 3) return 1;
    return digit_sum(n) % 3 != 0;
}

/* ---------------- EXPERIMENT RUNNER ---------------- */

void run_experiment(const char *name, filter_fn filters[], int filter_count, int limit) {

    int filtered = 0;
    int tested = 0;
    int primes = 0;

    clock_t start = clock();

    for (int n = 2; n < limit; n++) {

        int skip = 0;

        for (int i = 0; i < filter_count; i++) {
            if (!filters[i](n)) {
                filtered++;
                skip = 1;
                break;
            }
        }

        if (skip) continue;

        tested++;

        if (base_prime(n))
            primes++;
    }

    clock_t end = clock();
    double time_spent = (double)(end - start) / CLOCKS_PER_SEC;

    printf("\n=== %s ===\n", name);
    printf("filtered: %d\n", filtered);
    printf("tested: %d\n", tested);
    printf("primes: %d\n", primes);
    printf("time: %f sec\n", time_spent);
}

/* ---------------- MAIN ---------------- */

int main() {

    int limit = 10000000;

    /* experiment setups */

    filter_fn none[] = {};

    filter_fn even_only[] = {
        filter_even
    };

    filter_fn last_digit_only[] = {
        filter_last_digit
    };

    filter_fn digit_sum_only[] = {
        filter_digit_sum
    };

    filter_fn combined[] = {
        filter_last_digit,
        filter_digit_sum
    };

    run_experiment("baseline", none, 0, limit);
    run_experiment("even filter", even_only, 1, limit);
    run_experiment("last digit filter", last_digit_only, 1, limit);
    run_experiment("digit sum filter", digit_sum_only, 1, limit);
    run_experiment("combined filters", combined, 2, limit);

    return 0;
}

At the moment, I’m mainly looking for feedback from more experienced programmers.

I would especially appreciate feedback on:

  • whether these filters actually help performance

  • mistakes in my logic or assumptions

  • bad coding practices I should fix early

  • cleaner ways to structure this experiment

  • better mathematical or algorithmic approaches

  • what concepts I should study next

Since I’m still a beginner, even simple explanations or criticism would help a lot.

The main purpose of this project is to learn how to think about optimization, filtering, and computational efficiency in real programs.
while i was trying this i found that gpu are good at parallel work but only at simple mathematical work only and i thought that this filter could simplify the process such that it can run in batches in parallel . but as it is i do not understand enough of programing in general and gpu to implement this and see the improvement . i also do not have gpu to test on so this is mostly theoretical .

I started this project while learning about prime numbers and optimization. At first, the idea was simple: use lightweight filters before running expensive prime checks.

Examples:

  • even number filtering

  • last digit filtering

  • digit sum divisibility rules

This eventually led me into experimenting with modular arithmetic, number bases, and large-number representations.

One idea I became interested in was the relationship between:

  • number bases

  • divisibility rules

  • and computational efficiency

For example:

  • in base 10, the last digit determines divisibility by 2 and 5

  • the digit sum determines divisibility by 3 and 9

This happens because of how powers of the base behave modulo certain numbers.

That made me wonder whether similar filters could exist for other primes like:

  • 7

  • 11

  • 13

  • 17

I started exploring whether converting numbers into different bases could create similar “cheap filters” for those primes.

One direction I explored was:

  • instead of converting an entire huge number into another base,

  • convert only the last few digits,

  • then use properties of that base to determine divisibility quickly.

The motivation behind this idea was performance:
for extremely large numbers (hundreds or thousands of digits), full arithmetic operations become expensive, while operations on small chunks or digit sums might be parallelizable or GPU-friendly.

During this exploration I learned several important things:

  • divisibility rules based only on the last digits work mainly when the divisor shares factors with the base

  • in base 10, suffix-based divisibility works naturally for 2 and 5

  • in base 7, suffix-based divisibility works naturally for 7

  • but for unrelated primes like 11 or 13, divisibility depends on the entire positional structure of the number

So simply converting only the suffix into another base does not preserve enough information to reliably determine divisibility for arbitrary primes.

I also learned that:

  • mathematically elegant ideas are not always computationally faster

  • modern CPUs optimize modulo operations very aggressively

  • many “clever” filters may not outperform direct modular arithmetic in practice

At the same time, some parts of this exploration seem related to real topics such as:

  • wheel factorization

  • modular arithmetic

  • chunked modular reduction

  • residue number systems

  • GPU reductions

  • arbitrary precision arithmetic

I’m still a beginner, so I’m mainly interested in learning:

  • whether any part of this idea is computationally useful

  • whether similar approaches already exist formally

  • how modern primality testing libraries handle filtering

  • and what topics I should study next to better understand this area.

I’d appreciate corrections, criticism, or pointers to existing algorithms or mathematical concepts related to these ideas.

Welcome to the forum @vivekdave1992

A quick way to check is to see if the number is divisible by a prime number.

If the number is not divisible by the prime numbers before it, then it is a prime number.

Happy coding

i am trying to find a way to check if number is prime with simple math like checking last digits or just sum of all digits . we know that aside from 2 and 3 all prime must be in form of 6k±1 so i thought of using that . primes are odd numbers so -1 or +1 would be even numbers , so what is left is to check if prime±1 is dividable 3 which we can do with digit sum . i learn this yesterday so it is not used in this . but it is cool logic to use to remove lots of non prime . we can’t identify prime directly but can use this to filter it

Hi @vivekdave1992

I think someone on the forum used that approach, but they found implementing the logic challenging. 6k±1 can be used to tell if a number is prime, but it is not a guarantee.

For example, take 25:

25 + 1 = 26 (not divisible by 3)
25 - 1 = 24 (is divisible by 3)

But 25 is divisible by 5 so it is not a prime number.

A simpler way for a range of primes is to start with a seed of prime numbers such as 2, 3, 5

6 is divisible by 2 so is not a prime
7 is not divisible by any prime in the list so is added to the prime list
8 is divisible by 2 so is not a prime
9 is divisible by 3 so is not a prime
10 is divisible by 2 so is not a prime
11 is not divisible by any number in the list so is added to the list
12 is divisible by 2 so is not a prime
13 is not divisible by any prime so is added to the prime list
14 is divisible by 2 so is not a prime
15 is divisible by 3 so is not a prime
16 is divisible by 2 so is not a prime
17 is not divisible by any prime so is added to the prime list
18 is divisible by 2 so is not a prime
19 is not divisible by any prime so is added to the prime list
20 is divisible by 2 so is not a prime

Happy coding

yes, that is main misunderstanding i had about 6k±1 . later i learn that prime have to be in configuration of 6k±1 but not every 6k±1 is prime . i switched to creating it as filter rather than direct solution. i am thinking of using like this
number±1 %3 ==0 than it might be prime else it is not prime . so we get filter where
number+1%3 ==1 or 2 would not be prime .but for much longer number like 265+ length number doing any math would be time consuming . my idea was for specifically larger length numbers . where digit sum can be useful compare to traditional math .

If the number is greater than 9 and ends in 0, 2, 4, 5, 6, or 8 then it is not a prime number.

those already removed, with even filter and filter of 5 . last digit is also something i have thought of . i was thinking of changing base of number such that we can get direct results form last digits . but it is not very good way to solve for large number as it would take much longer to convert form base10 to any other base . i also thought of trying base of 2** that is 4,8,16,32…256 etc. it could allow for last digit to convert without converting entire number but as they are multiples of 2 it is not useful in prime finder . i also thought of converting to prime base like base of 7 or 13 .. but at least currently they do not seem useful mathematically, still my idea was to just convert last few digits to get result but it does not work mathematically . i am still trying to understand the conversion happening as we transform one base to another .