Algorithm Optimization (Prime Factorization)
Asked Answered
L

3

7

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

Lonnylonslesaunier answered 1/3, 2012 at 20:11 Comment(5)
You need to get out more if it not homework!Easterling
You know the prime factorization of (N!)² is just the prime factorization of N!, and then square everything? (and 121 == 11²).Lilith
@EdHeal I was just thinking someone would said that as I typed the phrase :)Lonnylonslesaunier
Why in gods name would you use trial division to factorize (N!)^2 ??? Simply factor each of the numbers 1 through N.Easternmost
@woodchips thanks for the tip but now I got it, seems stupid now.Lonnylonslesaunier
C
11

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.

Cavender answered 1/3, 2012 at 20:14 Comment(3)
Very interesting, let me think for a while.Lonnylonslesaunier
I get you, I don't need to calculate the the prime factorization of a factorial I can just calculate it for each member and concatenate the result. But I still need to calculate the prime factorization of N!^2, it's not the same. I will update an example to show you what I mean.Lonnylonslesaunier
Once you have the prime factors for N!, just multiply all the powers by 2 and you get N!^2.Cavender
E
7

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! = 211 * 36 * 53 * 72 * 11 * 13.

Eyecatching answered 1/3, 2012 at 21:32 Comment(0)
W
5

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

  1. For each prime p under N: find S=[N/p]+[N/p2]+[N/p3]+[N/p4].... ([ ] - is a whole part of an argument). So, if we define division as a whole one, the formula is: S=N/p+N/p2+N/p3+N/p4....
  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.
Weinrich answered 1/3, 2012 at 23:9 Comment(8)
it's probably best if you explain that your notation [N/p] is for integer division.Reproval
@WillNess It is a a standard in mathematics, isn't it? But thank you, you are right.Weinrich
not everyone knows this, and it's written differently anyway (not present in ASCII), right? BTW integer division is usually (sometimes?) written a // b.Reproval
@WillNess On the contrary, it is exact what we had at school and university, too. But not everybody here had a maths specialized education. And math notation standards are not universal.Weinrich
Interesting. I didn't know that. (I meant the bracket without the upper plank). :)Reproval
@WillNess I didn't know that, too, for a long time, and am forgetting it constantly. People tend to sincerely take their mini piece of knowledge for the universal standard. And as for math - take the names of numbers. A trillion in different countries is different numbers.Weinrich
@WillNess It is the Floor() function, why the upper plank? It was stuffed in our heads by our school teachers as absolute knowledge and the stuff was merely the heap of inventions of the schoolbooks authors.Weinrich
let us continue this discussion in chatWeinrich

© 2022 - 2024 — McMap. All rights reserved.