The Infinite List of Prime Numbers and its Applications
We mark the first value as being prime so that's the value p here, the third step is we remove all the multiples of p so all the multiples of two initially from the list. This is the part where we remove all the non-prime numbers to leave us with just the primes.
The final step is we run the algorithm again from step two and that's calling the sieve function. So, we've got our program here, let's see it actually working so I'll start up the haskell system again. Good, we've got no errors which is good. Let's run the prime number program and here we get all the prime numbers again so it gives exactly the same results as the simpler algorithm which we wrote before but it does so a lot more quickly.
If you do some simple experiments for numbers up to this kind of size, it runs about 10 times faster than the simple algorithm which we had previously. What could we do with this well, uh, we could do a whole bunch of things so for example if I wanted a hundred prime numbers, I don't know now need to write a program which generates 100 prime numbers, I just take the infinite list of all the prime numbers and I take 100. So, I've kind of separated my control part which is taking a hundred numbers from my data part which is having an infinite list of primes.
This idea of separating control and data using infinite data structures is a very powerful way of thinking about programming and I can do other things as well so for example if I wanted to say give me all the prime numbers less than 100, I don't need to write a separate program for that now I just take my infinite list of all the prime numbers and I say take while less than 100. This is what's called laziness in programming.
It means that instead of evaluating expressions all at once, we only evaluate them when their values are needed, which can greatly improve performance. In this case, the expression `take while less than 100` will not be evaluated until I actually need to take 100 prime numbers from the list. And then, in many languages if you wanted to write these two separate programs, you'd need to write some separate programs. But if you can manipulate infinite data structures, you just have your infinite list of primes and then you can do whatever you want with them afterwards.
You can select what parts you like and that's the beauty of it. Let me do one final little example to do with what's called twin primes so twin primes are primes which differ by two so three and five are twins because they differ by two, five and seven are twins because they differ by two, but seven and eleven are not twins because they differ by four. How do we write a program using what we've already done that generates all the twin primes? So, first of all, let me say what it means to be a twin so I'll say a pair xy is a twin if y is x plus two.
So, for example, if I say is 3 and 5 a twin, it will say true and if I say is 7 and 11 a twin, then I get no. So, how do I use this to write a program that generates all the twin primes well, what I'm going to do is I'm going to generate the infinite list of all the twins and I'm going to simply filter twin from zip of primes and tail primes.
What does tail do so tail takes a list which could be infinite and removes the first thing. So if we write tail of primes, what we're going to do is take the infinite list of all prime numbers and throw the two away so we'll get 3, 5, 7, 11, etc. What zip does is it takes two lists and they can be finite or infinite so suppose I've got two lists of five numbers, what zip does is it pairs up the corresponding elements in the two lists.
So if we've got a list of prime numbers and another list that's just one number less than each of those primes, then zip will pair them up so we'll get 2 and 3, 3 and 5, 5 and 7, and so on. So what we're going to get coming out of this zip is an infinite list of pairs of each prime number and the next prime number.
So we'll get 2 and 3, 3 and 5, 5 and 7, etc. And then all we're going to do is take that infinite list of all of those pairs and filter the twins out of it. This is enough to generate all the twin primes so I can test this out.
Here's all the infinite lists of twin primes. And this is a one-line program to do this based on a two-line program which we had to generate the infinite list of all the prime numbers going back to where I started programming with infinite data structures sounds impossible, but it's not really so what's happening here is that we're using a technique called "lazy evaluation" or more specifically "monadic evaluation".
It means that instead of evaluating expressions all at once, we only evaluate them when their values are needed. And by doing it this way, we can use the same piece of code to generate both prime numbers and twin primes.
So, while it may seem like a trick, it's actually a very powerful technique for manipulating infinite data structures in programming.