2

(ps。順列を扱っていると思ったので質問を書き直しましたが、実際には組み合わせを扱っています。)

をより具体的に考えてみましょうMap<String, List<WordGroupAndScore> baseMap:

private static class WordGroupAndScore {
    public final WordGroup wordGroup;
    public final int score;

    public WordGroupAndScore(final WordGroup wordGroup, final int score) {
        this.wordGroup = wordGroup;
        this.score = score;
    }
}

は変数です。つまり、マップ内にbaseMap.size()任意の数の が存在する可能性があります。Stringまた、 のすべての要素についてbaseMapbaseMap.get(i).size()は可変です。ただし、baseMapに空のリストを含めることはできません。

今、私はすべての可能な組み合わせを見つけようとしています。コード自体は請求書のデータをチェックするためのものであり、すべてのデータが請求書で利用できるとは限らないため、可変量のbaseMap.size(). baseMapまた、検出されるデータの量はそれがどの請求書であるかによって異なるため、要素ごとのリストは可変です。

(例のデータは実際には 1 対 1 で対応していませんが、例ではs またはs をWordGroupAndScore使用してデータを表します)StringBigDecimal

baseMap(値とキーのペア) 厳密に (Aと のペア)のデータ例List<B>:

  • ("invoiceNumber", ["0001", "0002"])
  • ("invoiceDate", ["2013-10-07"])
  • ("priceExclVAT, [new BigDecimal("10.00")])
  • ("highVAT, [new BigDecimal("2.10")])
  • ("priceInclVAT, [new BigDecimal("12.10"), new BigDecimal("14.10")])

データの可能なすべての組み合わせを生成したい。

出力例、1 つの (「最初の」) 組み合わせ (値と単一のキー ペア) 厳密 (ABペア):

  • ("invoiceNumber", "0001")
  • ("invoiceDate", "2013-10-07"])
  • ("priceExclVAT, new BigDecimal("10.00"))
  • ("highVAT, new BigDecimal("2.10"))
  • ("priceInclVAT, new BigDecimal("12.10"))

出力例、1 つの (「最後の」) 組み合わせ (値と単一のキー ペア) 厳密 (ABペア):

  • ("invoiceNumber", "0002")
  • ("invoiceDate", "2013-10-07")
  • ("priceExclVAT, new BigDecimal("10.00"))
  • ("highVAT, new BigDecimal("2.10"))
  • ("priceInclVAT, new BigDecimal("14.10"))

したがって、どういうわけか、すべてを反復処理し、baseMapevery に基づいてすべての組み合わせを記憶/作成する必要がありますbaseMap.get(i).size()が、どこから始めればよいかほとんどわかりません。baseMap最大の問題は、私のサイズが可変であるため、組み合わせをどのように覚えているかということです。変数がなければ、もっと簡単にできたのに。

質問が十分に明確であることを願っています。

編集:私の試みの1つを追加しましたが、うまくいきません。

//Assumes that wordGroupsAndScores does not get changed during the process
private void processWordGroupAndScores(TemplateBean template) {
    System.out.println();
    System.out.println("--wordGroupsAndScores--");
    for (Map.Entry<String, List<WordGroupAndScore>> entry : wordGroupsAndScores.entrySet()) {
        System.out.println("Attribute = " + entry.getKey());
        for (WordGroupAndScore wordGroupAndScore : entry.getValue()) {
            System.out.println("WordGroupAndScore = " + wordGroupAndScore);
        }
        System.out.println(";");
    }
    System.out.println();
    //create all possible unfinishedinvoices from wordgroupandscores
    int[] indices = new int[wordGroupsAndScores.keySet().size()];
    for (int index = 0; index < indices.length; index++) {
        indices[index] = 0;
    }
    String[] keyLocation = new String[wordGroupsAndScores.keySet().size()];
    int j = 0;
    for (String key : wordGroupsAndScores.keySet()) {
        keyLocation[j] = key;
        j++;
    }
    processWordGroupAndScoresRecursive(indices, keyLocation, template);
}

private void processWordGroupAndScoresRecursive(int[] indices, String[] keyLocation, TemplateBean template) {
    processWordGroupAndScoresWithIndices(indices, keyLocation, template);
    boolean changedIndices = false;
    for (int index = indices.length - 1; index >= 0; index--) {
        if (indices[index] < wordGroupsAndScores.get(keyLocation[index]).size() - 1) {
            indices[index]++;
            changedIndices = true;
            break;
        }
    }
    if (changedIndices) {
        processWordGroupAndScoresRecursive(indices, keyLocation, template);
    }
}

private void processWordGroupAndScoresWithIndices(int[] indices, String[] keyLocation, TemplateBean template) {
    System.out.println();
    System.out.println("--Generated combination--");
    UnfinishedInvoice unfinishedInvoice = new UnfinishedInvoice();
    for (int index = 0; index < indices.length; index++) {
        String key = keyLocation[index];
        WordGroupAndScore wordGroupAndScore = wordGroupsAndScores.get(key).get(indices[index]);
        System.out.println("Attribute = " + key);
        System.out.println("WordGroupAndScore = " + wordGroupAndScore);
        System.out.println(";");
        setUnfinishedInvoiceAttribute(key, unfinishedInvoice, Utils.joinWordGroup(wordGroupAndScore.wordGroup, " "), wordGroupAndScore.score);
    }
    System.out.println();
    unfinishedInvoice.verify();
    if (templateMap.containsKey(template)) {
        templateMap.get(template).add(unfinishedInvoice);
    }
    else {
        List<UnfinishedInvoice> list = new ArrayList<>();
        list.add(unfinishedInvoice);
        templateMap.put(template, list);
    }
}

それが生成するものをより明確に見てみましょう。実際のデータではなく、インデックスのみを操作してみましょう。

これが入力であるとしましょう: [1, 1, 2, 1, 0]. リストとしてのマップの特徴付けであり、元のマップ内のリスト内の要素のインデックスを要素として使用します。マップの最後の要素が取得される組み合わせから始めます。

私の失敗したコードでは、出力として次のようになります。

  • [1, 1, 2, 1, 0]
  • [1, 1, 2, 0, 0]
  • [1, 1, 1, 0, 0]
  • [1, 1, 0, 0, 0]
  • [1, 0, 0, 0, 0]
  • [0, 0, 0, 0, 0]

が欠落しているなど、多くの値[0, 0, 0, 1, 0]が欠落しているため、これは正しくありません。

ここで何がうまくいかないのですか?

4

4 に答える 4

1

それらのすべてのサイズが 3 であると仮定しましょう (説明のために)。

次に、2 番目の要素に対して出力する必要があるもののインデックスは次のようになります。

00000
10000
20000
01000
11000
21000
02000
...

ここまでで、私たちが実際に数えているだけであることを理解していただけたと思います (正確には 3 進法で)。

したがって、基数 3 ではなく、各要素をそれぞれの制限までインクリメントする必要があります。

コードをシンプルにするために、aString[][]ではなく aを使用しましたMap<A, List<B>>(各行の最初の要素はに対応しAます - 私はあなたと同じデータを使用したので、簡単に解読できるはずです)。

// some hard-coded data
static String[][] strArr = {{"invoiceNumber", "0001", "0002"},
                            {"invoiceDate", "2013-10-07"},
                            {"priceExclVAT", "10.00"},
                            {"highVAT", "2.10"},
                            {"priceInclVAT", "12.10", "14.10"}};
static int[] indices = new int[strArr.length];

static boolean increment(int index)
{
   // when we can simply increase the current element
   if (indices[index] < strArr[index].length-2)
   {
      indices[index]++;
      return true;
   }
   // when we need to reset this element to 0 and increase the next element
   else
   {
      if (index == strArr.length-1)
         // we reached the end of the last list, so we're done
         return false;
      indices[index] = 0;
      return increment(index+1);
   }
}

static void print()
{
   System.out.println(Arrays.toString(indices));
   for (int i = 0; i < strArr.length; i++)
      System.out.println(strArr[i][0] + ", " + strArr[i][indices[i]+1]);
   System.out.println();
}

public static void main(String[] args)
{
   // simply repeatedly print the output, then increment
   do
   {
      print();
   }
   while (increment(0));
}
于 2013-10-07T14:04:09.003 に答える
1

以下の Clojure コードは、堅牢で高速かつ機能的な方法で、求められていることを解決します。

(defn combinations* [acc pairs]
  (if-let [[my-key my-vals] (first pairs)]
    (mapcat
      (fn [my-val]
        (combinations*
          (for [m acc] (assoc m my-key my-val))
          (rest pairs)))
      my-vals)
    acc))

(defn combinations [map]
  (combinations* [{}] (vec map)))

上記のコードは再帰的なソリューションです。平易な英語で行うことは次のとおりです。 combinations*可能なベース マップリストとキーと複数値のペアのリストを指定すると、キーと値を入力ベース マップに関連付ける可能なすべての組み合わせを返す関数です。これは再帰的に行われます。キーと複数値のペアのリストが空の場合、ベース マップには何も関連付けず、変更せずに返します。それ以外の場合、ペアがある場合は、最初のキーと複数値のペアを取得し、その中のすべての値とすべてのベース マップを取得します。入力として与えられ、それらのキー値をベースマップに追加する方法をすべて組み合わせて作成します。combinations*この変更されたベース マップの組み合わせのリストは、残りのキーと複数値のペアを 2 番目のパラメーターとして使用して、 を再帰的に呼び出すための新しいベース マップ リストとして使用されます。キーと複数値のペアがなくなるまで、ベース マップを組み合わせて変更するこの再帰を行います。その時点で、前述のように、変更されていないベース マップをソリューションとして返し、それらを再帰の他のブランチからのソリューションと連結します。問題を解決するために関数を初期化するには、空のマップのシングルトン リストをベース マップとして使用する必要があります。combinations関数。その唯一のパラメーターはマルチマップであり、これをキーと複数値のペアのベクトルに分割して呼び出しますcombinations*

これを呼び出す方法は次のとおりです。

(combinations {"invoiceNumber" ["0001" "0002"]
               "invoiceDate" ["2013-10-07"]
               "priceExclVAT" [10.00M]
               "highVAT" [2.10M]
               "priceInclVAT" [12.10M 14.10M]})

これは出力です:

({"invoiceDate" "2013-10-07",
  "invoiceNumber" "0001",
  "highVAT" 2.10M,
  "priceExclVAT" 10.00M,
  "priceVAT" 12.10M}
 {"invoiceDate" "2013-10-07",
  "invoiceNumber" "0002",
  "highVAT" 2.10M,
  "priceExclVAT" 10.00M,
  "priceVAT" 12.10M}
 {"invoiceDate" "2013-10-07",
  "invoiceNumber" "0001",
  "highVAT" 2.10M,
  "priceExclVAT" 10.00M,
  "priceVAT" 14.10M}
 {"invoiceDate" "2013-10-07",
  "invoiceNumber" "0002",
  "highVAT" 2.10M,
  "priceExclVAT" 10.00M,
  "priceVAT" 14.10M})

これを Java に変換してみるか、Clojure の依存関係を組み込み、Java クラス生成ディレクティブを追加して、ここで説明したように Java コードから直接呼び出してください。Clojure 環境をローカルにセットアップする手間をかけずに、上記のコードをここでテストすることもできます。

アップデート

議論とアイデアの把握のために、Java 化されたバージョンをすぐに追加する予定です。

更新 2

ほらね。

private static List<HashMap<String, Object>> associateInAll(
        List<HashMap<String, Object>> orig, String key, Object val) {

    LinkedList<HashMap<String, Object>> result =
            new LinkedList<HashMap<String, Object>>();

    for (HashMap<String, Object> m : orig) {
        HashMap<String, Object> mCopy = new HashMap<String, Object>(m);
        mCopy.put(key, val);
        result.add(mCopy);
    }

    return result;
}

private static List<HashMap<String, Object>> combinations2(
        List<HashMap<String, Object>> acc,
        List<Entry<String, List<Object>>> pairs) {

    if (!pairs.isEmpty()) {

        Entry<String, List<Object>> first = pairs.get(0);
        String myKey = first.getKey();
        List<Object> myVals = first.getValue();

        LinkedList<Entry<String, List<Object>>> rest =
                new LinkedList<Entry<String, List<Object>>>(pairs);

        rest.removeFirst();

        LinkedList<HashMap<String, Object>> results =
                new LinkedList<HashMap<String, Object>>();

        for (Object myVal : myVals) {

            List<HashMap<String, Object>> newBaseMaps =
                    associateInAll(acc, myKey, myVal);

            List<HashMap<String, Object>> subcombinations =
                    combinations2(newBaseMaps, rest);

            results.addAll(subcombinations);
        }

        return results;
    }

    return acc;
}

private static List<HashMap<String, Object>> combinations(
        HashMap<String, List<Object>> map) {

    LinkedList<HashMap<String, Object>> baseMaps =
            new LinkedList<HashMap<String, Object>>();

    baseMaps.add(new HashMap<String, Object>());

    LinkedList<Entry<String, List<Object>>> pairs =
            new LinkedList<Entry<String, List<Object>>>(map.entrySet());

    return combinations2(baseMaps, pairs);
}

public static void main(String... args) {

    HashMap<String, List<Object>> input =
            new HashMap<String, List<Object>>();

    input.put("invoiceNumber",
            Arrays.<Object>asList("0001", "0002", "0003"));
    input.put("invoiceDate",
            Arrays.<Object>asList("2013-10-07"));
    input.put("priceExclVAT",
            Arrays.<Object> asList(new BigDecimal("10.00")));
    input.put("highVAT",
            Arrays.<Object>asList(new BigDecimal("2.10")));
    input.put("priceInclVAT",
            Arrays.<Object>asList(new BigDecimal("12.10"), new BigDecimal("14.10")));

    List<HashMap<String, Object>> results = combinations(input);

    for (HashMap<String, Object> combination : results) {
        System.out.println("=============================");
        for (Entry<String, Object> entry : combination.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}

「欲しいものはいつも手に入るとは限らない」という言葉があります。今、あなたはそれを手に入れましたが、それはあなたが必要としているものではないと言っておきます. このコードは、Clojure のバージョンとは比較になりません。エレガンス、パフォーマンス、再利用性が著しく損なわれています。怠惰やストリーミング性、永続的なデータ構造による最適化、構成可能性などはありません...そして非常に長くて冗長です! 書き終わる頃には、最初のことを忘れていました。

HTH。

于 2013-10-07T10:55:44.320 に答える
1

再帰関数を使用した疑似コードのサンプル。再帰の各レベルは、すべての要素を 1 つずつ取得して 1 つのリストを処理し、それらを出力変数に入れ、それ自体を再帰的に呼び出して次の反復レベルを処理します。

void allCombinations(Map<A, List<B>> input, Map<A, B> output){
   if (input not empty){
      (x, Y) = input.removeOneElement(); //removes one list from the input
      for each b in Y{
        output.insert(x, b);             //adds the element to the output
        allCombinations(input, output);  //recursively calls itself
        output.remove(x, b);             //removes the element from the output
      }
   }else{
      print(output)                      //here i print the output
   }
}

したがって、これは再帰を使用して sizeof(input) ネストされたループを効果的に作成します。

次を使用して呼び出します。

allCombinations(input, new Map<A, B>());

注:出力を印刷する代わりに、それを返したい場合。次に、メソッドのシグネチャを変更します。

void allCombinations(Map<A, List<B>> input, Map<A, B> output, List<Map<A,B>> result)
...
result.add(output); //instead of print(output);

次を使用して呼び出します。

List<Map<A,B>> result = new List<Map<A,B>>();
allCombinations(input, new Map<A, B>(), result);
于 2013-10-07T09:41:59.157 に答える