简体   繁体   中英

Algorithm Optimization (Prime Factorization)

Before starting let me say: It's not homework, just plain, old, fun.

Now, I'm trying to come up with an algorithm that can answer this question 1/x + 1/y = 1/n! .

And as you can see by the link above, the author asked only for hints and not the actual answer, so I would kindly ask for the same.

I simplified the expression until (x - n!)(y - n!) = (n!)^2 as suggested by one of the answers , and by that time I understood that the number of combinations of (x,y) pairs is the same as the number of divisors of n!^2 (correct me if I'm wrong here).

So, as suggested by the accepted answer , I'm trying to get the multiplication of all the factors of each prime composing N!^2.

I've come up with some code in C using trial division to factorize N!^2 and the Sieve of Eratosthenes to get all the prime numbers up to sqrt(N!^2).

The problem now is memory, I have tried with N = 15 and my Mac (Quad Core 6GB of memory) almost died on me. The problem was memory. So I added some printf's and tried with N=11:

Sieve of Eratosthenes took 13339.910000 ms and used 152 mb of memory
n= 11; n!^2 = 1593350922240000; d = 6885
[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,5,5,5,5,7,7,11,11]

The list is all the prime factors of N!^2 (besides 1 and N!^2 of course).

I would like some hints on how to minimize memory consumption and possible optimizations.

Code bellow, it was just a quick experiment so I'm sure it can be optimized.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <strings.h>
#include <sys/time.h>
#include <assert.h>

//Linked List
struct node {
    struct node * next;
    long val;
};

void addValue(struct node *list, long val) {
    struct node *n = list;

    if (n->val == -1) {
        n->val = val;
        return;
    }

    while (n->next) {
        n = n->next;
    }

    struct node *newNode = malloc(sizeof(struct node));
    newNode->val = val;
    newNode->next = NULL;
    n->next = newNode;
}

void freeLinkedList(struct node *list) {
    struct node *c = list;
    if (!c) return;
    struct node *n = c->next;
    free(c);
    freeLinkedList(n);
}

void printList(struct node *list) {
    struct node *n = list;
    printf("[");
    while (n) {
        printf("%ld", n->val);
        n = n->next;
        if (n) {
            printf(",");
        }
    }
    printf("]\n");
}
//-----------


int fac(int n) {
    if (n == 1) return 1;
    return fac(n-1)*n;
}

//Sieve of Eratosthenes
int sieve_primes(long limit, long **list) {
    struct timeval t1;
    struct timeval t2;
    double elapsedTime = 0;
    gettimeofday(&t1, NULL);

    assert(limit > 0);

    //Create a list of consecutive integers from 2 to n: (2, 3, 4, ..., n).
    long arrSize = limit-1;
    long *arr = malloc(sizeof(long)*arrSize);

    long c = 2;
    for (long i = 0; i < arrSize; i++) {
        arr[i] = c++;
    }   
    assert(arr[arrSize-1] == limit);


    for (long i = 0; i < arrSize; i++) {
        //Let p be equal to the first number not crossed
        long p = arr[i];    
        if (p == 0) continue;

        //Starting from p, count up in increments of p and mark each of these numbers greater than p itself in the list. 
        for (long f = p+p; f < arrSize; f+=p) {
            arr[f] = 0;
        }       
    }

    *list = arr;


    gettimeofday(&t2, NULL);

    elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0;      // sec to ms
    elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0;   // us to ms
    printf("Sieve of Eratosthenes took %f ms and used %lu mb of memory\n",elapsedTime, (arrSize * sizeof(int))/1024/1024);
    return arrSize;
}

void trial_division(struct node* list, long n) {    if (n == 1) {
        addValue(list, 1);
        return;
    }
    long *primes;
    long primesSize = sieve_primes(sqrt(n), &primes);   

    struct timeval t1;  
    struct timeval t2;
    double elapsedTime = 0;
    gettimeofday(&t1, NULL);
    for (long i = 0; i < primesSize; i++) {
        long p = primes[i];
        if (p == 0) continue;
        if (p*p > n) break;
        while (n % p == 0) {
            addValue(list, p);
            n/=p;
        }       
    }
    if (n > 1) {
        addValue(list, n);
    }
    free(primes);
}

int main(int argc, char *argv[]) {
    struct node *linkedList = malloc(sizeof(struct node));
    linkedList->val = -1;
    linkedList->next = NULL;


    long n = 11;
    long nF = fac(n);
    long nF2 = nF*nF;
    trial_division(linkedList, nF2);            

    long multOfAllPrimeFactors = 1;
    struct node *c = linkedList;
    while (c) {
        long sumOfVal = 2;
        long val = c->val;              
        c = c->next;
        while(c) {
            long val2 = c->val;
            if (val == val2) {
                sumOfVal++;
                c = c->next;
            } else break;           
        }
        multOfAllPrimeFactors*=sumOfVal;
    }       

    printf("n= %ld; n!^2 = %ld; d = %ld\n", n,nF2, multOfAllPrimeFactors);
    printList(linkedList);  

    freeLinkedList(linkedList);

}

EDIT:

As an example I will show you the calculation for getting all the possible positive integer solutions to the initial equation:

3!^2 = 36 = (3^2*2^2*1^0)

So there are (1+2)(1+2)(1+0)=9 possible positive integer solutions to the diophantine equation. Double if you count negative integers. I'm using WolframAlpha to be sure.

EDIT 2:

I think I just found out "what a factorial is", I'm getting this very interesting output:

3! = [2,3]
3!^2 = [2,2,3,3]
3!^3 = [2,2,2,3,3,3]
3!^4 = [2,2,2,2,3,3,3,3]

Thanks :D

The trick here is to recognize exactly what a factorial N! is. It's a product of all the numbers from 1 to N . Which is already a huge step forward.

So what you need to do, is just to prime factorize each of the numbers from 1 to N .

In this sense, you don't need to sieve up to N! . Instead, just sieve up to sqrt(N) . And the rest is just merging all your prime factors.

Even easier, you don't need to factor the numbers up to N. You just have to count prime factors. And that you can do without worrying about which numbers are factors.

Let me do 15 by hand.

Up to 15 there are 7 multiples of 2, 3 multiples of 4, and 1 multiple of 8, for a total of 11 factors of 2.

Up to 15 there are 5 multiples of 3, and one multiple of 9 for a total of 6 factors of 3.

Up to 15 there are 3 multiples of 5, for a total of 3 factors of 5.

Up to 15 there are 2 multiples of 7, for a total of 2 factors of 7.

There is 1 multiple each of 11 and 13.

So 15! = 2 11 * 3 6 * 5 3 * 7 2 * 11 * 13.

To find a prime factorization of N! you have to:

  1. For each prime p under N: find S=[N/p]+[N/p 2 ]+[N/p 3 ]+[N/p 4 ].... ([ ] - is a whole part of an argument). So, if we define division as a whole one, the formula is: S=N/p+N/p 2 +N/p 3 +N/p 4 ....
  2. This S is the number of p's in N! prime factorization
  3. And yes, if you need factorize N!^2, simply count the factorization of N! and double the powers of all primes in the result.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM