Algorytm znajdowania liczb z listy wielkości N sumy do innej liczby

Mam liczbę dziesiętną (nazwijmy ją bramka ) i tablicę innych liczb dziesiętnych (nazwijmy tablicę elementy ) i muszę znaleźć wszystkie kombinacje liczb z elementy które są sumą bramki.

Preferuję rozwiązanie w C# (. Net 2.0), ale niech wygra najlepszy algorytm.

Twój podpis metody może wyglądać mniej więcej tak:

public decimal[][] Solve(decimal goal, decimal[] elements)
Author: Jacob Schoen, 2008-09-17

6 answers

Ciekawe odpowiedzi. Dziękuję za wskazówki do Wikipedii - choć interesujące - nie rozwiązują one problemu tak jak to powiedziałem, jak Szukałem dokładnych dopasowań - bardziej problemu księgowości / księgowości niż tradycyjnego problemu bin-packing / knapsack.

Śledziłem rozwój stack overflow z zainteresowaniem i zastanawiałem się, jak przydatny będzie. Ten problem pojawił się w pracy i zastanawiałem się, czy stack overflow może dostarczyć gotową odpowiedź (lub lepsza odpowiedź) szybciej niż sam mógłbym to napisać. Dzięki również za komentarze sugerujące to być tagged homework-myślę, że jest to dość dokładne w świetle powyższego.

Dla tych, którzy są zainteresowani, oto moje rozwiązanie, które wykorzystuje rekurencję (naturalnie) zmieniłem również zdanie o metodzie signature i poszedłem do List> zamiast dziesiętnego [] [] jako typ powrotu:

public class Solver {

    private List<List<decimal>> mResults;

    public List<List<decimal>> Solve(decimal goal, decimal[] elements) {

        mResults = new List<List<decimal>>();
        RecursiveSolve(goal, 0.0m, 
            new List<decimal>(), new List<decimal>(elements), 0);
        return mResults; 
    }

    private void RecursiveSolve(decimal goal, decimal currentSum, 
        List<decimal> included, List<decimal> notIncluded, int startIndex) {

        for (int index = startIndex; index < notIncluded.Count; index++) {

            decimal nextValue = notIncluded[index];
            if (currentSum + nextValue == goal) {
                List<decimal> newResult = new List<decimal>(included);
                newResult.Add(nextValue);
                mResults.Add(newResult);
            }
            else if (currentSum + nextValue < goal) {
                List<decimal> nextIncluded = new List<decimal>(included);
                nextIncluded.Add(nextValue);
                List<decimal> nextNotIncluded = new List<decimal>(notIncluded);
                nextNotIncluded.Remove(nextValue);
                RecursiveSolve(goal, currentSum + nextValue,
                    nextIncluded, nextNotIncluded, startIndex++);
            }
        }
    }
}

Jeśli chcesz, aby aplikacja przetestowała to działanie, wypróbuj ten kod aplikacji konsoli:

class Program {
    static void Main(string[] args) {

        string input;
        decimal goal;
        decimal element;

        do {
            Console.WriteLine("Please enter the goal:");
            input = Console.ReadLine();
        }
        while (!decimal.TryParse(input, out goal));

        Console.WriteLine("Please enter the elements (separated by spaces)");
        input = Console.ReadLine();
        string[] elementsText = input.Split(' ');
        List<decimal> elementsList = new List<decimal>();
        foreach (string elementText in elementsText) {
            if (decimal.TryParse(elementText, out element)) {
                elementsList.Add(element);
            }
        }

        Solver solver = new Solver();
        List<List<decimal>> results = solver.Solve(goal, elementsList.ToArray());
        foreach(List<decimal> result in results) {
            foreach (decimal value in result) {
                Console.Write("{0}\t", value);
            }
            Console.WriteLine();
        }


        Console.ReadLine();
    }
}

Mam nadzieję, że to pomaga komuś innemu szybciej uzyskać odpowiedź (czy to do pracy domowej, czy w inny sposób).

Zdrówko...
 11
Author: Sam Meldrum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 15:00:49

Problem sumy podzbiorów i nieco bardziej ogólny problem plecaka są rozwiązywane za pomocą programowania dynamicznego: wyliczanie wszystkich kombinacji brute-force nie jest wymagane. Skonsultuj się z Wikipedią lub swoim ulubionym odniesieniem do algorytmów.

Chociaż problemy są np-kompletne, są one bardzo "łatwe" np-kompletne. Złożoność algorytmu w liczbie elementów jest niska.

 3
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 14:22:21

Myślę, że masz problem bin packing na rękach (co jest np-hard), więc myślę, że jedynym rozwiązaniem będzie wypróbowanie każdej możliwej kombinacji, dopóki nie znajdziesz takiej, która działa.

Edit: jak zaznaczono w komentarzu, nie będziesz Zawsze musiał próbować każdej kombinacji dla KAŻDEGO zestawu liczb, które napotkasz. Jednak każda metoda, którą wymyślisz, ma najgorszy scenariusz, w którym będziesz musiał próbować co kombinacja - lub przynajmniej podzbiór kombinacji, który rośnie wykładniczo wraz z wielkością zbioru.

Inaczej nie byłoby NP-trudne.

 3
Author: Rob Dickerson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 22:18:07
 2
Author: ARKBAN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 14:05:21

Nie rozwiązując problemu brute force (jak inni już wspomnieli) możesz najpierw posortować swoje liczby, a następnie przejść nad możliwymi w lewo (ponieważ po przekazaniu wartości sumy nie możesz dodać żadnej liczby większej niż suma celu).

To zmieni sposób implementacji algorytmu (aby posortować tylko raz, a następnie pominąć zaznaczone elementy), ale średnio poprawi wydajność.

 2
Author: Adi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-17 16:44:13
public class Logic1 {
    static int val = 121;
    public static void main(String[] args)
    {
        f(new int[] {1,4,5,17,16,100,100}, 0, 0, "{");
    }

    static void f(int[] numbers, int index, int sum, String output)
    {
        System.out.println(output + " } = " + sum);
        //System.out.println("Index value1 is "+index);
        check (sum);
        if (index == numbers.length)
        {
            System.out.println(output + " } = " + sum);
            return;
        }

        // include numbers[index]
        f(numbers, index + 1, sum + numbers[index], output + " " + numbers[index]);
        check (sum);
        //System.out.println("Index value2 is "+index);
        // exclude numbers[index]
        f(numbers, index + 1, sum, output);
        check (sum);
    }

    static void check (int sum1)
    {
        if (sum1 == val)
            System.exit(0);
    }
}
 -1
Author: guest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-31 16:29:21