在多个表

时间:2017-04-05 09:14:17

标签: algorithm lookup

给定一个数字N和一组由唯一元素组成的表格,我怎样才能找到每个表格中只出现偶数次数的N个条目的所有组合?

注意:实际上,我有几百个表,每个表包含大约一百万个条目,每个条目的数字介于0到2百万之间。而我正试图找到大约500个数字的集合 这就是为什么我在寻找一种有效的算法。

例如,给定N=4和以下表格:

Table    #1         #2        #3       #4         #5
        ,---.     ,---.     ,---.     ,---.     ,----.
        | 0 |     | 5 |     | 7 |     | 4 |     |  9 |
        | 1 |     | 3 |     | 6 |     | 1 |     |  7 |
        | 9 |     | 2 |     | 4 |     | 0 |     | 11 |
        | 2 |     | 7 |     | 3 |     | 7 |     | 10 |
        | 8 |     | 6 |     | 9 |     | 5 |     |  2 |
        | 5 |     | 0 |     | 0 |     | 8 |     | 12 |
        '---'     '---'     '---'     '---'     '----'

要找到的解决方案是:

Solution      table #1       #2           #3     #4     #5
--------      ------------------------------------------------
0, 2, 4, 10 : [0, 2]       [0, 2]       [0, 4] [0, 4] [2, 10]
0, 1, 2, 9  : [0, 1, 2, 9] [0, 2]       [0, 9] [0, 1] [9, 2]
0, 2, 4, 11 : [0, 2]       [0, 2]       [0, 4] [0, 4] [2, 11]
0, 2, 4, 12 : [0, 2]       [0, 2]       [0, 4] [0, 4] [2, 12]
0, 2, 8, 9  : [0, 8, 2, 9] [0, 2]       [0, 9] [0, 8] [9, 2]
1, 3, 6, 8  : [8, 1]       [3, 6]       [3, 6] [8, 1] []
1, 4, 9, 10 : [1, 9]       []           [9, 4] [1, 4] [9, 10]
1, 4, 9, 11 : [1, 9]       []           [9, 4] [1, 4] [9, 11]
1, 4, 9, 12 : [1, 9]       []           [9, 4] [1, 4] [9, 12]
1, 8, 10, 11: [8, 1]       []           []     [8, 1] [10, 11]
1, 8, 10, 12: [8, 1]       []           []     [8, 1] [10, 12]
1, 8, 11, 12: [8, 1]       []           []     [8, 1] [11, 12]
2, 3, 5, 7  : [2, 5]       [2, 3, 5, 7] [3, 7] [5, 7] [2, 7]
2, 5, 6, 7  : [2, 5]       [2, 5, 6, 7] [6, 7] [5, 7] [2, 7]
3, 6, 10, 11: []           [3, 6]       [3, 6] []     [10, 11]
3, 6, 10, 12: []           [3, 6]       [3, 6] []     [10, 12]
3, 6, 11, 12: []           [3, 6]       [3, 6] []     [11, 12]
4, 8, 9, 10 : [8, 9]       []           [9, 4] [8, 4] [9, 10]
4, 8, 9, 11 : [8, 9]       []           [9, 4] [8, 4] [9, 11]
4, 8, 9, 12 : [8, 9]       []           [9, 4] [8, 4] [9, 12]

我已经使用过这个python程序,但它是一个天真的实现,我不喜欢它的复杂性,它高于O(n^N)

X = (
  (0,1,9,2,8,5),
  (5,3,2,7,6,0),
  (7,6,4,3,9,0),
  (4,1,0,7,5,8),
  (9,7,11,10,2,12)
)
# find unique elements across all lists
ALL = tuple(set([l for i in range(len(X)) for l in X[i]]))
LEN = len(ALL)

def find_all():
  Solutions = []
  for i in range(0, LEN-3):
    for j in range(i+1, LEN-2):
      for k in range(j+1, LEN-1):
        for l in range(k+1, LEN):
          if count_even((ALL[i], ALL[j], ALL[k], ALL[l])):
            Solutions.append([ALL[i], ALL[j], ALL[k], ALL[l]])
  return Solutions

def count_even(values):
  """return True if some elements of `values` appear
     an even number of times in every list"""
  for List in X:
    Accumulator = 0
    for Value in values:
      for Elem in List:
        if Elem == Value:
          Accumulator += 1
    if Accumulator & 1 == 1:
      return False
  return True

