找到全零答案的快速方法

时间:2015-05-15 14:30:27

标签: python algorithm math numpy optimization

对于每个长度为n + h-1的数组,其值为0和1,我想检查是否存在另一个长度为n的非零数组,其值为-1,0,1,以便所有h内在产品为零。我天真的做法是

import numpy as np
import itertools
(n,h)= 4,3
for longtuple in itertools.product([0,1], repeat = n+h-1):
    bad = 0
    for v in itertools.product([-1,0,1], repeat = n):
        if not any(v):
            continue
        if (not np.correlate(v, longtuple, 'valid').any()):
            bad = 1
            break
    if (bad == 0):
        print "Good"
        print longtuple

如果我们设置n = 19h = 10这是我想要测试的内容,这非常慢。

  

我的目标是找到一个好的"好"长度为n+h-1的数组。有没有   如何加快速度,以便n = 19h = 10可行?

当前的朴素方法需要2 ^(n + h-1)3 ^(n)次迭代,每次迭代大约需要n次。那是n = 19h = 10的311,992,186,885,373,952次迭代,这是不可能的。

注意1 convolve更改为correlate,以便代码以正确的方式考虑v

2015年7月10日

问题依然存在,但是对于n=19h=10来说还没有足够快的解决方案。

5 个答案:

答案 0 :(得分:7)

考虑以下情况"在中间见面"做法。

首先,重新描述leekaiinthesky提供的矩阵公式中的情况。

接下来,请注意我们只需考虑"短"如果我们将问题更改为找到s {{3},则{0,1}^n形式的向量h x n(即,仅包含0' s和1' s的短向量) } H 0和1,Hs1永远不等于Hs2的两个不同的短矢量0' s和1&#39}秒。这是因为Hs1 = Hs2暗示H(s1-s2)=0意味着存在1' s,0' s的矢量v,即{{1} },s1-s2;相反,如果Hv = 0中的Hv = 0v,那么我们可以在{-1,0,1}^n中找到s1s2{0,1}^n v = s1 - s2

Hs1 = Hs2 n=19中只有524,288个向量s可供尝试时;对结果{0,1}^n进行哈希处理,如果相同的结果出现两次,那么Hs就没有问题,请尝试另一个H。就记忆而言,这种方法非常可行。有H Hankel矩阵2^(n+h-1)可以尝试;当Hn=19表示268,435,456的矩阵时。那些h=10测试,或274,877,906,944,每个测试都有大约2^38次操作来乘以矩阵nh和向量H,大约52万亿次操作。那似乎可行,不是吗?

