Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 740
fbernardo Avatar asked Mar 01 '12 20:03

fbernardo


1 Answers

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.

like image 135
Mysticial Avatar answered Sep 30 '22 13:09

Mysticial