私が持っているとしましょう{3, 1, 1, 2, 2, 1, 5, 2, 7}
数値のセット。サブセット1の合計がサブセット2の合計と等しくなるように数値を分割する必要があります{3,2,7} {1,1,2,1,5,2}
。最初に、数値を分割できるかどうかを確認する必要があります(一方通行は、余りなく2で割り切れる可能性があります)。可能であれば、アルゴリズム2を作成して、sからs1とs2を作成する必要があります。
このアプローチをどのように進めるか? wikiや一部の記事でパーティションの問題を読みましたが、何も取得できません。誰かが適切なアルゴリズムとその説明を簡単な英語で見つけるのを手伝ってくれませんか?
@wmeyerが気づいたように、この問題は制約プログラミングを使用して非常にうまく表現できます。そして、この単純な問題のインスタンスは簡単に解決されます。
まず、これは MiniZinc で記述された高レベルモデルで、基本的にはこのコードですが、出力セクションはありません。 (完全なモデルについては http://www.hakank.org/minizinc/partition_into_subset_of_equal_values.mzn を参照してください。)
%問題インスタンスとその長さ 配列[1..n] int:s = [3、1、1、2、2、1、5、2、7]; int:n = 9; %サブセット数 int:num_subsets = 2; %決定変数 %x [i]が属するサブセット? array [1..n] of var 1..num_subsets:x; solve meet; %すべてのソリューションが必要 %制約 %サブセットの合計が同じであることを確認します 制約 forall (p in 1..num_subsets-1)( sum([s [i] * bool2int(x [i] == p)| i in 1..n])== sum([s [i] * bool2int(x [i] == p + 1)| i in 1..n]) ) ; %対称性の破れ:最初の数値をサブセット1に割り当てます 制約x [1] = 1;
質問には「C#」というタグが付けられているので、ここに Google or-tools のC#インターフェースを使用したモデルがあります(MiniZincモデルとは少し異なるアプローチ): http://www.hakank .org/google_or_tools/partition_into_subsets_of_equal_values.cs
注:指定された数値について-最初の数値をサブセット1に割り当てるという対称性の破れを使用して-2つのサブセットを持つ19の異なるパーティション(ソリューション)があり、3つのサブセットを使用する場合は42の異なるパーティションがあります。 4サブセットの場合、解決策はありません。
誰かがコメントしたように、この問題は「サブセット和」と同型であるため、NP完全です。基本的に、この問題の素朴な解決策は最良の解決策であり、非常に悪いものです。 :)私はこれだけを言っているので、より「最適化された」バージョンなどを探す必要はありません。
using System;
using System.Linq;
public class CandidateCode
{
public static string partition(int[] input1)
{
bool[] best_assignment = PartitionValues(input1);
string result1 = "", result2 = "";
int total1 = 0, total2 = 0;
for (int i = 0; i < best_assignment.Length; i++)
{
if (best_assignment[i])
{
result1 += "\r\n " + input1[i];
total1 += input1[i];
}
else
{
result2 += "\r\n " + input1[i];
total2 += input1[i];
}
}
if (result1.Length > 0) result1 = result1.Substring(2);
if (result2.Length > 0) result2 = result2.Substring(2);
return "{"+ result1 + " } {" + result2 + " } total " + total1.ToString() + " & " + total2.ToString();
}
private static bool[] PartitionValues(int[] values)
{
bool[] best_assignment = new bool[values.Length];
bool[] test_assignment = new bool[values.Length];
int total_value = values.Sum();
int best_err = total_value;
PartitionValuesFromIndex(values, 0, total_value, test_assignment, 0, ref best_assignment, ref best_err);
return best_assignment;
}
private static void PartitionValuesFromIndex(int[] values, int start_index, int total_value,
bool[] test_assignment, int test_value,
ref bool[] best_assignment, ref int best_err)
{
// If start_index is beyond the end of the array,
// then all entries have been assigned.
if (start_index >= values.Length)
{
// We're done. See if this assignment is better than what we have so far.
int test_err = Math.Abs(2 * test_value - total_value);
if (test_err < best_err)
{
// This is an improvement. Save it.
best_err = test_err;
best_assignment = (bool[])test_assignment.Clone();
}
}
else
{
// Try adding values[start_index] to set 1.
test_assignment[start_index] = true;
PartitionValuesFromIndex(values, start_index + 1, total_value,
test_assignment, test_value + values[start_index],
ref best_assignment, ref best_err);
// Try adding values[start_index] to set 2.
test_assignment[start_index] = false;
PartitionValuesFromIndex(values, start_index + 1, total_value,
test_assignment, test_value,
ref best_assignment, ref best_err);
}
}
}