Как я могу определить, может ли определенное число быть составлено из набора чисел?

Итак, у меня есть целое число, например, 1234567890, и данный набор цифр, например,{4, 7, 18, 32, 57, 68}

вопрос в том, может ли 1234567890 быть составлен из заданных чисел (вы можете использовать число более одного раза, и вам не нужно использовать все из них). В приведенном выше случае, одним из решений является:
38580246 * 32 + 1 * 18

(Не нужно давать конкретное решение, только если это можно сделать)

моей идеей было бы попробовать все решения. Например, я бы попробовал
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 ==> превышает
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
и так далее...

вот мой код на 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;
        }
    }

этот код работает нормально (насколько я знаю), но слишком медленно. Это занимает около 3 секунд, чтобы решить пример, и может быть 10000 чисел, чтобы сделать из 100 чисел.

3 ответов


У меня есть рекурсивное решение. Это решает оригинальную проблему OP примерно .005 секунд (на моей машине) и говорит вам расчеты.

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;
}

в основном, он проходит через каждое число, чтобы увидеть, есть ли возможное решение "ниже", учитывая текущее частное и число. Если нет, он вычитает 1 из частного и повторяет попытку. Он делает это до тех пор, пока не исчерпает все варианты для этого числа, а затем переходит к следующему номеру, если он доступен. Если все числа исчерпаны, решения нет.


не имеют средств проверить решение, но следующее должно сделать.

учитывая целевой номер target и набор 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
}

до здания n С decomposition - это довольно просто.


вы всегда можете попробовать использовать функцию по модулю в сочетании с выражениями LINQ для решения проблемы.

У вас будет список и работающая переменная по модулю, чтобы отслеживать, где вы находитесь в своей итерации. Затем просто используйте рекурсию, чтобы определить, соответствуете ли вы условиям.

одним из примеров может быть следующее:

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.
    }