Search code examples
bashunix

Sieve of Eratosthenes UNIX Script


I'm writing a UNIX script to use the sieve to generate prime numbers. I keep getting a bad modulo division on line 19, and I can't seem to figure out why.

I have tried all kinds of different formatting, not sure what the right way is.

#!bin/bash
read -p "Upper limit? :" answer
theMultiple=2

#populate the array
for ((i=2;i<$answer;i++)); do
   sieveArray[$i]=$i
done
#Use Sieve
for ((i=0;i<=${#sieveArray[*]}; i++)); do
   if [ $[$(($[${sieveArray[$i]}] % $theMultiple))] -eq 0 ]; then
         theMultiple=${sieveArray[$i]}
         echo $theMultiple
         for ((j=$i;j<${#sieveArray[*]};j++)); do
            if [ $[$(($[${sieveArray[$j]}] % $theMultiple))] -eq 0 ]; then
               sieveArray[$j]=0
            fi
         done
   fi
done
}

Solution

  • The code you have provided has multiple issues and the core cause of the error you run into is the erroneous logic used in the code. In your confusion you fail to track the flow of the code to see when and why the variable theMultiple is set to zero leading to the error in the modulo operation.

    NOTICE that the code you have provided is generally designed to use brute force and therefore the question title Sieve of Eratosthenes UNIX Script is misleading, because there is no sieving by marking of multiples of already found primes implemented in the code.

    The code you have provided does not run at all because of multiple syntax error issues as for example: line 15: 0 % : syntax error: operand expected (error token is "% ") or superfluous } in the last line.

    This all said a working code nearest to the intended one running in bash version 5.1.16 is as follows:

    #!bin/bash
    answer=100 # ; read -p "Upper limit? :" answer
    theMultiple=2
    #populate the array
    for (( i=1 ; i<=$answer; i++ )); do
       sieveArray[$i]=$i  #; echo ${sieveArray[$i]}
    done
    #Use Sieve
    for ((i=2 ; i<=${#sieveArray[@]} ; i++ )); do
       if [ ${sieveArray[i]} -gt 0 ]; then
             theMultiple=${sieveArray[$i]}
             echo -n "  $theMultiple"
             for ((j=i ; j<=${#sieveArray[@]} ; j++)); do
                if [ $(( sieveArray[j] % theMultiple)) -eq 0 ]; then
                   sieveArray[j]=0
                fi
             done
       fi
    done
    

    The answer provided by Mickaël Bucas is still using a brute force even if in a optimized way refraining from explicit usage of loops in favor of delegating the marking of multiples to the comm command which depends on preceding call to sort.

    The actual sieving algorithm is used in following bash code which implements the fact that in bash it is not necessary to create all array elements before setting values to them. In bash any name at any index has already a value being an empty string with which bash responds on request:

    #!/usr/bin/bash
    #   default n=100
    if [[ n -eq "" ]]; then n=$1; fi
    if [[ n -eq "" ]]; then n=100; fi
    if [[ n -lt 2 ]]; then exit; else echo -n "  2"; fi ; if [[ n -eq 2 ]]; then exit; fi   
    for ((i=3; i<=n ; i=i+2 )); do
        if [[ $(( i*i )) -gt n ]]; then break; fi
        if [[ isMultiple[$i] -eq 1 ]]; then continue; fi 
        for ((k=$(( i*i )) ; k<=n ; k=k+i)); do
            isMultiple[$k]=1
        done
    done
    for (( i=3; i<=n ; i=i+2 )); do  
        if [[ isMultiple[i] -eq "" ]]; then  echo -n "  $i"; fi  
    done
    echo
    

    By the way: this question and its up to now answers is probably one of the reasons why AIs like ChatGPT fail sometimes to provide correct answers because of relying on the fact that the answers to such an old question must be correct, else it would be another, better and accepted answer there.