Slightly A.
Slightly A.

Reputation: 2875

Out of Memory error finding products and primes

So I wrote a quick app to check my son's homework and it appears to be working fine. It takes a number, finds all the primes, then finds all the product combinations. So, for example, you pass it "210", and it will return:
2 x 3 x 5 x 7
5 x 6 x 7
7 x 30
6 x 35
5 x 42
3 x 7 x 10
10 x 21
3 x 70
3 x 5 x 14
14 x 15
2 x 7 x 15
2 x 105
2 x 5 x 21
2 x 3 x 35

The problem I'm having is when doing large numbers. It will handle 256 (2^8) after only a moment's hesitation, but when I do 512 (2^9), I get a OutOfMemoryException. Can someone recommend a better way of finding the product sets? My code is below.

class Program
{
    private static List<List<int>> coll = new List<List<int>>();

    public static List<int> PrimeFactors(int i)
    {
        if (i < 2)
        {
            throw new ArgumentOutOfRangeException("Numbers less than 2 don't have prime factors");
        }

        List<int> result = new List<int>();

        int divisor = 2;

        while (divisor <= i)
        {
            if (i % divisor == 0)
            {
                result.Add(divisor);
                i /= divisor;
            }
            else
            {
                divisor++;
            }
        }

        return result;
    }

    private static void GetFactors(List<int> l)
    {
        for (int i = 0; i < l.Count - 1; i++)
        {
            for (int x = i + 1; x < l.Count; x++)
            {
                List<int> loopList = new List<int>(l);
                int newProd = l[i] * l[x];
                loopList.RemoveAt(i);
                loopList.RemoveAt(x-1);
                loopList.Add(newProd);
                loopList.Sort();
                coll.Add(loopList);
                if (loopList.Count > 2)
                {
                    GetFactors(loopList);
                }
            }
        }
    }

    static void Main(string[] args)
    {
        int target = Convert.ToInt16(args[0]);
        List<int> results = PrimeFactors(target);
        results.Sort();
        coll.Add(results);
        GetFactors(results);
        List<string> factors = new List<string>();
        foreach (List<int> lst in coll)
        {
            string listString = "";
            for (int i = 0; i < lst.Count; i++)
            {
                if (i == lst.Count - 1)
                {
                    listString += String.Format("{0}", lst[i]);
                }
                else
                {
                    listString += String.Format("{0} x ", lst[i]);
                }
            }
            factors.Add(listString);
        }

        foreach (String factorString in factors.Select(x => x).Distinct())
        {
            Console.WriteLine(factorString);
        }
    }
}

EDIT: Here is the new code, based on answers below. Works for any Int16 I throw at it.

    class Program
{
    private static List<List<int>> coll = new List<List<int>>();

    public static List<int> PrimeFactors(int i)
    {
        if (i < 2)
        {
            throw new ArgumentOutOfRangeException("Numbers less than 2 don't have prime factors");
        }

        List<int> result = new List<int>();

        int divisor = 2;

        while (divisor <= i)
        {
            if (i % divisor == 0)
            {
                result.Add(divisor);
                i /= divisor;
            }
            else
            {
                divisor++;
            }
        }

        return result;
    }

    private static void GetFactors(List<int> l)
    {
        for (int i = 0; i < l.Count - 1; i++)
        {
            for (int x = i + 1; x < l.Count; x++)
            {
                List<int> loopList = new List<int>(l);
                int newProd = l[i] * l[x];
                loopList.RemoveAt(i);
                loopList.RemoveAt(x-1);
                loopList.Add(newProd);
                bool existsInCollection = false;
                foreach (List<int> existingList in coll)
                {
                    if (ListEquality(existingList, loopList))
                    {
                        existsInCollection = true;
                        break;
                    }
                }
                if (!existsInCollection)
                {
                    coll.Add(loopList);
                    if (loopList.Count > 2)
                    {
                        GetFactors(loopList);
                    }
                }
            }
        }
    }

    private static bool ListEquality(List<int> listA, List<int> listB)
    {
        if (listA.Count != listB.Count)
            return false;

        listA.Sort();
        listB.Sort();

        for (int idx = 0; idx < listA.Count; idx++)
        {
            if (listA[idx] != listB[idx])
                return false;
        }
        return true;
    }

    static void Main(string[] args)
    {
        int target = Convert.ToInt16(args[0]);
        List<int> results = PrimeFactors(target);
        results.Sort();
        coll.Add(results);
        GetFactors(results);
        foreach (List<int> lst in coll)
        {
            string listString = "";
            for (int i = 0; i < lst.Count; i++)
            {
                if (i == lst.Count - 1)
                {
                    listString += String.Format("{0}", lst[i]);
                }
                else
                {
                    listString += String.Format("{0} x ", lst[i]);
                }
            }
            Console.WriteLine(listString);
        }
    }
}

Thanks again to everyone.

Upvotes: 1

Views: 224

Answers (1)

Hogan
Hogan

Reputation: 70529

There are so many things that could be better with this program.

Here are a few:

  • Don't use a global (coll)
  • Don't use bad variable names (i,l,x)
  • Don't use List<int> when int [] would work as well or better.
  • Use join to print out your list.

Some things that would make this program faster

  • Don't recurse, iterate. (Hint - an abstract stack object or abstract set object would help.)
  • Don't sort so much, should not matter and in fact might slow you down.
  • Prune your temporary storage as you go -- waiting till the end to do the distinct just means you have done WAY TO MANY of the same thing multiple times. Before you add it to the collection check if it is already there.

Upvotes: 2

Related Questions