
Windows PowerShell solution to Event 6 in the 2008 Winter Scripting Games.
Solutions are also available for Windows PowerShell and Perl.
This script was an interesting one to write, mainly because the Scripting Guys had no idea what we were doing: although we were vaguely aware of the Sieve of Eratosthenes we had absolutely no idea how that algorithm worked. And even after we read a thorough explanation of the Sieve of Eratosthenes we still had no idea how that algorithm worked. Somehow or another, though, we managed to convert the Sieve of Eratosthenes to VBScript, and come up with the following solution:
Dim arrNumbers(199)
For i = 1 to 200
arrNumbers(i - 1) = i
Next
arrNumbers(0) = "Not prime"
strPrimeNumbers = "2" & vbCrLf
intDivisor = 2
Do While intDivisor < Ubound(arrNumbers)
For j = 0 to Ubound(arrNumbers)
If IsNumeric(arrNumbers(j)) Then
intResult = arrNumbers(j) Mod intDivisor
If intResult = 0 Then
arrNumbers(j) = "Not prime"
End If
End If
Next
For i = 2 to UBound(arrNumbers)
If IsNumeric(arrNumbers(i)) Then
intDivisor = arrNumbers(i)
strPrimeNumbers = strPrimeNumbers & arrNumbers(i) & vbCrLf
arrNumbers(i) = "Not prime"
Exit For
End If
Next
Loop
Wscript.Echo strPrimeNumbers
As we noted, our solution – as you can probably tell just by looking at it – is based on the Sieve of Eratosthenes. In case you’re wondering, Eratosthenes was a Greek mathematician, poet, athlete, geographer and astronomer. Besides devising a way to calculate prime numbers, Eratosthenes also developed a system of latitude and longitude, and calculated the circumference of the Earth and the distance from the Earth to the Sun.
Which, back then, was all in a day’s work for a mathematician, poet, athlete, geographer and astronomer.
The Sieve of Eratosthenes is basically a brute force approach to solving problems. (No doubt that’s why we Scripting Guys were attracted to it in the first place.) The Sieve starts off with the premise that 1 is not a prime number, but that 2 is a prime number. From there the Sieve takes the number 2 and divides it into all the other numbers in our collection. (In this case, that’s going to be 2 divided into 3, 2 divided into 4, 2 divided into 5, etc.) If 2 divides evenly into a number then we know that this number is not prime, and we remove it from the list of potential prime numbers.
Now, what if 2 doesn’t divide evenly into a number (for example, 2 does not divide evenly into 9). Does that mean that this other number is a prime number?
Well, maybe, but we can’t say that this yet. Instead, after dividing all the numbers by 2 we find the smallest number that could not be evenly divided by 2 (which happens to be 3). We tag 3 as a prime number, then divide all the remaining numbers by 3. This continues until we’ve divided all the numbers in our collection by all the prime numbers that precede it.
So does out script actually do that? Let’s see for ourselves.
The script itself starts out by dimensioning an array named arrNumbers, configuring the array so it can hold 200 values (the numbers 1 through 200). That’s what we do here:
Dim arrNumbers(199)
We then use this block of code to add the numbers 1 through 200 to the array:
For i = 1 to 200 arrNumbers(i - 1) = i Next
Note. Why do we use the syntax arrNumbers(i-1)? Well, remember, arrays always start with item 0; thus the number 1 must go in slot 0 in the array. If i is equal to 1, then we want this number put in slot 0, which just happens to be i – 1. |
Next we cheat a tiny bit:
arrNumbers(0) = "Not prime" strPrimeNumbers = "2" & vbCrLf
Why is this cheating? Well, we know that 1 is not a prime number; hence, in the first line of code, we assign the string value Not prime to the first item in our array. Next, we know that 2 is a prime number; therefore, in the second line of code we assign the value 2 (and a carriage return-linefeed) to a variable named strPrimeNumbers. As you might have guessed, each time we find a prime number we’re going to add that number to this variable.
After assigning the value 2 to a variable named intDivisor (because we’re going to start off dividing numbers by 2) we set up a Do While loop that runs as long as intDivisor is less than the highest number in our array:
Do While intDivisor < Ubound(arrNumbers)
The first thing we do inside this loop is execute the following For loop:
For j = 0 to Ubound(arrNumbers)
If IsNumeric(arrNumbers(j)) Then
intResult = arrNumbers(j) Mod intDivisor
If intResult = 0 Then
arrNumbers(j) = "Not prime"
End If
End If
Next
What are we doing here? Well, as you can see, we’re looping through all the values in the array arrNumbers. For each value we use the IsNumeric function and the following line of code to make sure that the value is a number:
If IsNumeric(arrNumbers(j)) Then
Note. Why do we need to do that? Because any time we find a number that isn’t a prime number we’re going to set the value to Not prime, just like we did for the number 1. |
Assuming we have a number, we then use the following line of code to divide that number by intDivisor and return only the remainder (that’s what the Mod operator does):
intResult = arrNumbers(j) Mod intDivisor
If the remainder is 0, that means that intDivisor can be evenly divided into the number; needless to say, that also means that this is not a prime number. In that case, we use this line of code to change the value in the array to Not prime:
arrNumbers(j) = "Not prime"
Now, suppose the remainder is not 0. Does that mean that this is a prime number? Well, maybe, but we can’t say for sure until we’ve finished divided this number by every other number. Therefore we don’t do anything at all; we just leave the value as-is.
When we’re all done, the first few items in our array should look like this:
Not prime 2 3 Not prime 5 Not prime 7 Not prime
Now it’s time to make our next pass through the collection of numbers; this block of code gets us ready to do just that:
For i = 2 to UBound(arrNumbers)
If IsNumeric(arrNumbers(i)) Then
intDivisor = arrNumbers(i)
strPrimeNumbers = strPrimeNumbers & arrNumbers(i) & vbCrLf
arrNumbers(i) = "Prime"
Exit For
End If
Next
What we’re doing here is looping through the array, starting with the third item in the array. (We can skip items 0 and 1, which represent the values 1 and 2, because we’ve already determined whether or not they are prime numbers.) Following the Sieve of Eratosthenes, we check the third item in the array to see if it is a number. If it is, that means that we’ve found the next prime number in the collection. Therefore, we assign this value to intDivisor. In addition, we add the value to the list of prime numbers; that’s what this line of code is for:
strPrimeNumbers = strPrimeNumbers & arrNumbers(i) & vbCrLf
After that we assign the string value Prime to that spot in the array; that ensures that we’ll skip this value the next time we enter the loop. (We need to skip it because we’ve already used it as a divisor.) Finally, and because we only want the smallest number left in the array, we call the Exit For statement to exit the loop. That has the effect of taking us back to the top of our Do loop, where we repeat this process all over again, this time using the new value of intDivisor.
And once we finish with the Do loop we can echo back the list of prime numbers simply by echoing back the value of the variable strPrimeNumbers:
Wscript.Echo strPrimeNumbers
Here, for your viewing pleasure, are the prime numbers between 1 and 200:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199