-2

I have recently started using python and to try learn I have set a task of being able to run two chunks of code at once. I have 1 chunk of code to generate and append prime numbers into a list

primes=[]
for num in range(1,999999999999 + 1): 
    if num > 1: 
        for i in range(2,num): 
            if (num % i) == 0: 
                break 
        else: 
            primes.append(num)

And another chunk of code to use the prime numbers generates to find perfect numbers

limit = 25000000000000000000
for p in primes():
    pp = 2**p
    perfect = (pp - 1) * (pp // 2)
    if perfect > limit:
        break
    elif is_prime(pp - 1):
        print(perfect)

I have heard of something to do with importing thread or something along those lines but I am very confused by it, if anyone can help by giving me clear instructions on what to do that would be very appreciated. I have only been learning python for about a week now.

Final note, I didn't code these calculations myself but I have modified them to what I need them for

Scottish
  • 7
  • 4
  • 1
    If you want stuff to happen truly simultaneously, then you need to look into ‘multiprocessing’. – quamrana Oct 03 '19 at 18:52

2 Answers2

1

Why do 2 for loops at once when you can just put the logic of the second loop inside the first loop: Just instead of the break in the perfects loop use a bool to determine if you've reached the limit.

Also you don't need to check if num > 1. Just start the range at 2

primes=[]
limit = 25_000_000_000_000_000_000
reached_limit = False

def is_prime(n):
    return 2 in [n,2**n%n]

for num in range(2, 1_000_000_000_000): 
    for i in range(2,num): 
        if (num % i) == 0: 
            break 
    else: 
        primes.append(num)
        if not reached_limit:
            pp = 2 ** num
            perfect = (pp - 1) * (pp // 2)
            if perfect > limit:
                reached_limit = True
            elif is_prime(pp-1):
                print(perfect)
Jab
  • 26,853
  • 21
  • 75
  • 114
  • For some reason I didn't think about this. The code you have given doesn't work but I reckon if I give it some time I can make it work, which will help me learn even more :D, many thanks <3 – Scottish Oct 03 '19 at 19:20
  • The code didn't work because you didn't have an `isPrime` function in your code. My assumption was you already had one. I just edited and added one in my code from [this answer](https://stackoverflow.com/a/32882220/225020) – Jab Oct 03 '19 at 19:28
  • That said, if this answers your question please mark it as so with the check mark to the left of the answer. – Jab Oct 03 '19 at 19:31
1

You can use the multiprocessing library to accomplish this. The basic idea is to have two sets of processes. The first process can fill up a queue with primes, and then you can delegate other processes to deal with those primes and print your perfect numbers.

I did make some changes and implemented a basic is_prime function. (Note that for this implementation you only need to check until the square root of the number). There are better methods but that's not what this question is about.

Anyways, our append_primes function is the same as your first loop, except instead of appending a prime to a list, it puts a prime into a queue. We need some sort of signal to say that we're done appending primes, which is why we have q.put("DONE") at the end. The "DONE" is arbitrary and can be any kind of signal you want, as long as you handle it appropriately.

Then, the perfect_number is kind of like your second loop. It accepts a single prime and prints out a perfect number, if it exists. You may want to return it instead, but that depends on your requirements.

Finally, all of the logic that runs and performs the multiprocessing has to sit inside an if __name__ == "__main__" block to avoid being re-run over and over as the file is pickled and sent to the new process. We initialize our queue and create/start the process to append primes to this queue.

While that's running, we create our own version of a multiprocessing pool. Standard mp pools don't play along with queues, so we have to get a little fancy. We initialize the maximum number of processes we want to run and set it to the current cpu count minus 1 (since 1 will be running the append_primes function.

We loop over q until "DONE" is returned (remember, that's our signal from append_primes). We'll continuously loop over the process pool until we find an available process. Once that happens, we create and start the process, then move on to the next number.

Finally, we do some cleanup and make sure everything in processes is done by calling Process.join() which blocks until the process is done executing. We also ensure prime_finder has finished.

import multiprocessing as mp
import os
import queue
import time


def is_prime(n):
    """ Returns True if n is prime """
    for i in range(2, int(n**0.5)):
        if n%i == 0:
            return False
    return True


def append_primes(max, q):
    """ Searches for primes between 2 and max and adds them to the Queue (q) """
    pid = os.getpid()
    for num in range(2, int(max)+1):
        if is_prime(num):
            print(f"{pid} :: Put {num} in queue.")

            q.put(num)
    q.put("DONE") # A signal to stop processing
    return


def perfect_number(prime, limit = 25000000000000000000):
    """ Prints the perfect number, if it exists, given the prime """
    pp = 2**prime
    perfect = (pp - 1) * (pp // 2)
    if perfect > limit:
        return
    if is_prime(pp - 1):
        print(f"{os.getpid()} :: Perfect: {perfect}", flush = True)
    return


if __name__ == "__main__":
    q = mp.Queue()
    max = 1000  # When to stop looking for primes
    prime_finder = mp.Process(target = append_primes, args = (max, q,))
    prime_finder.start()
    n_processes = os.cpu_count() - 1 # -1 because 1 is for prime_finder
    processes = [None]*n_processes
    for prime in iter(q.get, "DONE"):
        proc_started = False
        while not proc_started: # Check each process till we find an 'available' one.
            for m, proc in enumerate(processes):
                if proc is None or not proc.is_alive():
                    processes[m] = mp.Process(target = perfect_number, args = (prime, ))
                    processes[m].start()
                    proc_started = True  # Get us out of the while loop
                    break                # and out of the for loop.
    for proc in processes:
        if proc is None: # In case max < n_processes
            continue
        proc.join()
    prime_finder.join()

Comment out the print statement in append_primes if you only want to see the perfect number. The number that appears before is the process' ID (just so that you can see there are multiple processes working at the same time)

SyntaxVoid
  • 2,501
  • 2
  • 15
  • 23
  • Edit: Sorry there was a tiny bug. Sometimes a prime number might not get passed to `perfect_number` if a process wasn't available. Now, it wont continue until it finds a free process. – SyntaxVoid Oct 06 '19 at 06:51