由于你现在只处理0和1,而不是-1,你也可以通过使用位操作来加速这个过程(shift,并且,计数1' s。

<强>更新

我用C ++实现了我的想法。我使用位操作来计算点积,将结果向量编码为长整数,并使用unordered_set检测重复项,当找到点乘积的重复向量时,从给定的长向量中提前退出。

我获得了00000000010010111000100100,其中n = 17,几分钟后h = 10,000000111011110001001101011,n = 18,h = 10,稍长一些。我正准备在n = 19和h = 10的情况下运行它。

s

第二次更新

n = 19和h = 10的程序在笔记本电脑的后台运行了两个星期。最后,它只是退出而不打印任何结果。除非程序中出现某种错误,否则看起来没有长矢量具有您想要的属性。我建议寻找没有这么长的载体的理论原因。也许某种计数论证会起作用。

答案 1 :(得分:3)

可能有更快的方法*

您所寻找的内容与kernel or null space of a matrix的概念有关。

特别地,对于每个n + h-1“longtuple”并且给定n,构造h×n矩阵,其行是长元组的n元组。换句话说,如果您的longtuple是[0,0,0,1,0,0]且n = 3,那么您的矩阵是:

[[0 0 0]
 [0 0 1]
 [0 1 0]
 [1 0 0]]

将此矩阵称为 A 。你正在寻找一个 x 向量,使 Ax = 0 ,其中 0 是所有0的向量。如果存在这样的 x (不是本身全部为0)并且可以缩放为仅包含{-1,0,1},那么你想要抛弃< strong> A 并转到下一个longtuple。

我不太确定计算内核的(理论上最有效的)计算复杂度是什么,但它似乎是O(h + n)^ 3左右的顺序,无论如何是好于O(3 ^ n)。有关如何计算内核的一些示例,请参阅上面的Wikipedia链接或Python (NumPy, SciPy), finding the null space of a matrix

无论如何,一旦你确定内核,你将不得不做一些额外的工作来弄清楚{-1,0,1} ^ n形式的任何向量是否存在于那里,但我不认为那是这是一个很大的计算负担。

* NB :在评论中,@ vib指出这实际上可能是一个很大的计算负担。我不确定最好的算法是什么来确定这些向量是否与内核相交。也许它无法在多项式时间内解决,在这种情况下,这个答案并没有为原始问题提供加速!

示例代码

根据您在评论中提供的示例调整上面链接的其他Stack Overflow问题的代码:

import scipy
from scipy import linalg, matrix
def null(A, eps=1e-15):
    u, s, vh = scipy.linalg.svd(A)
    null_mask = (s <= eps)
    null_space = scipy.compress(null_mask, vh, axis=0)
    return scipy.transpose(null_space)

A = matrix([[0,0,0,1],[0,0,1,0],[0,1,0,0],[0,0,0,0]])
print null(A)

#> [[-1.]
#>  [ 0.]
#>  [ 0.]
#>  [ 0.]]

代码给出了一个n元组的例子(事实上,你给出了同样的例子),它使[0, 0, 0, 1, 0, 0]无效为“好”的longtuple。如果代码返回[],那么可能没有这样的n元组,​​并且longtuple是“好的”。 (如果代码确实返回了一些内容,你仍然需要检查{-1,0,1}部分。)

进一步的想法

是否存在这样的 x ,忽略现在的{-1,0,1}约束,等同于 nullity <的问题/ strong>(内核的维度) A 大于0.这相当于询问 等级 A 等于n。所以如果你找到一些方法来巧妙地对{-1,0,1}约束进行分解并将其分解为需要计算 A 的等级,我相信这可以做得更快

顺便说一下,你(或任何给你这个问题的人)很可能已经知道了这一切......否则你为什么要调用longtuple“n + h-1”的长度,如果你尚未开始使用高度矩阵......!

答案 2 :(得分:2)

这只是一个部分答案,因为检查案例n=19, h=10似乎仍然太慢(在这种情况下,没有&#34;好的&#34;向量甚至可能存在)。

以下是@vib描述的检查算法的实现,并在Mathematica中对所有2^(n+h-1)向量使用随机采样。

TestFullNullSpace[A_, ns_] := Module[{dim, n},
  {dim, n} = Dimensions[ns];
  For[i = 1, i < 3^dim, i++,
   testvec = Mod[IntegerDigits[i, 3, dim].ns, 3] /. {2 -> -1};
   If[Norm[A.testvec] == 0,
    Return[False]];
   ];
  Return[True];
]

n = 17;
h = 10;

Do[
 v = Table[RandomChoice[{0, 1}], {n + h - 1}];
 A = Table[v[[i ;; i + n - 1]], {i, 1, h}];
 ns = NullSpace[A, Modulus -> 3] /. {2 -> -1};
 If[TestFullNullSpace[A, ns],
  Print[v]];,
 {1000}]

计算几秒后,上述运行的样本输出:

{0,0,1,1,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,1,0,0,0,1,1,0}
{1,1,0,1,0,0,0,1,1,0,1,1,1,1,1,0,1,0,1,0,0,1,1,0,0,0}
{1,0,1,1,1,1,1,0,0,0,1,1,0,1,0,1,1,0,0,1,1,0,1,1,1,0}
{0,0,0,0,1,0,1,1,1,0,1,1,0,0,1,1,1,1,0,1,0,1,0,0,1,1}

因此,从1000个检查过的载体中,4个是好的&#34; (除非我有一个错误)。不幸的是,对于n=18,我跑了几分钟,仍然没有找到一个好的&#34;向量。我不知道他们是不存在还是非常罕见。

答案 3 :(得分:1)

以下是一种将复杂度从3 ^ n降低到3 ^ {n-h}的算法。

设v_1,v_2,..,v_h是您需要与之正交的向量。

考虑矢量空间(Z / 3Z)^ n。设v'_1,..,v'_h是这个空间中v_1,..,v_h的自然包含。

现在让w为系数在{-1,0,1}的向量,让w'为(Z / 3Z)^ n的向量,通过自然地将w视为(Z / 3Z)的向量得到^ N。那么w与v_1,..,v_h(在R中)具有零标量积的必要条件是w'具有零标量乘积(在(Z / 3Z)^ n中)与v'_1,..,v “_H。

现在你可以很容易地确定具有v',...,v'_h的零标量积的w'。它们将形成一个大小为3 ^ {n-h}的空间。然后,您需要检查每个关联的w是否实际上与所有v_i正交。

答案 4 :(得分:1)

这是一种将其缩减为O(n*h*3^(n/2 + 1))的方法。这很难扩展,但对你的用例来说已经足够了。

迭代向量的前半部分的每种可能性。创建一个数组字典的词典字典,其键是每个移位的内积的值,其最终值是导致该序列的向量的前半部分数组。

现在遍历向量的后半部分的每种可能性。在计算每个内部产品时,遍历嵌套字典并查看是否存在对内部产品的贡献仍然取消的相应的前半部分。如果你一路走到最后,那么你可以把你找到的后半部分找到的前半部分放在一起,你也找到了答案。

不要忘记忽略全是0的答案!