如何限制我的powerset功能?

时间:2017-07-02 17:12:03

标签: c# optimization powerset

我从StackOverflow Post找到了一个powerset函数:

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

但我对整个powerset并不感兴趣。它使用太多内存,仅限于整数的maxvalue,并且耗时太长。想象一下,我有一个无限长度的字符串,我可以使用它,我无法存储它。我只希望10个值位于通过powerset的25%的位置。

例如,如果我向powerset函数发送一个包含22个值的数组:

var toTakePowerSetOf = {3,-3,39,-39,392,-392,3920,-3920, 9, -9, 92, -92, 920, -920, 9203, -9203, 2, -2, 20, -20, 203, -203}

我通常会得到一个4194304元素的powerset。但我只关心索引@ 1049089/4194304的值{3,-9,203}。如何编辑快速powerset函数以使用无限精度,转到特定索引,并且只存储~10个值。

索引计算为2 ^ n / 4,其中n是toTakePowerSetOf中元素的数量。对于我的例子,有22个元素:2 ^ 22 = 4194304。 4194304/4 = 1048576。我寻求的实际价值是1049089,距离513。在我的问题中,我说我想检查周围的10个值,但我想我应该说我想检查一下513左右的数字值(.0122%)。

我试图使用的这个算法可能会导致更快的因子分解,但我需要弄清楚因为这些因素总是出现在通过powerset的25%附近。它可能没有多大意义,但如果它有效,我会与你分享,我们可以解决P = NP lol

2 个答案:

答案 0 :(得分:2)

给定的FastPowerSet算法的问题是您需要计算新的前面的所有功率集。另一个Algo是Wikipedia page的权力集。

这是一个简短的实现:

    public static T[][] FastPowerSet2<T>(T[] seq)
    {
        var powerSet = new T[1 << seq.Length][];
        powerSet[0] = new T[0]; // starting only with empty set
        for (uint i = 1; i < powerSet.Length; i++)
        {
            powerSet[i] = PowerSetItem(i, seq, powerSet.Length);
        }
        return powerSet;
    }

    private static T[] PowerSetItemBig<T>(BigInteger neededSetIndex, T[] p)
    {
        byte[] neededSetBytes = neededSetIndex.ToByteArray();
        BitArray b = new BitArray(neededSetBytes);
        return p.Take(b.Length).Where((s, j) =>  b[j]).ToArray();
    }

    private static T[] PowerSetItem<T>(uint powersetIndex, T[] sequence, int powersetLength)
    {
        BitArray b = new BitArray(BitConverter.GetBytes(powersetIndex));
        if (b.Length < powersetLength)
        {
            var prefix = new BitArray(powersetLength - b.Length);
            b = Append(prefix, b);
        }

        return sequence.Where((s, j) => b[j]).ToArray();
    }
    public static BitArray Append(BitArray current, BitArray after)
    {
        var bools = new bool[current.Count + after.Count];
        current.CopyTo(bools, 0);
        after.CopyTo(bools, current.Count);
        return new BitArray(bools);
    }

使用这些功能,您还可以使用以下命令生成特定的powerset项目:

int[] P = { 3, -3, 39, -39, 392, -392, 3920, -3920, 9, -9, 92, -92, 920, -920, 9203, -9203, 2, -2, 20, -20, 203, -203 };
var p1049089 = PowerSetItem(1049089, P, (2^P.Length));
Console.WriteLine(string.Join(", ", p1049089));

BigInteger版本与

一起使用
var p1049089 = PowerSetItemBig(new BigInteger(1049089), P);

注意:我使用linq获取了一些快捷方式,并省略了对传入数组的一些绑定检查,但这适用于此特定用例

答案 1 :(得分:1)

我的解决方案可以获得25%左右的数据集。两者都在该索引之后和该索引之前设置。

我的解决方案不仅限于整数大小,也不使用位数组。

它将处理极长的序列。所需要的只是上下文大小(在你的情况下为513)并不是非常大。

以下是代码的解释:

所以,你想看到一些前向和后向“背景”围绕25%指数的权力集(我称之为“中心”)。

25%索引处的集合由单个项目组成。这是因为2 ^ n / 4 = 1&lt;&lt; (N-1-2)     0000000000100 .....

当我们从这个集合/数字前进时,我们基本上重复相同的前513个电源集,但包括中心项目:

0000000000100..... = +0 context = powerSet[0] U { seq[central] }
1000000000100..... = +1 context = powerSet[1] U { seq[central] }
0100000000100..... = +2 context = powerSet[2] U { seq[central] }
1100000000100..... = +3 context = powerSet[3] U { seq[central] }
0010000000100..... = +4 context = powerSet[4] U { seq[central] }

当我们倒退时,数字表现得有些相似,但这些位看起来相反:

1111111111000..... = -1 context = { seq[0], seq[1], ..., seq[central - 1] } \ powerSet[0]
0111111111000..... = -2 context = { seq[0], seq[1], ..., seq[central - 1] } \ powerSet[1]
1011111111000..... = -3 context = { seq[0], seq[1], ..., seq[central - 1] } \ powerSet[2]
0011111111000..... = -4 context = { seq[0], seq[1], ..., seq[central - 1] } \ powerSet[3]
1101111111000..... = -5 context = { seq[0], seq[1], ..., seq[central - 1] } \ powerSet[4]

通过将后向上下文与前向上下文相结合,我们得到了完整的[-514 .. +513]上下文

以下是解决问题的代码:

    public static T[][] PartialPowerSet<T>(T[] seq, int forwardContextSize) {
        int n = seq.Length;
        int centralItemIndex = n - 2; //=log2(2^n/4)
        int numberOfItemsForContext = (int)Math.Ceiling(Math.Log(forwardContextSize, 2));

        var smallContextSeq = seq.Take(numberOfItemsForContext).ToArray();

        var smallContextPowerSet = FastPowerSet(smallContextSeq);
        smallContextPowerSet = smallContextPowerSet.Take(forwardContextSize + 1).ToArray();

        var forwardContextTemplateItems = new[] { seq[centralItemIndex] }; //To get forward context we're adding (union) the small context items
        var forwardContextItems = smallContextPowerSet.Select(s => s.Concat(forwardContextTemplateItems).ToArray());

        var backwardContextTemplateItems = seq.Take(centralItemIndex - 1).ToList(); //To get backward context we're removing (minus) the small context items from the set of all items up to, but not including the central item
        var backwardContextItems = smallContextPowerSet.Reverse().Select(s => backwardContextTemplateItems.Except(s).ToArray()); //Getting sets for c-513, c-512, ...., c-1. That's why we reverse the order of the smallContextPowerSet.

        return backwardContextItems.Concat(forwardContextItems).ToArray();
    }

    static void Main(string[] args) {
        var toTakePowerSetOf = new[] { 3, -3, 39, -39, 392, -392, 3920, -3920, 9, -9, 92, -92, 920, -920, 9203, -9203, 2, -2, 20, -20, 203, -203 };

        var res = PartialPowerSet(toTakePowerSetOf, 513);

        Console.WriteLine(string.Join(",", res[513 + 1 + 513])); //Prints "3,-9,203"
    }

打印3,-9,203