請注意,這是必需的C#.NET 2.0項目(LINQ的不允許)。找到所有的k大小的子集有重複無序正整數的正尺寸袋和S
我知道非常類似的問題已經被問這裏,我已經產生了一些工作代碼(見下文),但仍想的意見,如何使算法更快給出K和S的條件。
這是我到目前爲止已經瞭解到: 動態編程是最有效的方式來找到一個(不是全部)子集。如果我錯了,請糾正我。是否有一種方法可以重複調用DP代碼來生成較新的子集,直到包(用重複項設置)耗盡?
如果沒有,那麼有沒有一種方法可以加快我下面的回溯遞歸算法,它會產生我需要的但運行在O(2^n),我認爲考慮s和k?
這是我的永遠不會在n = 114和數量範圍爲3〜286改變數字的固定袋:
int[] numbers = new int[]
{
7, 286, 200, 176, 120, 165, 206, 75, 129, 109,
123, 111, 43, 52, 99, 128, 111, 110, 98, 135,
112, 78, 118, 64, 77, 227, 93, 88, 69, 60,
34, 30, 73, 54, 45, 83, 182, 88, 75, 85,
54, 53, 89, 59, 37, 35, 38, 29, 18, 45,
60, 49, 62, 55, 78, 96, 29, 22, 24, 13,
14, 11, 11, 18, 12, 12, 30, 52, 52, 44,
28, 28, 20, 56, 40, 31, 50, 40, 46, 42,
29, 19, 36, 25, 22, 17, 19, 26, 30, 20,
15, 21, 11, 8, 8, 19, 5, 8, 8, 11,
11, 8, 3, 9, 5, 4, 7, 3, 6, 3,
5, 4, 5, 6
};
要求
空間限制爲2-3GB最大但時間應該是O(N ^東西)不是 (東西^ n)。
袋不得進行排序和複製不得拆除。
結果應該是匹配 子集中的數字的索引,而不是數字本身(因爲我們有重複)。
動態規劃嘗試
這是改編自一個答案C#動態規劃版本,以一個類似的問題在這裏stackoverflow.com:
using System;
using System.Collections.Generic;
namespace Utilities
{
public static class Combinations
{
private static Dictionary<int, bool> m_memo = new Dictionary<int, bool>();
private static Dictionary<int, KeyValuePair<int, int>> m_previous = new Dictionary<int, KeyValuePair<int, int>>();
static Combinations()
{
m_memo.Clear();
m_previous.Clear();
m_memo[0] = true;
m_previous[0] = new KeyValuePair<int, int>(-1, 0);
}
public static bool FindSubset(IList<int> set, int sum)
{
//m_memo.Clear();
//m_previous.Clear();
//m_memo[0] = true;
//m_previous[0] = new KeyValuePair<int, int>(-1, 0);
for (int i = 0; i < set.Count; ++i)
{
int num = set[i];
for (int s = sum; s >= num; --s)
{
if (m_memo.ContainsKey(s - num) && m_memo[s - num] == true)
{
m_memo[s] = true;
if (!m_previous.ContainsKey(s))
{
m_previous[s] = new KeyValuePair<int, int>(i, num);
}
}
}
}
return m_memo.ContainsKey(sum) && m_memo[sum];
}
public static IEnumerable<int> GetLastIndex(int sum)
{
while (m_previous[sum].Key != -1)
{
yield return m_previous[sum].Key;
sum -= m_previous[sum].Value;
}
}
public static void SubsetSumMain(string[] args)
{
int[] numbers = new int[]
{
7, 286, 200, 176, 120, 165, 206, 75, 129, 109,
123, 111, 43, 52, 99, 128, 111, 110, 98, 135,
112, 78, 118, 64, 77, 227, 93, 88, 69, 60,
34, 30, 73, 54, 45, 83, 182, 88, 75, 85,
54, 53, 89, 59, 37, 35, 38, 29, 18, 45,
60, 49, 62, 55, 78, 96, 29, 22, 24, 13,
14, 11, 11, 18, 12, 12, 30, 52, 52, 44,
28, 28, 20, 56, 40, 31, 50, 40, 46, 42,
29, 19, 36, 25, 22, 17, 19, 26, 30, 20,
15, 21, 11, 8, 8, 19, 5, 8, 8, 11,
11, 8, 3, 9, 5, 4, 7, 3, 6, 3,
5, 4, 5, 6
};
int sum = 400;
//int size = 4; // don't know to use in dynamic programming
// call dynamic programming
if (Numbers.FindSubset(numbers, sum))
{
foreach (int index in Numbers.GetLastIndex(sum))
{
Console.Write((index + 1) + "." + numbers[index] + "\t");
}
Console.WriteLine();
}
Console.WriteLine();
Console.ReadKey();
}
}
}
遞歸程序嘗試
這裏是C#遞歸編程ve rsion改編自回答類似的問題在這裏stackoverflow.com:
using System;
using System.Collections.Generic;
namespace Utilities
{
public static class Combinations
{
private static int s_count = 0;
public static int CountSubsets(int[] numbers, int index, int current, int sum, int size, List<int> result)
{
if ((numbers.Length <= index) || (current > sum)) return 0;
if (result == null) result = new List<int>();
List<int> temp = new List<int>(result);
if (current + numbers[index] == sum)
{
temp.Add(index);
if ((size == 0) || (temp.Count == size))
{
s_count++;
}
}
else if (current + numbers[index] < sum)
{
temp.Add(index);
CountSubsets(numbers, index + 1, current + numbers[index], sum, size, temp);
}
CountSubsets(numbers, index + 1, current, sum, size, result);
return s_count;
}
private static List<List<int>> m_subsets = new List<List<int>>();
public static List<List<int>> FindSubsets(int[] numbers, int index, int current, int sum, int size, List<int> result)
{
if ((numbers.Length <= index) || (current > sum)) return m_subsets;
if (result == null) result = new List<int>();
List<int> temp = new List<int>(result);
if (current + numbers[index] == sum)
{
temp.Add(index);
if ((size == 0) || (temp.Count == size))
{
m_subsets.Add(temp);
}
}
else if (current + numbers[index] < sum)
{
temp.Add(index);
FindSubsets(numbers, index + 1, current + numbers[index], sum, size, temp);
}
FindSubsets(numbers, index + 1, current, sum, size, result);
return m_subsets;
}
public static void SubsetSumMain(string[] args)
{
int[] numbers = new int[]
{
7, 286, 200, 176, 120, 165, 206, 75, 129, 109,
123, 111, 43, 52, 99, 128, 111, 110, 98, 135,
112, 78, 118, 64, 77, 227, 93, 88, 69, 60,
34, 30, 73, 54, 45, 83, 182, 88, 75, 85,
54, 53, 89, 59, 37, 35, 38, 29, 18, 45,
60, 49, 62, 55, 78, 96, 29, 22, 24, 13,
14, 11, 11, 18, 12, 12, 30, 52, 52, 44,
28, 28, 20, 56, 40, 31, 50, 40, 46, 42,
29, 19, 36, 25, 22, 17, 19, 26, 30, 20,
15, 21, 11, 8, 8, 19, 5, 8, 8, 11,
11, 8, 3, 9, 5, 4, 7, 3, 6, 3,
5, 4, 5, 6
};
int sum = 17;
int size = 2;
// call backtracking recursive programming
Console.WriteLine("CountSubsets");
int count = Numbers.CountSubsets(numbers, 0, 0, sum, size, null);
Console.WriteLine("Count = " + count);
Console.WriteLine();
// call backtracking recursive programming
Console.WriteLine("FindSubsets");
List<List<int>> subsets = Numbers.FindSubsets(numbers, 0, 0, sum, size, null);
for (int i = 0; i < subsets.Count; i++)
{
if (subsets[i] != null)
{
Console.Write((i + 1).ToString() + ":\t");
for (int j = 0; j < subsets[i].Count; j++)
{
int index = subsets[i][j];
Console.Write((index + 1) + "." + numbers[index] + " ");
}
Console.WriteLine();
}
}
Console.WriteLine("Count = " + subsets.Count);
Console.ReadKey();
}
}
}
請讓我知道如何限制的動態規劃版本大小爲k的子集,如果我能反覆調用它,所以它返回不同的子集直到沒有更多的匹配子集。
而且我不知道從哪裏初始化DP算法的備忘錄。我在靜態構造函數中做了這個,它在訪問任何方法時自動運行。這是正確的初始化位置,還是需要將它移動到FindSunset()方法[註釋掉]內部?
對於遞歸版本,它是回溯?我們如何加快速度。它工作正常,並考慮到k和s,但完全沒有效率。
讓我們讓這個線程成爲所有C#SubsetSum相關問題的母親!
請不要只是在我們身上丟棄一層代碼,並要求我們爲您查看它。我們有[codereview.se]爲此目的。 –
如果且僅當代碼按預期工作時,問題纔可能成爲代碼複審的主題。這個短語:_「請讓我知道如何將動態編程版本限制爲大小爲k的子集」,這聽起來像所期望的行爲尚未寫入。 – Phrancis
有兩個版本: –