for v in find_all():
  print v

我对优化感兴趣的函数是find_all(),因为它有太多的嵌套循环 也许使用高斯消除或树或任何其他众所周知的算法可以做些什么。

任何帮助都非常感激。

2 个答案:

答案 0 :(得分:0)

(抱歉无法发表评论)

作为一项小改进,您可以预先计算和存储(例如,在散列图中,或者在数组中,如果值是从0到已知I_MAX的整数),每个表中每个元素的出现次数(时间O( N * N))

然后:

  • 构建N个元素的所有集合O(n ^ N)

  • 每个表的
  • ,每个表:
        对于集合中的每个元素:         查找表中的出现次数,将其添加到表的总计中。如果得到奇怪的结果,则拒绝该集

需要时间O(n * N)+ O(n ^ N)+ O(n ^ N * T * N),其为(对于T = N / 2):O(N ^ 3 * n ^ N如果N与n相比被认为是常数,则为O(n ^ N)。

也许有一个完全不同的智能算法不能生成具有N个元素的所有集合,但无论如何可能会发生你的问题有O(n ^ N)个答案,所以你不能这样做在最坏的情况下,这要好得多。 (如果N与n相比被认为是常数)。

答案 1 :(得分:0)

如果你没有太多的桌子,这个很好用。如果你有超过31个,你必须将位向量的格式从int更改为更复杂的东西。

评论解释了它是如何运作的

N=4
TABLES = [
  [0,1,9,2,8,5],
  [5,3,2,7,6,0],
  [7,6,4,3,9,0],
  [4,1,0,7,5,8],
  [9,7,11,10,2,12]
]

#Make a bit mask for each item, with a bit for each table
#The table's bit is 1 if the number occurs an even number
#of times in the table
#A set of N items is then valid iff the masks for those items
#XOR to 0
MASKS={}
bit=1
for table in TABLES:
    for val in table:
        MASKS[val] = MASKS.get(val,0) ^ bit
    bit*=2

ITEMS=sorted(MASKS.keys())

#Make REACHABLE[n][val] = the highest position pos such that
#you can make XOR value val using n elements at positions >= pos
REACHABLE=[]
for n in range(0,N):
    REACHABLE.append({})
REACHABLE[0][0]=len(ITEMS)
for pos in range(len(ITEMS)-1,-1,-1):
    itemval=MASKS[ITEMS[pos]]
    for n in range(N-1,1,-1):
        for testkey in REACHABLE[n-1].keys():
            newval=itemval^testkey
            REACHABLE[n][newval]=REACHABLE[n].get(newval,pos)
    REACHABLE[1][itemval]=REACHABLE[1].get(itemval,pos)

#now print all the solutions using the REACHABLE array to ensure that we
#don't go down any non-viable paths
def printSolutions(prefix, needval, startpos, count):
    if count<1:
        if needval==0:
            print(prefix)
        return
    for pos in range(startpos,len(ITEMS)):
        testval=needval^MASKS[ITEMS[pos]]
        if REACHABLE[count-1].get(testval,-1) > pos:
            prefix.append(ITEMS[pos])
            printSolutions(prefix,testval,pos+1,count-1)
            prefix.pop()

printSolutions([],0,0,N)

使REACHABLE数组取O(N * n * 2 ^(num_tables))并产生输出需要O(n * number_of_solutions)。空间复杂度为O(N * 2 ^(num_tables))。

显然,如果表的数量很大,这种算法可能效率低下。如果你必须支持大量的表,那么你可以在REACHABLE中使用类似Bloom过滤器(但使用XOR)而不是原始位向量。它在加速输出传递方面不会那么好,但是REACHABLE将会更小更快生成。

以下是上述程序的输出:

[0, 1, 2, 9]
[0, 2, 4, 10]
[0, 2, 4, 11]
[0, 2, 4, 12]
[0, 2, 8, 9]
[1, 3, 6, 8]
[1, 4, 9, 10]
[1, 4, 9, 11]
[1, 4, 9, 12]
[1, 8, 10, 11]
[1, 8, 10, 12]
[1, 8, 11, 12]
[2, 3, 5, 7]
[2, 5, 6, 7]
[3, 6, 10, 11]
[3, 6, 10, 12]
[3, 6, 11, 12]
[4, 8, 9, 10]
[4, 8, 9, 11]
[4, 8, 9, 12]