13

私は 10 進数 (それを goalと呼びましょう) と他の 10 進数の配列 (配列elementsと呼びましょう) を持っており、合計がゴールになる要素から数値のすべての組み合わせを見つける必要があります。

私は C# (.Net 2.0) でのソリューションを好みますが、最高のアルゴリズムが勝つ可能性があります。

メソッドの署名は次のようになります。

public decimal[][] Solve(decimal goal, decimal[] elements)
4

6 に答える 6

13

興味深い答え。ウィキペディアへのポインタをありがとう - 興味深いが - 私が正確な一致を探していたので、述べたように実際には問題を解決していない.

私は興味を持ってスタック オーバーフローの開発を追跡しており、それがどれほど役立つか疑問に思っていました。この問題は職場で発生し、スタック オーバーフローが既製の回答 (またはより良い回答) を自分で書くよりも早く提供できるかどうか疑問に思いました。また、これを宿題とタグ付けすることを示唆するコメントにも感謝します。上記を踏まえると、それはかなり正確だと思います。

興味のある方のために、ここに再帰を使用する私のソリューションを示します (当然のことです) メソッドのシグネチャについても考えを変え、戻り値の型として decimal[][] ではなく List> を使用しました。

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

アプリでこの動作をテストする場合は、次のコンソール アプリ コードを試してください。

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

これが他の誰かが(宿題であろうとなかろうと)より早く答えを得るのに役立つことを願っています.

乾杯...

于 2008-09-17T15:00:49.207 に答える
3

ビンパッキング問題(NP困難)があると思うので、うまくいくものが見つかるまで、考えられるすべての組み合わせを試すことが唯一の解決策になると思います。

編集:コメントで指摘されているように、出くわした数字のセットごとにすべての組み合わせを試す必要はありません。ただし、思いついた方法には、すべての組み合わせを試す必要ある最悪のシナリオの数値のセットがあります。少なくとも、セットのサイズに応じて指数関数的に増加する組み合わせのサブセットです。

そうでなければ、それはNP困難ではありません。

于 2008-09-17T14:05:03.053 に答える
3

サブセット合計問題と、もう少し一般的なナップザック問題は、動的計画法で解決されます。すべての組み合わせを力ずくで列挙する必要はありません。ウィキペディアまたはお気に入りのアルゴリズム リファレンスを参照してください。

問題はNP完全ですが、非常に「簡単な」NP完全です。要素数のアルゴリズムの複雑さは低いです。

于 2008-09-17T14:22:21.423 に答える
2

あなたはナップサック問題について説明しましたが、唯一の真の解決策はブルートフォースです。より高速な近似解がいくつかありますが、それらはニーズに合わない場合があります。

于 2008-09-17T14:05:21.820 に答える
2

ブルート フォースの問題を解決するわけではありませんが (他の人が既に述べたように)、最初に数値を並べ替えてから、残りの可能性のある数値を調べることができます (Sum 値を渡すと、Goal よりも大きい数値を追加することはできないため)。和)。

これにより、アルゴリズムの実装方法が変更されますが (一度だけ並べ替えてからマークされた要素をスキップするため)、平均してパフォーマンスが向上します。

于 2008-09-17T16:44:13.250 に答える
-1
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);
    }
}
于 2010-04-13T11:34:58.143 に答える