我正在尝试创建一个程序,它是创建序列,字符串或数字的可能组合的基础。这是某种加密/解密程序。我正在使用Visual Studio 2013和C#。我想要做的是从序列中生成一个功率集,但我有点困惑,无法继续进行。这是代码。
public static void randomSeq(){
int temp = 0;
string seq = "1234";
StringBuilder sb = new StringBuilder();
char[] bits = seq.Select((char c) => c).ToArray();
Console.Write("Given Sequence: ");
Console.Write(seq);
Console.WriteLine();
Console.WriteLine("Generated possiblities");
foreach (char item in bits){
Console.WriteLine(item);
}
do{
if (temp <= 2){
for (int i = temp + 1; i < bits.Length; i++){
sb.Append(bits[temp]);
sb.Append(bits[i]);
Console.WriteLine(sb);
sb.Clear();
}
}else{
if (temp > 2){
for (int k = 0; k < temp; k++){
sb.Append(bits[k]);
}
for (int l = temp + 1; l < bits.Length; l++){
sb.Append(bits[temp]);
sb.Append(bits[l]);
Console.WriteLine(sb);
sb.Clear();
}
}
}
temp++;
}
while (temp != bits.Length);
}
我希望这段代码是通用的,即我传递任何序列,它会为我生成一个幂集。然后我想在我的程序中进一步重用它。我可以完成剩下的工作,我只是停留在发电机组中。有人能帮助我吗?。
答案 0 :(得分:27)
如果熟悉位,则很容易生成功率集。对于N
元素集,将有2^N
个子集将进入幂集(包括空集和初始集)。因此,每个元素将是IN或OUT(换句话说,1
或0
)。考虑到这一点,很容易将该集的子集表示为位掩码。通过列举所有可能的位掩码,可以构建整个功率集。为了做到这一点,我们需要检查位掩码中的每个位,并在该位置有1
时获取输入集的元素。下面是string
(字符集合)作为输入的示例。它可以很容易地重写,以便收集任何类型的值。
private static List<string> PowerSet(string input)
{
int n = input.Length;
// Power set contains 2^N subsets.
int powerSetCount = 1 << n;
var ans = new List<string>();
for (int setMask = 0; setMask < powerSetCount; setMask++)
{
var s = new StringBuilder();
for (int i = 0; i < n; i++)
{
// Checking whether i'th element of input collection should go to the current subset.
if ((setMask & (1 << i)) > 0)
{
s.Append(input[i]);
}
}
ans.Add(s.ToString());
}
return ans;
}
假设您有字符串"xyz"
作为输入,它包含3个元素,而不是幂集中的2^3 == 8
元素。如果您要从0
迭代到7
,您将获得下表。列:( 10基整数;位表示(2基);初始集的子集)。
0 000 ...
1 001 ..z
2 010 .y.
3 011 .yz
4 100 x..
5 101 x.z
6 110 xy.
7 111 xyz
您可以注意到第三列包含初始字符串"xyz"
受Eric的想法的启发,我已经实现了这种算法的另一种变体(现在没有位)。我也把它变成了通用的。我相信这段代码几乎可以为Power Set计算编写最快的代码。它的复杂性与位方法O(n * 2^n)
相同,但对于这种方法,常数减半。
public static T[][] FastPowerSet<T>(T[] seq)
{
var powerSet = new T[1 << seq.Length][];
powerSet[0] = new T[0]; // starting only with empty set
for (int i = 0; i < seq.Length; i++)
{
var cur = seq[i];
int count = 1 << i; // doubling list each time
for (int j = 0; j < count; j++)
{
var source = powerSet[j];
var destination = powerSet[count + j] = new T[source.Length + 1];
for (int q = 0; q < source.Length; q++)
destination[q] = source[q];
destination[source.Length] = cur;
}
}
return powerSet;
}
答案 1 :(得分:4)
出于本答案的目的,我将假设“集合”是有限序列。
我们递归地定义函数P如下。
P(empty) --> { empty }
P(H : T) -->
P(T)
的联合以及P(T)
的每个元素都加上H
。让我们尝试一下。 {Apple, Banana, Cherry}
的权力集是什么?
它不是空集,因此{Apple, Banana, Cherry}
的幂集是{Banana, Cherry}
的幂集,加上由Apple
前置{Banana, Cherry}
组成的集合。
所以我们需要知道{Cherry}
的功率集。它是Banana
的幂集加上集合形式,前缀为{Cherry}
。
所以我们需要知道Cherry
的功率集。它是空集的幂集,加上由{ {} }
前置的集合。
所以我们需要知道空集的功率集。它是包含空集的集合。 Cherry
现在在每个元素前加{ {Cherry}, {} }
并取结合。那是{ Cherry }
。这给了我们{Banana, Cherry}
的权力集。请记住,我们需要找到Banana
的幂集,因此我们将其与每个{ {Banana, Cherry}, {Banana}, {Cherry}, {}}
联合起来并获得{Banana, Cherry}
,这就是{Apple, Banana, Cherry}
的幂集。
现在我们需要获得Apple
的权力集,因此将其与{ {Apple, Banana, Cherry}, {Apple, Banana}, {Apple, Cherry}, {Apple}, {Banana, Cherry}, {Banana}, {Cherry}, {}}
联合,并且每个人都有static IEnumerable<T> Prepend<T>(this IEnumerable<T> tail, T head)
{
yield return head;
foreach(T item in tail) yield return item;
}
,我们已经完成了static IEnumerable<IEnumerable<T>> PowerSet<T>(this IEnumerable<T> items)
{
if (!items.Any())
yield return items; // { { } }
else
{
var head = items.First();
var powerset = items.Skip(1).PowerSet().ToList();
foreach(var set in powerset) yield return set.Prepend(head);
foreach(var set in powerset) yield return set;
}
}
。
代码应该很容易编写。首先,我们需要一个辅助方法:
class ImmutableList<T>
{
public static readonly ImmutableList<T> Empty = new ImmutableList<T>(null, default(T));
private ImmutableList(ImmutableList<T> tail, T head)
{
this.Head = head;
this.Tail = tail;
}
public T Head { get; private set; }
public ImmutableList<T> Tail { get; private set; }
public ImmutableList<T> Push(T head)
{
return new ImmutableList<T>(this, head);
}
public IEnumerable<ImmutableList<T>> PowerSet()
{
if (this == Empty)
yield return this;
else
{
var powerset = Tail.PowerSet();
foreach (var set in powerset) yield return set.Push(Head);
foreach (var set in powerset) yield return set;
}
}
}
现在代码是对算法描述的直接翻译:
{{1}}
有意义吗?
-----------更新----------------
谢尔盖正确地指出我的代码有Schlemiel the Painter算法,因此消耗了大量的时间和内存;好抓住谢尔盖。这是一个使用不可变堆栈的高效版本:
{{1}}
答案 2 :(得分:1)
使用Linq提及相同的算法SergeyS(其中inputSet是输入,outputPowerSet是输出):
int setLength = inputSet.Count;
int powerSetLength = 1 << setLength;
for (int bitMask = 0; bitMask < powerSetLength; bitMask++)
{
var subSet = from x in inputSet
where ((1 << inputSet.IndexOf(x)) & bitMask) != 0
select x;
outputPowerSet.Add(subSet.ToList());
}
答案 3 :(得分:0)
游戏很晚,但为什么不采用下面的方法呢?它似乎比这里发布的建议简单得多:
/*
Description for a sample set {1, 2, 2, 3}:
Step 1 - Start with {}:
{}
Step 2 - "Expand" previous set by adding 1:
{}
---
{1}
Step 3 - Expand previous set by adding the first 2:
{}
{1}
---
{2}
{1,2}
Step 4 - Expand previous set by adding the second 2:
{}
{1}
{2}
{1,2}
---
{2}
{1,2}
{2,2}
{1,2,2}
Step 5 - Expand previous set by adding 3:
{}
{1}
{2}
{1,2}
{2}
{1,2}
{2,2}
{1,2,2}
---
{3}
{1,3}
{2,3}
{1,2,3}
{2,3}
{1,2,3}
{2,2,3}
{1,2,2,3}
Total elements = 16 (i.e. 2^4), as expected.
*/
private static void PowerSet(IList<int> nums, ref IList<IList<int>> output)
{
// ToDo: validate args
output.Add(new List<int>());
ExpandSet(nums, 0, ref output);
}
private static void ExpandSet(IList<int> nums, int pos, ref IList<IList<int>> output)
{
if (pos == nums.Count)
{
return;
}
List<int> tmp;
int item = nums[pos];
for (int i = 0, n = output.Count; i < n; i++)
{
tmp = new List<int>();
tmp.AddRange(output[i]);
tmp.Add(item);
output.Add(tmp);
}
ExpandSet(nums, pos + 1, ref output);
}