如何限制我的 powerset 功能?

How can I limit my powerset function?

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

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

但我对整个动力装置不感兴趣。它使用太多内存,仅限于整数的最大值,并且花费的时间太长。想象一下,我有一个无限长的字符串,我正在使用它的幂集,但我无法存储它。我只想要 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 个元素的幂集。但我只关心 index @ 1049089/4194304 处的值,即 {3, -9, 203} 。如何编辑 fast powerset 函数以使用无限精度、转到特定索引并仅存储 ~10 个值。

索引计算为 2^n/4,其中 n 是 toTakePowerSetOf 中的元素数。对于我的示例,有 22 个元素: 2^22=4194304 。 4194304/4 = 1048576。我正在寻找的实际值是 1049089,相距 513。在我的问题中,我说我想检查周围的 10 个值,但我想我真的应该说我想检查周围数字的大约 513 个值 (.0122 %)。

我尝试使用的这个算法可能会导致更快的因式分解,但我需要查明是否因式总是出现在幂集的 25% 附近。它可能没有多大意义,但如果有效,我会与您分享,我们可以解决 P=NP lol

给定的 FastPowerSet 算法的问题是您需要为新算法计算所有前面的幂集。 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 采取了一些快捷方式,并省略了对传入数组的一些绑定检查,但这适用于这个特定的用例

我的解决方案为您提供了 25% 指数附近的集合。该索引之后和该索引之前的两个集合。

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

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

代码解释如下:

因此,您希望在功率设置为 25% 指数(我称之为 "central")附近看到一些向前和向后 "context"。

索引为 25% 的集合由单个项目组成。那是因为 2^n/4 = 1 << (n-1-2) 0000000000100.....

当我们从这个 set/number 继续前进时,我们基本上是在重复相同的前 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