简体   繁体   中英

How can I determine if a certain number can be made up from a set of numbers?

So I have an integer, eg 1234567890, and a given set of numbers, eg {4, 7, 18, 32, 57, 68}

The question is whether 1234567890 can be made up from the numbers given (you can use a number more than once, and you don't have to use all of them). In the case above, one solution is:
38580246 * 32 + 1 * 18

(Doesn't need to give specific solution, only if it can be done)

My idea would be to try all solutions. For example I would try
1 * 4 * + 0 * 7 + 0 * 18 + 0 * 32 + 0 * 57 + 0 * 68 = 4
2 * 4 * + 0 * 7 + 0 * 18 + 0 * 32 + 0 * 57 + 0 * 68 = 8
3 * 4 * + 0 * 7 + 0 * 18 + 0 * 32 + 0 * 57 + 0 * 68 = 12
.....
308 641 972 * 4 * + 0 * 7 + 0 * 18 + 0 * 32 + 0 * 57 + 0 * 68 = 1234567888
308 641 973 * 4 * + 0 * 7 + 0 * 18 + 0 * 32 + 0 * 57 + 0 * 68 = 1234567892 ==> exceeds
0 * 4 * + 1 * 7 + 0 * 18 + 0 * 32 + 0 * 57 + 0 * 68 = 7
1 * 4 * + 1 * 7 + 0 * 18 + 0 * 32 + 0 * 57 + 0 * 68 = 11
2 * 4 * + 1 * 7 + 0 * 18 + 0 * 32 + 0 * 57 + 0 * 68 = 15
and so on...

Here is my code in c#:

    static int toCreate = 1234567890;
    static int[] numbers = new int[6] { 4, 7, 18, 32, 57, 68};
    static int[] multiplier;
    static bool createable = false;

    static void Main(string[] args)
    {
        multiplier = new int[numbers.Length];
        for (int i = 0; i < multiplier.Length; i++)
            multiplier[i] = 0;

        if (Solve())
        {
            Console.WriteLine(1);
        }
        else
        {
            Console.WriteLine(0);
        }
    }

    static bool Solve()
    {
        int lastIndex = 0;
        while (true)
        {
            int comp = compare(multiplier);
            if (comp == 0)
            {
                return true;
            }
            else if (comp < 0)
            {
                lastIndex = 0;
                multiplier[multiplier.Length - 1]++;
            }
            else
            {
                lastIndex++;
                for (int i = 0; i < lastIndex; i++)
                {
                    multiplier[multiplier.Length - 1 - i] = 0;
                }
                if (lastIndex >= multiplier.Length)
                {
                    return false;
                }
                multiplier[multiplier.Length - 1 - lastIndex]++;
            }
        }
    }

    static int compare(int[] multi)
    {
        int osszeg = 0;
        for (int i = 0; i < multi.Length; i++)
        {
            osszeg += multi[i] * numbers[i];
        }
        if (osszeg == toCreate)
        {
            return 0;
        }
        else if (osszeg < toCreate)
        {
            return -1;
        }
        else
        {
            return 1;
        }
    }

The code works fine (as far as I know) but is way too slow. It takes about 3 secs to solve the example, and there may be 10000 numbers to make from 100 numbers.

I have a recursive solution. It solves the OP's original problem in about .005 seconds (on my machine) and tells you the calculations.

private static readonly int Target = 1234567890;
private static readonly List<int> Parts = new List<int> { 4, 7, 18, 32, 57, 68 };

static void Main(string[] args)
{
    Console.WriteLine(Solve(Target, Parts));
    Console.ReadLine();
}

private static bool Solve(int target, List<int> parts)
{
    parts.RemoveAll(x => x > target || x <= 0);
    if (parts.Count == 0) return false;

    var divisor = parts.First();
    var quotient = target / divisor;
    var modulus = target % divisor;

    if (modulus == 0)
    {
        Console.WriteLine("{0} X {1}", quotient, divisor);
        return true;
    }

    if (quotient == 0 || parts.Count == 1) return false;

    while (!Solve(target - divisor * quotient, parts.Skip(1).ToList()))
    {
        if (--quotient != 0) continue;
        return Solve(target, parts.Skip(1).ToList());
    }

    Console.WriteLine("{0} X {1}", quotient, divisor);
    return true;
}

Basically, it goes through each number to see if there is a possible solution "below" it given the current quotient and number. If there isn't, it subtracts 1 from the quotient and tries again. It does this until it exhausts all options for that number and then moves on to the next number if available. If all numbers are exhausted, there is no solution.

Don't have the means test the solution, but the following should do.

Given a target number target and a set numbers of valid numbers:

bool FindDecomposition(int target, IEnumerable<int> numbers, Queue<int> decomposition)
{
    foreach (var i in numbers)
    {
        var remainder = target % i;

        if (remainder == 0)
        {
             decomposition.Enqueue(i);
             return true;
        } 

        if (FindDecomposition(remainder, numbers.Where(n => n < i), decomposition))
        {
             return true;
        }
    }

    return false
}

Building up n from decomposition is pretty straightforward.

You could always try using the modulo function in conjunction with LINQ expressions to solve the problem.

You would have a list and a running modulo variable to keep track of where you are at in your iteration. Then simply use recursion to determine whether or not you have meet the conditions.

One example would be the following:

static int toCreate = 1234567890;
    static List<int> numbers = new List<int> { 4, 7 };


    static void Main(string[] args)
    {
        numbers.Sort();
        numbers.Reverse();

        Console.WriteLine(Solve(numbers,toCreate).ToString());
    }

    static bool Solve(List<int> lst1, int runningModulo)
    {
        if (lst1.Count == 0 && runningModulo != 0) 
            return false;
        if (lst1.Count == 0 || runningModulo == 0)
            return true;

        return numbers.Any(o => o < (toCreate % lst1.First())) ? //Are there any in the remaining list that are smaller in value than the runningModulo mod the first element in the list.
            Solve(lst1.Where(o => o != lst1.First()).ToList(), runningModulo % lst1.First()) //If yes, then remove the first element and set the running modulo = to your new modulo
            : Solve(lst1.Where(o => o != lst1.First()).ToList(), toCreate); //Otherwise, set the running modulo back to the original toCreate value.
    }

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